<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ca">
	<id>http://wiki.joanillo.org/index.php?action=history&amp;feed=atom&amp;title=MIDI_classes_and_parser_in_Python%3A_midi.py</id>
	<title>MIDI classes and parser in Python: midi.py - Historial de revisió</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.joanillo.org/index.php?action=history&amp;feed=atom&amp;title=MIDI_classes_and_parser_in_Python%3A_midi.py"/>
	<link rel="alternate" type="text/html" href="http://wiki.joanillo.org/index.php?title=MIDI_classes_and_parser_in_Python:_midi.py&amp;action=history"/>
	<updated>2026-09-23T00:01:16Z</updated>
	<subtitle>Historial de revisió per a aquesta pàgina del wiki</subtitle>
	<generator>MediaWiki 1.34.2</generator>
	<entry>
		<id>http://wiki.joanillo.org/index.php?title=MIDI_classes_and_parser_in_Python:_midi.py&amp;diff=248513&amp;oldid=prev</id>
		<title>Joan: /* fitxer midi.py */</title>
		<link rel="alternate" type="text/html" href="http://wiki.joanillo.org/index.php?title=MIDI_classes_and_parser_in_Python:_midi.py&amp;diff=248513&amp;oldid=prev"/>
		<updated>2012-01-17T09:11:45Z</updated>

		<summary type="html">&lt;p&gt;&lt;span dir=&quot;auto&quot;&gt;&lt;span class=&quot;autocomment&quot;&gt;fitxer midi.py&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Pàgina nova&lt;/b&gt;&lt;/p&gt;&lt;div&gt;__TOC__&lt;br /&gt;
=fitxer midi.py=&lt;br /&gt;
Els fitxer midi són binaris. Es tracta de poder processar/parsejar un fitxer midi en línia de comandes per tal d'obtenir la informació: quins canals utilitza, quins són els programes de cada canal, etc.&lt;br /&gt;
&lt;br /&gt;
*http://groups.google.com/group/alt.sources/msg/22467419ad4bf416?pli=1&lt;br /&gt;
&lt;br /&gt;
Fitxer midi.py i midis d'exemple: [[Fitxer:Midipy.zip]]&lt;br /&gt;
&lt;br /&gt;
Fitxer '''midi.py''':&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import sys, string, types, exceptions&lt;br /&gt;
&lt;br /&gt;
debugflag = 0&lt;br /&gt;
&lt;br /&gt;
def showstr(str, n=16):&lt;br /&gt;
    for x in str[:n]:&lt;br /&gt;
        print ('%02x' % ord(x)),&lt;br /&gt;
    print&lt;br /&gt;
&lt;br /&gt;
def getNumber(str, length):&lt;br /&gt;
    # MIDI uses big-endian for everything&lt;br /&gt;
    sum = 0&lt;br /&gt;
    for i in range(length):&lt;br /&gt;
        sum = (sum &amp;lt;&amp;lt; 8) + ord(str[i])&lt;br /&gt;
    return sum, str[length:]&lt;br /&gt;
&lt;br /&gt;
def getVariableLengthNumber(str):&lt;br /&gt;
    sum = 0&lt;br /&gt;
    i = 0&lt;br /&gt;
    while 1:&lt;br /&gt;
        x = ord(str[i])&lt;br /&gt;
        i = i + 1&lt;br /&gt;
        sum = (sum &amp;lt;&amp;lt; 7) + (x &amp;amp; 0x7F)&lt;br /&gt;
        if not (x &amp;amp; 0x80):&lt;br /&gt;
            return sum, str[i:]&lt;br /&gt;
&lt;br /&gt;
def putNumber(num, length):&lt;br /&gt;
    # MIDI uses big-endian for everything&lt;br /&gt;
    lst = [ ]&lt;br /&gt;
    for i in range(length):&lt;br /&gt;
        n = 8 * (length - 1 - i)&lt;br /&gt;
        lst.append(chr((num &amp;gt;&amp;gt; n) &amp;amp; 0xFF))&lt;br /&gt;
    return string.join(lst, &amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
def putVariableLengthNumber(x):&lt;br /&gt;
    lst = [ ]&lt;br /&gt;
    while 1:&lt;br /&gt;
        y, x = x &amp;amp; 0x7F, x &amp;gt;&amp;gt; 7&lt;br /&gt;
        lst.append(chr(y + 0x80))&lt;br /&gt;
        if x == 0:&lt;br /&gt;
            break&lt;br /&gt;
    lst.reverse()&lt;br /&gt;
    lst[-1] = chr(ord(lst[-1]) &amp;amp; 0x7f)&lt;br /&gt;
    return string.join(lst, &amp;quot;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
class EnumException(exceptions.Exception):&lt;br /&gt;
    pass&lt;br /&gt;
&lt;br /&gt;
class Enumeration:&lt;br /&gt;
    def __init__(self, enumList):&lt;br /&gt;
        lookup = { }&lt;br /&gt;
        reverseLookup = { }&lt;br /&gt;
        i = 0&lt;br /&gt;
        uniqueNames = [ ]&lt;br /&gt;
        uniqueValues = [ ]&lt;br /&gt;
        for x in enumList:&lt;br /&gt;
            if type(x) == types.TupleType:&lt;br /&gt;
                x, i = x&lt;br /&gt;
            if type(x) != types.StringType:&lt;br /&gt;
                raise EnumException, &amp;quot;enum name is not a string: &amp;quot; + x&lt;br /&gt;
            if type(i) != types.IntType:&lt;br /&gt;
                raise EnumException, &amp;quot;enum value is not an integer: &amp;quot; + i&lt;br /&gt;
            if x in uniqueNames:&lt;br /&gt;
                raise EnumException, &amp;quot;enum name is not unique: &amp;quot; + x&lt;br /&gt;
            if i in uniqueValues:&lt;br /&gt;
                raise EnumException, &amp;quot;enum value is not unique for &amp;quot; + x&lt;br /&gt;
            uniqueNames.append(x)&lt;br /&gt;
            uniqueValues.append(i)&lt;br /&gt;
            lookup[x] = i&lt;br /&gt;
            reverseLookup[i] = x&lt;br /&gt;
            i = i + 1&lt;br /&gt;
        self.lookup = lookup&lt;br /&gt;
        self.reverseLookup = reverseLookup&lt;br /&gt;
    def __add__(self, other):&lt;br /&gt;
        lst = [ ]&lt;br /&gt;
        for k in self.lookup.keys():&lt;br /&gt;
            lst.append((k, self.lookup[k]))&lt;br /&gt;
        for k in other.lookup.keys():&lt;br /&gt;
            lst.append((k, other.lookup[k]))&lt;br /&gt;
        return Enumeration(lst)&lt;br /&gt;
    def hasattr(self, attr):&lt;br /&gt;
        return self.lookup.has_key(attr)&lt;br /&gt;
    def has_value(self, attr):&lt;br /&gt;
        return self.reverseLookup.has_key(attr)&lt;br /&gt;
    def __getattr__(self, attr):&lt;br /&gt;
        if not self.lookup.has_key(attr):&lt;br /&gt;
            raise AttributeError&lt;br /&gt;
        return self.lookup[attr]&lt;br /&gt;
    def whatis(self, value):&lt;br /&gt;
        return self.reverseLookup[value]&lt;br /&gt;
&lt;br /&gt;
channelVoiceMessages = Enumeration([(&amp;quot;NOTE_OFF&amp;quot;, 0x80),&lt;br /&gt;
                                    (&amp;quot;NOTE_ON&amp;quot;, 0x90),&lt;br /&gt;
                                    (&amp;quot;POLYPHONIC_KEY_PRESSURE&amp;quot;, 0xA0),&lt;br /&gt;
                                    (&amp;quot;CONTROLLER_CHANGE&amp;quot;, 0xB0),&lt;br /&gt;
                                    (&amp;quot;PROGRAM_CHANGE&amp;quot;, 0xC0),&lt;br /&gt;
                                    (&amp;quot;CHANNEL_KEY_PRESSURE&amp;quot;, 0xD0),&lt;br /&gt;
                                    (&amp;quot;PITCH_BEND&amp;quot;, 0xE0)])&lt;br /&gt;
&lt;br /&gt;
channelModeMessages = Enumeration([(&amp;quot;ALL_SOUND_OFF&amp;quot;, 0x78),&lt;br /&gt;
                                   (&amp;quot;RESET_ALL_CONTROLLERS&amp;quot;, 0x79),&lt;br /&gt;
                                   (&amp;quot;LOCAL_CONTROL&amp;quot;, 0x7A),&lt;br /&gt;
                                   (&amp;quot;ALL_NOTES_OFF&amp;quot;, 0x7B),&lt;br /&gt;
                                   (&amp;quot;OMNI_MODE_OFF&amp;quot;, 0x7C),&lt;br /&gt;
                                   (&amp;quot;OMNI_MODE_ON&amp;quot;, 0x7D),&lt;br /&gt;
                                   (&amp;quot;MONO_MODE_ON&amp;quot;, 0x7E),&lt;br /&gt;
                                   (&amp;quot;POLY_MODE_ON&amp;quot;, 0x7F)])&lt;br /&gt;
&lt;br /&gt;
metaEvents = Enumeration([(&amp;quot;SEQUENCE_NUMBER&amp;quot;, 0x00),&lt;br /&gt;
                          (&amp;quot;TEXT_EVENT&amp;quot;, 0x01),&lt;br /&gt;
                          (&amp;quot;COPYRIGHT_NOTICE&amp;quot;, 0x02),&lt;br /&gt;
                          (&amp;quot;SEQUENCE_TRACK_NAME&amp;quot;, 0x03),&lt;br /&gt;
                          (&amp;quot;INSTRUMENT_NAME&amp;quot;, 0x04),&lt;br /&gt;
                          (&amp;quot;LYRIC&amp;quot;, 0x05),&lt;br /&gt;
                          (&amp;quot;MARKER&amp;quot;, 0x06),&lt;br /&gt;
                          (&amp;quot;CUE_POINT&amp;quot;, 0x07),&lt;br /&gt;
                          (&amp;quot;MIDI_CHANNEL_PREFIX&amp;quot;, 0x20),&lt;br /&gt;
                          (&amp;quot;MIDI_PORT&amp;quot;, 0x21),&lt;br /&gt;
                          (&amp;quot;END_OF_TRACK&amp;quot;, 0x2F),&lt;br /&gt;
                          (&amp;quot;SET_TEMPO&amp;quot;, 0x51),&lt;br /&gt;
                          (&amp;quot;SMTPE_OFFSET&amp;quot;, 0x54),&lt;br /&gt;
                          (&amp;quot;TIME_SIGNATURE&amp;quot;, 0x58),&lt;br /&gt;
                          (&amp;quot;KEY_SIGNATURE&amp;quot;, 0x59),&lt;br /&gt;
                          (&amp;quot;SEQUENCER_SPECIFIC_META_EVENT&amp;quot;, 0x7F)])&lt;br /&gt;
&lt;br /&gt;
# runningStatus appears to want to be an attribute of a MidiTrack. But&lt;br /&gt;
# it doesn't seem to do any harm to implement it as a global.&lt;br /&gt;
runningStatus = None&lt;br /&gt;
&lt;br /&gt;
class MidiEvent:&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, track):&lt;br /&gt;
        self.track = track&lt;br /&gt;
        self.time = None&lt;br /&gt;
        self.channel = self.pitch = self.velocity = self.data = None&lt;br /&gt;
&lt;br /&gt;
    def __cmp__(self, other):&lt;br /&gt;
        # assert self.time != None and other.time != None&lt;br /&gt;
        return cmp(self.time, other.time)&lt;br /&gt;
&lt;br /&gt;
    def __repr__(self):&lt;br /&gt;
        r = (&amp;quot;&amp;lt;MidiEvent %s, t=%s, track=%s, channel=%s&amp;quot; %&lt;br /&gt;
             (self.type,&lt;br /&gt;
              repr(self.time),&lt;br /&gt;
              self.track.index,&lt;br /&gt;
              repr(self.channel)))&lt;br /&gt;
        for attrib in [&amp;quot;pitch&amp;quot;, &amp;quot;data&amp;quot;, &amp;quot;velocity&amp;quot;]:&lt;br /&gt;
            if getattr(self, attrib) != None:&lt;br /&gt;
                r = r + &amp;quot;, &amp;quot; + attrib + &amp;quot;=&amp;quot; + repr(getattr(self,&lt;br /&gt;
attrib))&lt;br /&gt;
        return r + &amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    def read(self, time, str):&lt;br /&gt;
        global runningStatus&lt;br /&gt;
        self.time = time&lt;br /&gt;
        # do we need to use running status?&lt;br /&gt;
        if not (ord(str[0]) &amp;amp; 0x80):&lt;br /&gt;
            str = runningStatus + str&lt;br /&gt;
        runningStatus = x = str[0]&lt;br /&gt;
        x = ord(x)&lt;br /&gt;
        y = x &amp;amp; 0xF0&lt;br /&gt;
        z = ord(str[1])&lt;br /&gt;
&lt;br /&gt;
        if channelVoiceMessages.has_value(y):&lt;br /&gt;
            self.channel = (x &amp;amp; 0x0F) + 1&lt;br /&gt;
            self.type = channelVoiceMessages.whatis(y)&lt;br /&gt;
            if (self.type == &amp;quot;PROGRAM_CHANGE&amp;quot; or&lt;br /&gt;
                self.type == &amp;quot;CHANNEL_KEY_PRESSURE&amp;quot;):&lt;br /&gt;
                self.data = z&lt;br /&gt;
                return str[2:]&lt;br /&gt;
            else:&lt;br /&gt;
                self.pitch = z&lt;br /&gt;
                self.velocity = ord(str[2])&lt;br /&gt;
                channel = self.track.channels[self.channel - 1]&lt;br /&gt;
                if (self.type == &amp;quot;NOTE_OFF&amp;quot; or&lt;br /&gt;
                    (self.velocity == 0 and self.type == &amp;quot;NOTE_ON&amp;quot;)):&lt;br /&gt;
                    channel.noteOff(self.pitch, self.time)&lt;br /&gt;
                elif self.type == &amp;quot;NOTE_ON&amp;quot;:&lt;br /&gt;
                    channel.noteOn(self.pitch, self.time, self.velocity)&lt;br /&gt;
                return str[3:]&lt;br /&gt;
&lt;br /&gt;
        elif y == 0xB0 and channelModeMessages.has_value(z):&lt;br /&gt;
            self.channel = (x &amp;amp; 0x0F) + 1&lt;br /&gt;
            self.type = channelModeMessages.whatis(z)&lt;br /&gt;
            if self.type == &amp;quot;LOCAL_CONTROL&amp;quot;:&lt;br /&gt;
                self.data = (ord(str[2]) == 0x7F)&lt;br /&gt;
            elif self.type == &amp;quot;MONO_MODE_ON&amp;quot;:&lt;br /&gt;
                self.data = ord(str[2])&lt;br /&gt;
            return str[3:]&lt;br /&gt;
&lt;br /&gt;
        elif x == 0xF0 or x == 0xF7:&lt;br /&gt;
            self.type = {0xF0: &amp;quot;F0_SYSEX_EVENT&amp;quot;,&lt;br /&gt;
                         0xF7: &amp;quot;F7_SYSEX_EVENT&amp;quot;}[x]&lt;br /&gt;
            length, str = getVariableLengthNumber(str[1:])&lt;br /&gt;
            self.data = str[:length]&lt;br /&gt;
            return str[length:]&lt;br /&gt;
&lt;br /&gt;
        elif x == 0xFF:&lt;br /&gt;
            if not metaEvents.has_value(z):&lt;br /&gt;
                print &amp;quot;Unknown meta event: FF %02X&amp;quot; % z&lt;br /&gt;
                sys.stdout.flush()&lt;br /&gt;
                raise &amp;quot;Unknown midi event type&amp;quot;&lt;br /&gt;
            self.type = metaEvents.whatis(z)&lt;br /&gt;
            length, str = getVariableLengthNumber(str[2:])&lt;br /&gt;
            self.data = str[:length]&lt;br /&gt;
            return str[length:]&lt;br /&gt;
&lt;br /&gt;
        raise &amp;quot;Unknown midi event type&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    def write(self):&lt;br /&gt;
        sysex_event_dict = {&amp;quot;F0_SYSEX_EVENT&amp;quot;: 0xF0,&lt;br /&gt;
                            &amp;quot;F7_SYSEX_EVENT&amp;quot;: 0xF7}&lt;br /&gt;
        if channelVoiceMessages.hasattr(self.type):&lt;br /&gt;
            x = chr((self.channel - 1) +&lt;br /&gt;
                    getattr(channelVoiceMessages, self.type))&lt;br /&gt;
            if (self.type != &amp;quot;PROGRAM_CHANGE&amp;quot; and&lt;br /&gt;
                self.type != &amp;quot;CHANNEL_KEY_PRESSURE&amp;quot;):&lt;br /&gt;
                data = chr(self.pitch) + chr(self.velocity)&lt;br /&gt;
            else:&lt;br /&gt;
                data = chr(self.data)&lt;br /&gt;
            return x + data&lt;br /&gt;
&lt;br /&gt;
        elif channelModeMessages.hasattr(self.type):&lt;br /&gt;
            x = getattr(channelModeMessages, self.type)&lt;br /&gt;
            x = (chr(0xB0 + (self.channel - 1)) +&lt;br /&gt;
                 chr(x) +&lt;br /&gt;
                 chr(self.data))&lt;br /&gt;
            return x&lt;br /&gt;
&lt;br /&gt;
        elif sysex_event_dict.has_key(self.type):&lt;br /&gt;
            str = chr(sysex_event_dict[self.type])&lt;br /&gt;
            str = str + putVariableLengthNumber(len(self.data))&lt;br /&gt;
            return str + self.data&lt;br /&gt;
&lt;br /&gt;
        elif metaEvents.hasattr(self.type):&lt;br /&gt;
            str = chr(0xFF) + chr(getattr(metaEvents, self.type))&lt;br /&gt;
            str = str + putVariableLengthNumber(len(self.data))&lt;br /&gt;
            return str + self.data&lt;br /&gt;
&lt;br /&gt;
        else:&lt;br /&gt;
            raise &amp;quot;unknown midi event type: &amp;quot; + self.type&lt;br /&gt;
&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
register_note() is a hook that can be overloaded from a script that&lt;br /&gt;
imports this module. Here is how you might do that, if you wanted to&lt;br /&gt;
store the notes as tuples in a list. Including the distinction&lt;br /&gt;
between track and channel offers more flexibility in assigning voices.&lt;br /&gt;
&lt;br /&gt;
import midi&lt;br /&gt;
notelist = [ ]&lt;br /&gt;
def register_note(t, c, p, v, t1, t2):&lt;br /&gt;
    notelist.append((t, c, p, v, t1, t2))&lt;br /&gt;
midi.register_note = register_note&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
def register_note(track_index, channel_index, pitch, velocity,&lt;br /&gt;
                  keyDownTime, keyUpTime):&lt;br /&gt;
    pass&lt;br /&gt;
&lt;br /&gt;
class MidiChannel:&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;A channel (together with a track) provides the continuity&lt;br /&gt;
connecting&lt;br /&gt;
    a NOTE_ON event with its corresponding NOTE_OFF event. Together,&lt;br /&gt;
those&lt;br /&gt;
    define the beginning and ending times for a Note.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, track, index):&lt;br /&gt;
        self.index = index&lt;br /&gt;
        self.track = track&lt;br /&gt;
        self.pitches = { }&lt;br /&gt;
&lt;br /&gt;
    def __repr__(self):&lt;br /&gt;
        return &amp;quot;&amp;lt;MIDI channel %d&amp;gt;&amp;quot; % self.index&lt;br /&gt;
&lt;br /&gt;
    def noteOn(self, pitch, time, velocity):&lt;br /&gt;
        self.pitches[pitch] = (time, velocity)&lt;br /&gt;
&lt;br /&gt;
    def noteOff(self, pitch, time):&lt;br /&gt;
        if self.pitches.has_key(pitch):&lt;br /&gt;
            keyDownTime, velocity = self.pitches[pitch]&lt;br /&gt;
            register_note(self.track.index, self.index, pitch, velocity,&lt;br /&gt;
                          keyDownTime, time)&lt;br /&gt;
            del self.pitches[pitch]&lt;br /&gt;
        # The case where the pitch isn't in the dictionary is illegal,&lt;br /&gt;
        # I think, but we probably better just ignore it.&lt;br /&gt;
&lt;br /&gt;
class DeltaTime(MidiEvent):&lt;br /&gt;
&lt;br /&gt;
    type = &amp;quot;DeltaTime&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    def read(self, oldstr):&lt;br /&gt;
        self.time, newstr = getVariableLengthNumber(oldstr)&lt;br /&gt;
        return self.time, newstr&lt;br /&gt;
&lt;br /&gt;
    def write(self):&lt;br /&gt;
        str = putVariableLengthNumber(self.time)&lt;br /&gt;
        return str&lt;br /&gt;
&lt;br /&gt;
class MidiTrack:&lt;br /&gt;
&lt;br /&gt;
    def __init__(self, index):&lt;br /&gt;
        self.index = index&lt;br /&gt;
        self.events = [ ]&lt;br /&gt;
        self.channels = [ ]&lt;br /&gt;
        self.length = 0&lt;br /&gt;
        for i in range(16):&lt;br /&gt;
            self.channels.append(MidiChannel(self, i+1))&lt;br /&gt;
&lt;br /&gt;
    def read(self, str):&lt;br /&gt;
        time = 0&lt;br /&gt;
        assert str[:4] == &amp;quot;MTrk&amp;quot;&lt;br /&gt;
        length, str = getNumber(str[4:], 4)&lt;br /&gt;
        self.length = length&lt;br /&gt;
        mystr = str[:length]&lt;br /&gt;
        remainder = str[length:]&lt;br /&gt;
        while mystr:&lt;br /&gt;
            delta_t = DeltaTime(self)&lt;br /&gt;
            dt, mystr = delta_t.read(mystr)&lt;br /&gt;
            time = time + dt&lt;br /&gt;
            self.events.append(delta_t)&lt;br /&gt;
            e = MidiEvent(self)&lt;br /&gt;
            mystr = e.read(time, mystr)&lt;br /&gt;
            self.events.append(e)&lt;br /&gt;
        return remainder&lt;br /&gt;
&lt;br /&gt;
    def write(self):&lt;br /&gt;
        time = self.events[0].time&lt;br /&gt;
        # build str using MidiEvents&lt;br /&gt;
        str = &amp;quot;&amp;quot;&lt;br /&gt;
        for e in self.events:&lt;br /&gt;
            str = str + e.write()&lt;br /&gt;
        return &amp;quot;MTrk&amp;quot; + putNumber(len(str), 4) + str&lt;br /&gt;
&lt;br /&gt;
    def __repr__(self):&lt;br /&gt;
        r = &amp;quot;&amp;lt;MidiTrack %d -- %d events\n&amp;quot; % (self.index,&lt;br /&gt;
len(self.events))&lt;br /&gt;
        for e in self.events:&lt;br /&gt;
            r = r + &amp;quot;    &amp;quot; + `e` + &amp;quot;\n&amp;quot;&lt;br /&gt;
        return r + &amp;quot;  &amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
class MidiFile:&lt;br /&gt;
&lt;br /&gt;
    def __init__(self):&lt;br /&gt;
        self.file = None&lt;br /&gt;
        self.format = 1&lt;br /&gt;
        self.tracks = [ ]&lt;br /&gt;
        self.ticksPerQuarterNote = None&lt;br /&gt;
        self.ticksPerSecond = None&lt;br /&gt;
&lt;br /&gt;
    def open(self, filename, attrib=&amp;quot;rb&amp;quot;):&lt;br /&gt;
        if filename == None:&lt;br /&gt;
            if attrib in [&amp;quot;r&amp;quot;, &amp;quot;rb&amp;quot;]:&lt;br /&gt;
                self.file = sys.stdin&lt;br /&gt;
            else:&lt;br /&gt;
                self.file = sys.stdout&lt;br /&gt;
        else:&lt;br /&gt;
            self.file = open(filename, attrib)&lt;br /&gt;
&lt;br /&gt;
    def __repr__(self):&lt;br /&gt;
        r = &amp;quot;&amp;lt;MidiFile %d tracks\n&amp;quot; % len(self.tracks)&lt;br /&gt;
        for t in self.tracks:&lt;br /&gt;
            r = r + &amp;quot;  &amp;quot; + `t` + &amp;quot;\n&amp;quot;&lt;br /&gt;
        return r + &amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    def close(self):&lt;br /&gt;
        self.file.close()&lt;br /&gt;
&lt;br /&gt;
    def read(self):&lt;br /&gt;
        self.readstr(self.file.read())&lt;br /&gt;
&lt;br /&gt;
    def readstr(self, str):&lt;br /&gt;
        assert str[:4] == &amp;quot;MThd&amp;quot;&lt;br /&gt;
        length, str = getNumber(str[4:], 4)&lt;br /&gt;
        assert length == 6&lt;br /&gt;
        format, str = getNumber(str, 2)&lt;br /&gt;
        self.format = format&lt;br /&gt;
        assert format == 0 or format == 1   # dunno how to handle 2&lt;br /&gt;
        numTracks, str = getNumber(str, 2)&lt;br /&gt;
        division, str = getNumber(str, 2)&lt;br /&gt;
        if division &amp;amp; 0x8000:&lt;br /&gt;
            framesPerSecond = -((division &amp;gt;&amp;gt; 8) | -128)&lt;br /&gt;
            ticksPerFrame = division &amp;amp; 0xFF&lt;br /&gt;
            assert ticksPerFrame == 24 or ticksPerFrame == 25 or \&lt;br /&gt;
                   ticksPerFrame == 29 or ticksPerFrame == 30&lt;br /&gt;
            if ticksPerFrame == 29: ticksPerFrame = 30  # drop frame&lt;br /&gt;
            self.ticksPerSecond = ticksPerFrame * framesPerSecond&lt;br /&gt;
        else:&lt;br /&gt;
            self.ticksPerQuarterNote = division &amp;amp; 0x7FFF&lt;br /&gt;
        for i in range(numTracks):&lt;br /&gt;
            trk = MidiTrack(i)&lt;br /&gt;
            str = trk.read(str)&lt;br /&gt;
            self.tracks.append(trk)&lt;br /&gt;
&lt;br /&gt;
    def write(self):&lt;br /&gt;
        self.file.write(self.writestr())&lt;br /&gt;
&lt;br /&gt;
    def writestr(self):&lt;br /&gt;
        division = self.ticksPerQuarterNote&lt;br /&gt;
        # Don't handle ticksPerSecond yet, too confusing&lt;br /&gt;
        assert (division &amp;amp; 0x8000) == 0&lt;br /&gt;
        str = &amp;quot;MThd&amp;quot; + putNumber(6, 4) + putNumber(self.format, 2)&lt;br /&gt;
        str = str + putNumber(len(self.tracks), 2)&lt;br /&gt;
        str = str + putNumber(division, 2)&lt;br /&gt;
        for trk in self.tracks:&lt;br /&gt;
            str = str + trk.write()&lt;br /&gt;
        return str&lt;br /&gt;
&lt;br /&gt;
def main(argv):&lt;br /&gt;
    global debugflag&lt;br /&gt;
    import getopt&lt;br /&gt;
    infile = None&lt;br /&gt;
    outfile = None&lt;br /&gt;
    printflag = 0&lt;br /&gt;
    optlist, args = getopt.getopt(argv[1:], &amp;quot;i:o:pd&amp;quot;)&lt;br /&gt;
    for (option, value) in optlist:&lt;br /&gt;
        if option == '-i':&lt;br /&gt;
            infile = value&lt;br /&gt;
        elif option == '-o':&lt;br /&gt;
            outfile = value&lt;br /&gt;
        elif option == '-p':&lt;br /&gt;
            printflag = 1&lt;br /&gt;
        elif option == '-d':&lt;br /&gt;
            debugflag = 1&lt;br /&gt;
&lt;br /&gt;
    m = MidiFile()&lt;br /&gt;
    m.open(infile)&lt;br /&gt;
    m.read()&lt;br /&gt;
    m.close()&lt;br /&gt;
&lt;br /&gt;
    if printflag:&lt;br /&gt;
        print m&lt;br /&gt;
    else:&lt;br /&gt;
        m.open(outfile, &amp;quot;wb&amp;quot;)&lt;br /&gt;
        m.write()&lt;br /&gt;
        m.close()&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    main(sys.argv) &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=paserjar amb midi.py=&lt;br /&gt;
La sintaxi bàsica és:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ python midi.py -i Its_All_Good.mid&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
però si vull obtenir la informació interessant i de forma clara:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ python midi.py -p -d -i Its_All_Good.mid&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
i si vull enviar aquesta informació a un fitxer:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ python midi.py -p -d -i Its_All_Good.mid &amp;gt; itsallgood.txt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La primera línia ens diu que tenim 13 pistes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;MidiFile 13 tracks&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
En el primer track de fet hi ha dades generals, com per exemple el tempo, time signature:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;MidiTrack 0 -- 6 events&lt;br /&gt;
    &amp;lt;MidiEvent DeltaTime, t=0, track=0, channel=None&amp;gt;&lt;br /&gt;
    &amp;lt;MidiEvent SET_TEMPO, t=0, track=0, channel=None, data=&amp;quot;\t'\xc0&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;MidiEvent DeltaTime, t=0, track=0, channel=None&amp;gt;&lt;br /&gt;
    &amp;lt;MidiEvent TIME_SIGNATURE, t=0, track=0, channel=None, data='\x04\x02\x18\x08'&amp;gt;&lt;br /&gt;
    &amp;lt;MidiEvent DeltaTime, t=0, track=0, channel=None&amp;gt;&lt;br /&gt;
    &amp;lt;MidiEvent END_OF_TRACK, t=0, track=0, channel=None, data=''&amp;gt;&lt;br /&gt;
  &amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
i el track1 ja és un track normal on es dóna informació de la pista (''lead'') i veiem que primer s'envia un program change per definir el patch que sonarà i ja podem veure els note on i note off. També veiem que en aquest track els missatges s'envien pel canal 1 (té associat el canal 1).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;MidiTrack 1 -- 2228 events&lt;br /&gt;
    &amp;lt;MidiEvent DeltaTime, t=0, track=1, channel=None&amp;gt;&lt;br /&gt;
    &amp;lt;MidiEvent SEQUENCE_TRACK_NAME, t=0, track=1, channel=None, data='Lead'&amp;gt;&lt;br /&gt;
    &amp;lt;MidiEvent DeltaTime, t=0, track=1, channel=None&amp;gt;&lt;br /&gt;
    &amp;lt;MidiEvent PROGRAM_CHANGE, t=0, track=1, channel=1, data=26&amp;gt;&lt;br /&gt;
    &amp;lt;MidiEvent DeltaTime, t=0, track=1, channel=None&amp;gt;&lt;br /&gt;
    &amp;lt;MidiEvent CONTROLLER_CHANGE, t=0, track=1, channel=1, pitch=7, velocity=98&amp;gt;&lt;br /&gt;
    &amp;lt;MidiEvent DeltaTime, t=50804, track=1, channel=None&amp;gt;&lt;br /&gt;
    &amp;lt;MidiEvent NOTE_ON, t=50804, track=1, channel=1, pitch=61, velocity=115&amp;gt;&lt;br /&gt;
    &amp;lt;MidiEvent DeltaTime, t=99, track=1, channel=None&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{Autor}}, novembre 2011&lt;/div&gt;</summary>
		<author><name>Joan</name></author>
		
	</entry>
</feed>