예제 #1
0
    def addProgChange(self, offset, program, oldprg):
        """ Create a midi program change (handles extended voicing).

            program - The MIDI program (voice) value
            oldprg  - existing MIDI program
        """

        # We truck around a special pseudo voice 'NONE' or 127.127.127 which
        # is a signal that we don't want mma to set the voicing. Might be
        # useful when know the track that mma is using and we have preset
        # an external synth.

        if program == NONETONE:
            return

        v1, lsb1, msb1 = MMA.midiC.voice2tup(oldprg)
        v2, lsb2, msb2 = MMA.midiC.voice2tup(program)

        if msb1 != msb2:   # only if CTRL32 has changed
            self.addToTrack(offset, packBytes((0xb0 | self.channel, 0x20, msb2)))

        if lsb1 != lsb2:   # only if CTRL0 has changed
            self.addToTrack(offset, packBytes((0xb0 | self.channel, 0x00, lsb2)))

        # Always do voice change. Maybe not necessary, but let's be safe.

        self.addToTrack(offset, packBytes((0xc0 | self.channel, v2)), MIDI_PRG)
예제 #2
0
    def addProgChange(self, offset, program, oldprg):
        """ Create a midi program change (handles extended voicing).

            program - The MIDI program (voice) value
            oldprg  - existing MIDI program
        """

        # We truck around a special pseudo voice 'NONE' or 127.127.127 which
        # is a signal that we don't want mma to set the voicing. Might be
        # useful when know the track that mma is using and we have preset
        # an external synth.

        if program == NONETONE:
            return

        v1, lsb1, msb1 = MMA.midiC.voice2tup(oldprg)
        v2, lsb2, msb2 = MMA.midiC.voice2tup(program)

        if msb1 != msb2:   # only if CTRL32 has changed
            self.addToTrack(offset, packBytes((0xb0 | self.channel, 0x20, msb2)))

        if lsb1 != lsb2:   # only if CTRL0 has changed
            self.addToTrack(offset, packBytes((0xb0 | self.channel, 0x00, lsb2)))

        # Always do voice change. Maybe not necessary, but let's be safe.

        self.addToTrack(offset, packBytes((0xc0 | self.channel, v2)), MIDI_PRG)
예제 #3
0
파일: midi.py 프로젝트: infojunkie/mma
    def addNoteOnToTrack(self, boffset, note, v, startRnd=None, endRnd=None):
        """ Add a single note on or note off when v=0 to a track.
            boffset      - offset into current bar
            duration  - note len
            note      - midi value of note
            v      - midi velocity, set to 0 for note off
            startRnd/endRnd  - rand val start adjustment

            Added by louisjb for plectrum.
        """

        # Start offsets

        onOffset = getOffset(boffset, startRnd, endRnd)
        onEvent = packBytes((0x90 | self.channel, note, v))

        # this avoids situations where the new note is placed
        # before a NoteOff event. This could be due to a bad
        # sequence specification or, more likely, due to
        # RTIME setting the start point of this note before
        # the current beat in a tight sequence.
        f = self.lastOffEvent[note]
        if f is not None and f >= onOffset:
            offEvent = packBytes(onEvent[:-1], 0)
            if offEvent in self.miditrk[f]:
                self.miditrk[f].remove(offEvent)

        # ON/OFF events (off is on with v = 0)


        self.addToTrack(onOffset, onEvent, MIDI_NOTE)
        if v==0:
            self.lastOffEvent[note] = onOffset
        return onOffset
예제 #4
0
    def addTrkName(self, offset, msg):
        """ Creates a midi track name event. """

        offset = 0  # ignore user offset, always put this at 0

        self.trackname = msg

        cmd = packBytes((0xff, 0x03))
        self.delDup(offset, cmd)
        self.addToTrack(offset, packBytes(cmd, intToVarNumber(len(msg)), msg))
예제 #5
0
    def addTrkName(self, offset, msg):
        """ Creates a midi track name event. """

        offset = 0  # ignore user offset, always put this at 0

        self.trackname = msg

        cmd = packBytes((0xff, 0x03))
        self.delDup(offset, cmd)
        self.addToTrack(offset, packBytes(cmd, intToVarNumber(len(msg)), msg))
예제 #6
0
    def addTempo(self, offset, beats):
        """ Create a midi tempo meta event.

             beats - beats per second

             Return - packed midi string
        """

        cmd = packBytes((0xff, 0x51, 0x03))
        self.delDup(offset, cmd)

        self.addToTrack(offset, packBytes(cmd, intTo3Byte(60000000 // beats)))
예제 #7
0
    def addTempo(self, offset, beats):
        """ Create a midi tempo meta event.

             beats - beats per second

             Return - packed midi string
        """

        cmd = packBytes((0xff, 0x51, 0x03))
        self.delDup(offset, cmd)

        self.addToTrack(offset, packBytes(cmd, intTo3Byte(60000000 // beats)))
예제 #8
0
파일: midi.py 프로젝트: infojunkie/mma
    def addTempo(self, offset, bpm):
        """ Create a midi tempo meta event.

             bpm - beats per minute

             Return - packed midi string
        """

        cmd = packBytes((0xff, 0x51, 0x03))
        self.delDup(offset, cmd)

        self.addToTrack(offset, packBytes(cmd, intTo3Byte(60000000 // bpm)))
        tempoChanges.append([offset, bpm])
예제 #9
0
    def addGlis(self, offset, v):
        """ Set the portamento. LowLevel MIDI.

            This does 2 things:
                1. turns portamento on/off,
                2. sets the LSN rate.
        """

        if v == 0:
            self.addToTrack(offset, packBytes((0xb0 | self.channel, 0x41, 0x00)))

        else:
            self.addToTrack(offset, packBytes((0xb0 | self.channel, 0x41, 0x7f)))
            self.addToTrack(offset, packBytes((0xb0 | self.channel, 0x05, v)))
예제 #10
0
    def addGlis(self, offset, v):
        """ Set the portamento. LowLevel MIDI.

            This does 2 things:
                1. turns portamento on/off,
                2. sets the LSN rate.
        """

        if v == 0:
            self.addToTrack(offset, packBytes((0xb0 | self.channel, 0x41, 0x00)))

        else:
            self.addToTrack(offset, packBytes((0xb0 | self.channel, 0x41, 0x7f)))
            self.addToTrack(offset, packBytes((0xb0 | self.channel, 0x05, v)))
예제 #11
0
    def addChannelVol(self, offset, v):
        """ Set the midi channel volume."""

        tr = self.miditrk
        cvol = packBytes(0xb0 | self.channel, 0x07)  # 2 byte channel vol

        # Before adding a new channel volume event we check to see if there
        # are any future channel volume events and delete them.
        
        for off in tr:
            if off >= offset:
                tr[off] = [e for e in tr[off] if e[0:2] != cvol]
        
        self.addToTrack(offset, packBytes(cvol, v))
예제 #12
0
    def addChannelVol(self, offset, v):
        """ Set the midi channel volume."""

        tr = self.miditrk
        cvol = packBytes(0xb0 | self.channel, 0x07)  # 2 byte channel vol

        # Before adding a new channel volume event we check to see if there
        # are any future channel volume events and delete them.
        
        for off in tr:
            if off >= offset:
                tr[off] = [e for e in tr[off] if e[0:2] != cvol]
        
        self.addToTrack(offset, packBytes(cvol, v))
예제 #13
0
    def addCopyright(self, offset, msg):
        """ Insert copyright. """

        # should never happen since the caller sets offset=0
        if offset != 0:
            error("Copyright message must be at offset 0, not %s." % offset)

        # We need to bypass addToTrack to force copyright to the start of the track.

        # Create the copyright event
        ev = packBytes((0xff, 0x02), intToVarNumber(len(msg)), msg)

        tr = self.miditrk   # this is the meta track

        # We keep a pointer (ipoint) which points to the position of
        # the last copyright string. If there isn't one, we create
        # it in the expect; else it's just incremented

        try:
            self.ipoint += 1
        except AttributeError:
            self.ipoint = 0

        if offset in tr:
            tr[offset].insert(self.ipoint, ev)
        else:
            tr[offset] = [ev]
예제 #14
0
    def addCopyright(self, offset, msg):
        """ Insert copyright. """

        # should never happen since the caller sets offset=0
        if offset != 0:
            error("Copyright message must be at offset 0, not %s." % offset)

        # We need to bypass addToTrack to force copyright to the start of the track.

        # Create the copyright event
        ev = packBytes((0xff, 0x02), intToVarNumber(len(msg)), msg)

        tr = self.miditrk   # this is the meta track

        # We keep a pointer (ipoint) which points to the position of
        # the last copyright string. If there isn't one, we create
        # it in the expect; else it's just incremented

        try:
            self.ipoint += 1
        except AttributeError:
            self.ipoint = 0

        if offset in tr:
            tr[offset].insert(self.ipoint, ev)
        else:
            tr[offset] = [ev]
예제 #15
0
    def addNoteOff(self, offset):
        """ Insert a "All Note Off" into the midi stream.

            Called from the cutTrack() function.
        """

        self.addToTrack(offset, packBytes((0xb0 | self.channel, 0x7b, 0)), MIDI_NOTE )
예제 #16
0
def insertControl(trk, ln):
    """ Insert a controller event. """

    if len(ln) != 3:
        error("MidiNote: Controller expecting 3 arguments.")

    offset = getoffset(trk, ln[0])

    v = MMA.midiC.ctrlToValue(ln[1])
    if v < 0:
        v = stoi(ln[1])
        if v < 0 or v > 0x7F:
            error("MidiNote: Controller values must be 0x00 to 0x7f, not '%s'." % ln[1])

    d = stoi(ln[2])
    if d < 0 or d > 0x7F:
        error("MidiNote: Control Datum value must be 0x00 to 0x7f, not '%s'." % ln[2])

    channel = trk.channel
    track = gbl.mtrks[channel]

    # bypass the addctl() defined in midi.py just to keep all the calls in this
    # module similar. We should have add**() command in midi.py for the above stuff
    # and redo this.???

    track.addToTrack(gbl.tickOffset + offset, packBytes((0xB0 | channel - 1, v, d)))

    if gbl.debug:
        print("MidiNote Ctrl %s: inserted Controller %s value %s at offset %s." % (trk.name, v, d, offset))
예제 #17
0
def insertControl(trk, ln):
    """ Insert a controller event. """

    if len(ln) != 3:
        error("MidiNote: Controller expecting 3 arguments.")

    offset = getoffset(trk, ln[0])

    v = MMA.midiC.ctrlToValue(ln[1])
    if v < 0:
        v = stoi(ln[1])
        if v < 0 or v > 0x7f:
            error(
                "MidiNote: Controller values must be 0x00 to 0x7f, not '%s'." %
                ln[1])

    d = stoi(ln[2])
    if d < 0 or d > 0x7f:
        error("MidiNote: Control Datum value must be 0x00 to 0x7f, not '%s'." %
              ln[2])

    channel = trk.channel
    track = gbl.mtrks[channel]

    # bypass the addctl() defined in midi.py just to keep all the calls in this
    # module similar. We should have add**() command in midi.py for the above stuff
    # and redo this.???

    track.addToTrack(gbl.tickOffset + offset,
                     packBytes((0xb0 | channel - 1, v, d)))

    if gbl.debug:
        print(
            "MidiNote Ctrl %s: inserted Controller %s value %s at offset %s." %
            (trk.name, v, d, offset))
예제 #18
0
    def addNoteOff(self, offset):
        """ Insert a "All Note Off" into the midi stream.

            Called from the cutTrack() function.
        """

        self.addToTrack(offset, packBytes((0xb0 | self.channel, 0x7b, 0)), MIDI_NOTE )
예제 #19
0
def insertPBrange(trk, ln):
    """ Insert a range of PB events. """

    if len(ln) != 3:
        error(
            "MidiNote: PBR expecting 3 arguments <count> <start,end> <v1,v2>.")

    count = stoi(ln[0])
    try:
        s1, s2 = ln[1].split(',')
    except:
        error("MidiNote PBR: event range must be 'v1,v2', not '%s'." % ln[1])
    s1 = getoffset(trk, s1)
    s2 = getoffset(trk, s2)
    tinc = (s2 - s1) / float(count)

    try:
        v1, v2 = ln[2].split(',')
    except:
        error("MidiNote PBR: pitch blend range must be 'v1,v2', not '%s'." %
              ln[2])
    v1 = stoi(v1)
    v2 = stoi(v2)

    if v1 < -8192 or v1 > 8191 or v2 < -8192 or v2 > 8191:
        error("MidiNote: PBR values must be -8192..+8191, not '%s'." % ln[2])

    v1 += 8192  # convert to 0..16383, max 14 bit value
    v2 += 8192
    vinc = (v2 - v1) / float(count)

    channel = trk.channel
    track = gbl.mtrks[channel]

    ev = packBytes(0xe0 | channel - 1)
    offset = s1
    bend = v1
    for i in range(count + 1):
        v = int(bend)
        track.addToTrack(gbl.tickOffset + int(offset),
                         packBytes(ev, v % 128, v // 128))
        offset += tinc
        bend += vinc

    if gbl.debug:
        print("MidiNote PBR %s: inserted bends %s to %s at offsets %s to %s." %
              (trk.name, v1 - 8192, v2 - 8192, s1, s2))
예제 #20
0
def insertChTouchRange(trk, ln):
    """ Insert a range of channel aftertouch events. """

    if len(ln) != 3:
        error(
            "MidiNote: ChATR expecting 3 arguments <count> <start,end> <v1,v2>."
        )

    count = stoi(ln[0])
    try:
        s1, s2 = ln[1].split(',')
    except:
        error("MidiNote ChATR: event range must be 'v1,v2', not '%s'." % ln[1])
    s1 = getoffset(trk, s1)
    s2 = getoffset(trk, s2)
    tinc = (s2 - s1) / float(count)

    try:
        v1, v2 = ln[2].split(',')
    except:
        error("MidiNote ChATR: range must be 'v1,v2', not '%s'." % ln[2])
    v1 = stoi(v1)
    v2 = stoi(v2)

    if v1 < 0 or v1 > 127 or v2 < 0 or v2 > 127:
        error("MidiNote: ChATR values must be 0.. 127, not '%s'." % ln[2])

    vinc = (v2 - v1) / float(count)

    channel = trk.channel
    track = gbl.mtrks[channel]

    ev = packBytes(0xd0 | channel - 1)
    offset = s1
    bend = v1
    for i in range(count + 1):
        v = int(bend)
        track.addToTrack(gbl.tickOffset + int(offset), packBytes(ev, v))
        offset += tinc
        bend += vinc

    if gbl.debug:
        print(
            "MidiNote ChATR %s: inserted events %s to %s at offsets %s to %s."
            % (trk.name, v1, v2, s1, s2))
예제 #21
0
def insertPBrange(trk, ln):
    """ Insert a range of PB events. """

    if len(ln) != 3:
        error("MidiNote: PBR expecting 3 arguments <count> <start,end> <v1,v2>.")

    count = stoi(ln[0])
    try:
        s1, s2 = ln[1].split(",")
    except:
        error("MidiNote PBR: event range must be 'v1,v2', not '%s'." % ln[1])
    s1 = getoffset(trk, s1)
    s2 = getoffset(trk, s2)
    tinc = (s2 - s1) / float(count)

    try:
        v1, v2 = ln[2].split(",")
    except:
        error("MidiNote PBR: pitch blend range must be 'v1,v2', not '%s'." % ln[2])
    v1 = stoi(v1)
    v2 = stoi(v2)

    if v1 < -8192 or v1 > 8191 or v2 < -8192 or v2 > 8191:
        error("MidiNote: PBR values must be -8192..+8191, not '%s'." % ln[2])

    v1 += 8192  # convert to 0..16383, max 14 bit value
    v2 += 8192
    vinc = (v2 - v1) / float(count)

    channel = trk.channel
    track = gbl.mtrks[channel]

    ev = packBytes(0xE0 | channel - 1)
    offset = s1
    bend = v1
    for i in range(count + 1):
        v = int(bend)
        track.addToTrack(gbl.tickOffset + int(offset), packBytes(ev, v % 128, v // 128))
        offset += tinc
        bend += vinc

    if gbl.debug:
        print(
            "MidiNote PBR %s: inserted bends %s to %s at offsets %s to %s." % (trk.name, v1 - 8192, v2 - 8192, s1, s2)
        )
예제 #22
0
    def addTimeSig(self, offset,  nn, dd, cc, bb):
        """ Create a midi time signature.

            delta - midi delta offset
            nn = sig numerator, beats per measure
            dd - sig denominator, 2=quarter note, 3=eighth,
            cc - midi clocks/tick
            bb - # of 32nd notes in quarter (normally 8)

            This is only called by timeSig.create(). Don't
            call this directly since the timeSig.create() checks for
            duplicate settings.
        """

        cmd = packBytes(0xff, 0x58)
        # we might have several different timesigs on the same offset,
        # so take time to delete any. 
        self.delDup(offset, cmd)
        self.addToTrack(offset, packBytes(cmd, (0x04, nn, dd, cc, bb)))
예제 #23
0
    def addTimeSig(self, offset,  nn, dd, cc, bb):
        """ Create a midi time signature.

            delta - midi delta offset
            nn = sig numerator, beats per measure
            dd - sig denominator, 2=quarter note, 3=eighth,
            cc - midi clocks/tick
            bb - # of 32nd notes in quarter (normally 8)

            This is only called by timeSig.set(). Don't
            call this directly since the timeSig.set() checks for
            duplicate settings.
        """

        cmd = packBytes(0xff, 0x58)
        # we might have several different timesigs on the same offset,
        # so take time to delete any. 
        self.delDup(offset, cmd)
        self.addToTrack(offset, packBytes(cmd, (0x04, nn, dd, cc, bb)))
예제 #24
0
def insertChTouchRange(trk, ln):
    """ Insert a range of channel aftertouch events. """

    if len(ln) != 3:
        error("MidiNote: ChATR expecting 3 arguments <count> <start,end> <v1,v2>.")

    count = stoi(ln[0])
    try:
        s1, s2 = ln[1].split(",")
    except:
        error("MidiNote ChATR: event range must be 'v1,v2', not '%s'." % ln[1])
    s1 = getoffset(trk, s1)
    s2 = getoffset(trk, s2)
    tinc = (s2 - s1) / float(count)

    try:
        v1, v2 = ln[2].split(",")
    except:
        error("MidiNote ChATR: range must be 'v1,v2', not '%s'." % ln[2])
    v1 = stoi(v1)
    v2 = stoi(v2)

    if v1 < 0 or v1 > 127 or v2 < 0 or v2 > 127:
        error("MidiNote: ChATR values must be 0.. 127, not '%s'." % ln[2])

    vinc = (v2 - v1) / float(count)

    channel = trk.channel
    track = gbl.mtrks[channel]

    ev = packBytes(0xD0 | channel - 1)
    offset = s1
    bend = v1
    for i in range(count + 1):
        v = int(bend)
        track.addToTrack(gbl.tickOffset + int(offset), packBytes(ev, v))
        offset += tinc
        bend += vinc

    if gbl.debug:
        print("MidiNote ChATR %s: inserted events %s to %s at offsets %s to %s." % (trk.name, v1, v2, s1, s2))
예제 #25
0
    def addMasterVolume(self, offset, v):
        """ System Exclusive master volume message. Meta track. """

        # We send to packBytes as a long list, no tuples. Just a bit
        # easier to maintain a long list like this.
        self.addToTrack(offset, packBytes(
                0xf0,       # Start sysex
                0x07,       # message size (needed for SMF)
                0x7f,       # realtime
                0x7f,       # disregard channel
                0x04,       # device control
                0x01,       # master volume
                intTo14(v), # params (14 bit)
                0xf7 ))     # EOX
예제 #26
0
    def addMasterVolume(self, offset, v):
        """ System Exclusive master volume message. Meta track. """

        # We send to packBytes as a long list, no tuples. Just a bit
        # easier to maintain a long list like this.
        self.addToTrack(offset, packBytes(
                0xf0,       # Start sysex
                0x07,       # message size (needed for SMF)
                0x7f,       # realtime
                0x7f,       # disregard channel
                0x04,       # device control
                0x01,       # master volume
                intTo14(v), # params (14 bit)
                0xf7 ))     # EOX
예제 #27
0
def insertPB(trk, ln):
    """ Insert a pitch controller event. """

    if len(ln) != 2:
        error("MidiNote: PB expecting 2 arguments.")

    offset = getoffset(trk, ln[0])
    v = stoi(ln[1])

    if v < -8192 or v > 8191:
        error("MidiNote: PB value must be -8192..+8191, not '%s'." % v)

    v += 8192  # convert to 0..16383, max 14 bit value

    channel = trk.channel
    track = gbl.mtrks[channel]
    track.addToTrack(gbl.tickOffset + offset, packBytes((0xe0 | channel-1, v % 128, v // 128)))

    if MMA.debug.debug:
        dPrint("MidiNote PB %s: inserted bend %s at offset %s." % (trk.name, v-8192, offset))
예제 #28
0
def insertChTouch(trk, ln):
    """ Insert a channel aftertouch) event. """

    if len(ln) != 2:
        error("MidiNote: ChAT expecting 2 arguments.")

    offset = getoffset(trk, ln[0])

    v = stoi(ln[1])

    if v < 0 or v > 127:
        error("MidiNote: ChAT value must be 0 .. 127, not '%s'." % v)

    channel = trk.channel
    track = gbl.mtrks[channel]

    track.addToTrack(gbl.tickOffset + offset, packBytes((0xD0 | channel - 1, v)))

    if gbl.debug:
        print("MidiNote ChAT %s: inserted channel aftertouch %s at offset %s." % (trk.name, v, offset))
예제 #29
0
def insertPB(trk, ln):
    """ Insert a pitch controller event. """

    if len(ln) != 2:
        error("MidiNote: PB expecting 2 arguments.")

    offset = getoffset(trk, ln[0])
    v = stoi(ln[1])

    if v < -8192 or v > 8191:
        error("MidiNote: PB value must be -8192..+8191, not '%s'." % v)

    v += 8192  # convert to 0..16383, max 14 bit value

    channel = trk.channel
    track = gbl.mtrks[channel]
    track.addToTrack(gbl.tickOffset + offset, packBytes((0xE0 | channel - 1, v % 128, v // 128)))

    if gbl.debug:
        print("MidiNote PB %s: inserted bend %s at offset %s." % (trk.name, v - 8192, offset))
예제 #30
0
    def addNoteOnToTrack(self, boffset, note, v, startRnd=None, endRnd=None):
        """ Add a single note on or note off when v=0 to a track.
            boffset      - offset into current bar
            duration  - note len
            note      - midi value of note
            v      - midi velocity, set to 0 for note off
            startRnd/endRnd  - rand val start adjustment

            Added by louisjb for plectrum.
        """

        # Start offsets

        onOffset = getOffset(boffset, startRnd, endRnd)

        # ON/OFF events (off is on with v = 0)

        onEvent = packBytes((0x90 | self.channel, note, v))

        self.addToTrack(onOffset, onEvent, MIDI_NOTE)
        return onOffset
예제 #31
0
def insertChTouch(trk, ln):
    """ Insert a channel aftertouch) event. """

    if len(ln) != 2:
        error("MidiNote: ChAT expecting 2 arguments.")

    offset = getoffset(trk, ln[0])

    v = stoi(ln[1])

    if v < 0 or v > 127:
        error("MidiNote: ChAT value must be 0 .. 127, not '%s'." % v)

    channel = trk.channel
    track = gbl.mtrks[channel]

    track.addToTrack(gbl.tickOffset + offset, packBytes((0xd0 | channel-1, v)))
 
    if MMA.debug.debug:
        dPrint("MidiNote ChAT %s: inserted channel aftertouch %s at offset %s." % 
            (trk.name, v, offset))
예제 #32
0
    def addNoteOnToTrack(self, boffset, note, v, startRnd=None, endRnd=None):
        """ Add a single note on or note off when v=0 to a track.
            boffset      - offset into current bar
            duration  - note len
            note      - midi value of note
            v      - midi velocity, set to 0 for note off
            startRnd/endRnd  - rand val start adjustment

            Added by louisjb for plectrum.
        """

        # Start offsets

        onOffset = getOffset(boffset, startRnd, endRnd)

        # ON/OFF events (off is on with v = 0)

        onEvent = packBytes((0x90 | self.channel, note, v))

        self.addToTrack(onOffset, onEvent, MIDI_NOTE)
        return onOffset
예제 #33
0
    def writeMidiTrack(self, out):
        """ Create/write the MIDI track.

            We convert timing offsets to midi-deltas.
        """

        tr = self.miditrk

        """ If the -1 command line option is set we need to add a terminate
            to the end of each track. This is done to make looping
            software like seq24 happy. We do this by truncating all
            data in the file past the current tick pointer and inserting
            an all-notes-off at that position.
        """

        if MMA.sync.endsync and self.channel >= 0:
            eof = gbl.tickOffset
            for offset in tr.keys():
                if offset > eof:
                    del tr[offset]
            self.addToTrack(eof, packBytes((0xb0 | self.channel, 0x7b, 0)))

        """ To every MIDI track we generate we add (if the -0 flag
            was set) an on/off beep at offset 0. This makes for
            easier sync in multi-tracks.
        """

        if MMA.sync.synctick and self.channel >= 0:
            t, v = MMA.sync.syncTone
            self.addToTrack(0, packBytes((0x90 | self.channel, t, v)))
            self.addToTrack(1, packBytes((0x90 | self.channel, t, 0)))

        if MMA.debug.debug:
            ttl = 0
            lg = 1
            for t in tr:
                a = len(tr[t])
                if a > lg:
                    lg = a
                ttl += a
            if self.channel == -1:
                nm = "META"
            else:
                nm = self.trackname
            dPrint( "<%s> Unique ts: %s; Ttl events %s; Average ev/ts %.2f" %
                (nm, len(tr), ttl, float(ttl)/len(tr)))

        last = 0

        # Convert all events to MIDI deltas and store in
        # the track array/list

        tdata = []        # empty track container
        lastSts = None    # Running status tracker

        for a in sorted(tr.keys()):
            delta = a-last

            if not tr[a]:
                continue  # this skips the delta offset update!

            for d in tr[a]:
                """ Running status check. For each packet compare
                    the first byte with the first byte of the previous
                    packet. If it is can be converted to running status
                    we strip out byte 0. Note that valid running status
                    byte are 0x80..0xef. 0xfx are system messages
                    and are not suitable for running status.
                """

                if len(d) > 1:
                    if d[0] == lastSts:
                        d = d[1:]
                    else:
                        lastSts = d[0]
                        if lastSts < 0x80 or lastSts > 0xef or not gbl.runningStatus:
                            lastSts = None

                tdata.extend([intToVarNumber(delta), d])
                delta = 0
            last = a

        # Add an EOF to the track (included in total track size)

        tdata.append(intToVarNumber(0))
        tdata.append(packBytes((0xff, 0x2f, 0x00)))

        tdata = bytearray(b'').join(tdata)
        totsize = len(tdata)

        out.write(b"MTrk")
        out.write(intToLong(totsize))
        out.write(tdata)
예제 #34
0
def mkHeader(count, tempo, Mtype):

    return packBytes("MThd", intToLong(6), intToWord(Mtype), intToWord(count), intToWord(tempo))
예제 #35
0
    def addLyric(self, offset, msg):
        """ Create a midi lyric event. """

        self.addToTrack(offset, packBytes((0xff, 0x05), intToVarNumber(len(msg)), msg))
예제 #36
0
def fermata(ln):
    """ Apply a fermata timing to the specified beat. """

    if len(ln) != 3:
        error("Fermata: use 'offset' 'duration' 'adjustment'")

    offset = stof(ln[0],
                  "Fermata: Expecting a value (not '%s') for offset" % ln[0])

    if offset < -gbl.QperBar or offset > gbl.QperBar:
        warning("Fermata: %s is a large beat offset" % offset)

    dur = stof(ln[1],
               "Fermata: Expecting a value (not '%s') for duration" % ln[1])

    if dur <= 0:
        error("Fermata: duration must be greater than 0")

    if dur > gbl.QperBar:
        warning("Fermata: %s is a large duration." % dur)

    adj = stof(ln[2],
               "Fermata: expecting a value (not '%s') for adjustment." % ln[2])

    if adj < 100:
        warning("Fermata: Adjustment less than 100 is shortening beat value.")

    if adj == 100:
        error(
            "Fermata: using value of 100 makes no difference, must be an error."
        )

    moff = int(gbl.tickOffset + (gbl.BperQ * offset))

    if moff < 0:
        error("Fermata: Offset comes before track start.")

    if offset >= 0:
        warning(
            "Fermata: Better results when placed after event (negative offset)."
        )

    fermataDuration = int(gbl.BperQ * dur)  # Duration in ticks
    mend = moff + fermataDuration

    # This next section is needed to figure out the start tempo (which
    # is not always gbl.tempo!), the needed tempo at the end of the fermata
    # section (again, not always gbl.tempo!) and to delete any tempo changes
    # in the section. All this due to the effects of tempo changes over a
    # a set of bars.

    # Extract ALL tempo changes from the meta track and save in a list.
    # Also, delete any tempo changes found in the miditrack in our range.

    tempos = []
    tcmd = packBytes((0xff, 0x51, 0x03))  # midi TEMPO
    mt = gbl.mtrks[0].miditrk  # The meta track

    # 1st we copy existing tempos into a new list
    for t in mt:
        for ev in mt[t]:
            if ev[0:3] == tcmd:
                tempos.append((t, ev[3:]))  # save the 24 bit value

    # now we delete any tempos in the fermata range. This avoids broken loops
    for t, ev in tempos:
        if t >= moff and t <= mend:
            gbl.mtrks[0].delDup(t, tcmd)

    # find last tempo before fermata bock and last tempo in block
    tempos.sort()
    oldTempo = gbl.tempo
    newTempo = gbl.tempo

    for f, t in tempos:  # f==offsets, t==encoded tempos
        if f <= moff:
            oldTempo = 60000000 // byte3ToInt(t)
        if f >= moff and f <= mend:
            newTempo = 60000000 // byte3ToInt(t)

    gbl.mtrks[0].addTempo(moff, int(oldTempo / (adj / 100)))
    gbl.mtrks[0].addTempo(mend, newTempo)

    # Move selected events in the effected area to it's start/end.
    #   To start -> note on, program change
    #   to end   -> note off
    # Done only if the fermata comes after desired location ...
    # otherwise there aren't any events to zap!

    if offset < 0:
        for n, tr in gbl.mtrks.items():  # do each track
            if n <= 0:
                continue  # skip meta track

            trk = gbl.mtrks[n].miditrk
            startEvents = []
            endEvents = []

            for f in sorted(trk):  # all in this track (sort keeps orig order)
                if f > moff and f < mend:  # well, only in the fermata range
                    remain = []
                    for ev in trk[f]:  # all events in the offset
                        if not ev:  # skip empty events
                            continue
                        # Get event type. The [0:1] is needed to
                        # maintain this as a byte() for python3, otherwise
                        # py3 will think it's an int() and barfs
                        evtype = unpack('B', ev[0:1])[0] >> 4
                        if evtype == 0x9:  # note event
                            if ev[2] == 0:  # off to end
                                endEvents.append(ev)
                            else:
                                startEvents.append(ev)  # on to start
                            continue
                        if evtype == 0xb or evtype == 0xc:  # program/controller change
                            startEvents.append(ev)  # all to start (??)
                            continue
                        remain.append(ev)
                    trk[f] = remain  # remaining events for this offset

            if startEvents:
                if moff in trk:
                    trk[moff].extend(startEvents)
                else:
                    trk[moff] = startEvents

            if endEvents:
                if mend in trk:
                    trk[mend] = endEvents + trk[mend]
                else:
                    trk[mend] = endEvents

    if gbl.debug:
        print("Fermata: Beat %s, Duration %s, Change %s, Bar %s" %
              (offset, dur, adj, gbl.barNum + 1))
        if offset < 0:
            print("         NoteOn Events moved in tick range %s to %s" %
                  (moff + 1, mend - 1))
예제 #37
0
def fermata(ln):
    """ Apply a fermata timing to the specified beat. """

    if len(ln) != 3:
        error("Fermata: use 'offset' 'duration' 'adjustment'")

    offset = stof(ln[0], "Fermata: Expecting a value (not '%s') for offset" % ln[0])

    if offset < -gbl.QperBar or offset > gbl.QperBar:
        warning("Fermata: %s is a large beat offset" % offset)

    dur = stof(ln[1], "Fermata: Expecting a value (not '%s') for duration" % ln[1])

    if dur <= 0:
        error("Fermata: duration must be greater than 0")

    if dur > gbl.QperBar:
        warning("Fermata: %s is a large duration." % dur)

    adj = stof(ln[2], "Fermata: expecting a value (not '%s') for adjustment." % ln[2])

    if adj < 100:
        warning("Fermata: Adjustment less than 100 is shortening beat value.")

    if adj == 100:
        error("Fermata: using value of 100 makes no difference, must be an error.")

    moff = int(gbl.tickOffset + (gbl.BperQ * offset))

    if moff < 0:
        error("Fermata: Offset comes before track start.")

    if offset >= 0:
        warning("Fermata: Better results when placed after event (negative offset).")

    fermataDuration = int(gbl.BperQ * dur)  # Duration in ticks
    mend = moff + fermataDuration

    # This next section is needed to figure out the start tempo (which
    # is not always gbl.tempo!), the needed tempo at the end of the fermata
    # section (again, not always gbl.tempo!) and to delete any tempo changes
    # in the section. All this due to the effects of tempo changes over a
    # a set of bars.

    # Extract ALL tempo changes from the meta track and save in a list.
    # Also, delete any tempo changes found in the miditrack in our range.

    tempos = []
    tcmd = packBytes((0xff, 0x51, 0x03))   # midi TEMPO
    mt = gbl.mtrks[0].miditrk  # The meta track

    # 1st we copy existing tempos into a new list
    for t in mt:
        for ev in mt[t]:
            if ev[0:3] == tcmd:
                tempos.append((t, ev[3:]))  # save the 24 bit value

    # now we delete any tempos in the fermata range. This avoids broken loops
    for t, ev in tempos:
        if t >= moff and t <= mend:
            gbl.mtrks[0].delDup(t, tcmd)

    # find last tempo before fermata bock and last tempo in block
    tempos.sort()
    oldTempo = gbl.tempo
    newTempo = gbl.tempo

    for f, t in tempos:   # f==offsets, t==encoded tempos
        if f <= moff:
            oldTempo = 60000000 // byte3ToInt(t)
        if f >= moff and f <= mend:
            newTempo = 60000000 // byte3ToInt(t)

    gbl.mtrks[0].addTempo(moff, int(oldTempo / (adj / 100)))
    gbl.mtrks[0].addTempo(mend, newTempo)

    # Move selected events in the effected area to it's start/end.
    #   To start -> note on, program change
    #   to end   -> note off
    # Done only if the fermata comes after desired location ...
    # otherwise there aren't any events to zap!

    if offset < 0:
        for n, tr in gbl.mtrks.items():  # do each track
            if n <= 0:
                continue        # skip meta track

            trk = gbl.mtrks[n].miditrk
            startEvents = []
            endEvents = []

            for f in sorted(trk):          # all in this track (sort keeps orig order)
                if f > moff and f < mend:  # well, only in the fermata range
                    remain = []
                    for ev in trk[f]:      # all events in the offset
                        if not ev:         # skip empty events
                            continue
                        # Get event type. The [0:1] is needed to
                        # maintain this as a byte() for python3, otherwise
                        # py3 will think it's an int() and barfs
                        evtype = unpack('B', ev[0:1])[0] >> 4
                        if evtype == 0x9:         # note event
                            if ev[2] == 0:    # off to end
                                endEvents.append(ev)
                            else:
                                startEvents.append(ev)  # on to start
                            continue
                        if evtype == 0xb or evtype == 0xc:  # program/controller change
                            startEvents.append(ev)          # all to start (??)
                            continue
                        remain.append(ev)
                    trk[f] = remain        # remaining events for this offset

            if startEvents:
                if moff in trk:
                    trk[moff].extend(startEvents)
                else:
                    trk[moff] = startEvents

            if endEvents:
                if mend in trk:
                    trk[mend] = endEvents + trk[mend]
                else:
                    trk[mend] = endEvents

    if gbl.debug:
        print("Fermata: Beat %s, Duration %s, Change %s, Bar %s" % 
              (offset, dur, adj, gbl.barNum + 1))
        if offset < 0:
            print("         NoteOn Events moved in tick range %s to %s" 
                  % (moff + 1, mend - 1))
예제 #38
0
    def addKeySig(self, offset, n, mi):
        """ Set the midi key signature. """

        cmd = packBytes(0xff, 0x59)
        self.delDup(offset, cmd)
        self.addToTrack(offset, packBytes(cmd, (0x02, n, mi)))
예제 #39
0
    def addMarker(self, offset, msg):
        """ Create a midi MARKER event."""

        self.addToTrack(offset, packBytes((0xff, 0x06), intToVarNumber(len(msg)), msg))
예제 #40
0
    def addText(self, offset, msg):
        """ Create a midi TextEvent."""

        self.addToTrack(offset, packBytes((0xff, 0x01), intToVarNumber(len(msg)), msg))
예제 #41
0
    def addCuePoint(self, offset, msg):
        """ Create a MIDI cue pointr event. """

        self.addToTrack(offset, packBytes((0xff, 0x07), intToVarNumber(len(msg)), msg ))
예제 #42
0
    def addLyric(self, offset, msg):
        """ Create a midi lyric event. """

        self.addToTrack(offset, packBytes((0xff, 0x05), intToVarNumber(len(msg)), msg))
예제 #43
0
    def addText(self, offset, msg):
        """ Create a midi TextEvent."""

        self.addToTrack(offset, packBytes((0xff, 0x01), intToVarNumber(len(msg)), msg))
예제 #44
0
    def addPairToTrack(self, boffset, startRnd, endRnd, duration, note, v, unify ):
        """ Add a note on/off pair to a track.

            boffset      - offset into current bar
            startRnd, endRnd  - rand val start adjustment
            duration  - note len
            note      - midi value of note
            v      - midi velocity
            unify      - if set attempt to unify/compress on/offs

            This function tries its best to handle overlapping events.
            Easy to show effect with a table of note ON/OFF pairs. Both
            events are for the same note pitch.

            Offsets     |     200  |      300  |  320  |  420
            ---------|--------|--------|-------|--------
            Pair1     |     on      |       |  off  |
            Pair2     |      |      on   |       |  off

            The logic here will delete the OFF event at 320 and
            insert a new OFF at 300. Result is that when playing
            Pair1 will turn off at 300 followed by the same note
            in Pair2 beginning sounded right after. Why the on/off?
            Remember: Velocities may be different!

            However, if the unify flag is set we should end up with:

            Offsets     |     200  |      300  |  320  |  420
            ---------|--------|--------|-------|--------
            Pair1     |     on      |       |       |
            Pair2     |      |       |       |  off

        """

        # Start/end offsets

        onOffset = getOffset(boffset, startRnd, endRnd)

        # If the ON or OFF offset is <0 we change it to 0 for a couple of
        # reasons:
        #   1. It'll be converted anyway in 'addToTrack', but that doesn't
        #      get back to us here.
        #   2. If we don't that wrong offset will be reported in lastOffEvent.

        if onOffset < 0:
            onOffset = 0
        offOffset = onOffset + duration
        if offOffset < 0:
            offOffset = 0

        # ON/OFF events

        onEvent = packBytes(0x90 | self.channel, note, v)
        offEvent = packBytes(onEvent[:-1], 0)

        noOnFlag = False

        f = self.lastOffEvent[note] 

        if f is not None and f >= onOffset and f <= offOffset:
            # evlist is a delta-offset list. It should have a note off event
            # for the this note. Just in case, we do check; but it's probably
            # not necessary. The off event is deleted and the unify stuff is done
            if offEvent in self.miditrk[f]:
                self.miditrk[f].remove(offEvent)
                if not unify:
                    self.addToTrack(onOffset, offEvent, MIDI_NOTE)
                else:
                    noOnFlag = True

        if not noOnFlag:
            self.addToTrack(onOffset, onEvent, MIDI_NOTE)
        self.addToTrack(offOffset, offEvent, MIDI_NOTE)

        # Save the NOTE OFF time for the next loop.

        self.lastOffEvent[note] = offOffset
예제 #45
0
def insertNote(trk, ln):
    """ Insert specified (raw) MIDI notes into track. """

    if len(ln) != 4:
        error("Use: %s MidiNote: <offset> <note> <velocity> <duration>" %
              trk.name)

    acctable = keySig.accList  # keysig modifier, use for chord

    offset = getoffset(trk, ln[0])

    # Set a flag if this is a drum track.

    if trk.vtype == 'DRUM':
        isdrum = 1
    elif trk.vtype in ('MELODY', 'SOLO') and trk.drumType:
        isdrum = 1
    else:
        isdrum = 0

    notes = []
    for n in ln[1].split(','):
        if n[0] in '0123456789':
            n = stoi(n)
        else:
            if isdrum:
                if n == '*':
                    if trk.vtype in ('MELODY', 'SOLO'):
                        n = trk.drumTone
                    else:
                        n = trk.toneList[gbl.seqCount]
                else:
                    n = MMA.midiC.drumToValue(n)
                    if n < 0:
                        error("MidiNote: unknown drum tone '%s' in %s." %
                              (n, trk.name))
            else:
                n = note2val(trk, acctable, n)

        if n < 0 or n > 127:
            error("MidiNote: Notes must be in the range 0...127, not %s" % n)

        if trk.transpose and not isdrum:
            n += gbl.transpose
            while n < 0:
                n += 12
            while n > 127:
                n -= 12

        if trk.oadjust and not isdrum:
            n += (trk.oadjust * 12)
            while n < 0:
                n += 12
            while n > 127:
                n -= 12

        notes.append(n)

    velocity = stoi(ln[2])

    if velocity < 1 or velocity > 127:
        error("MidiNote: Note velocity must be in the range 1...127, not %s" %
              velocity)

    velocity *= trk.vadjust

    if velocity < 1:
        velocity = 1
    elif velocity > 127:
        velocity = 127

    velocity = int(velocity)  # trk.adjust can be a float

    if trk.tickdur:
        duration = stoi(ln[3])
    else:
        duration = MMA.notelen.getNoteLen(ln[3])
        if trk.articulate:
            duration = (duration * trk.artic[gbl.seqCount]) // 100
            if duration < 1:
                duration = 1

    channel = trk.channel
    track = gbl.mtrks[channel]

    for n in notes:
        onEvent = packBytes((0x90 | channel - 1, n, velocity))
        offEvent = packBytes(onEvent[:-1], 0)

        track.addToTrack(gbl.tickOffset + offset, onEvent)
        track.addToTrack(gbl.tickOffset + offset + duration, offEvent)

    if gbl.debug:
        print("MidiNote Note %s: inserted note %s at offset %s." %
              (trk.name, notes, offset))
예제 #46
0
    def addPairToTrack(self, boffset, startRnd, endRnd, duration, note, v, unify ):
        """ Add a note on/off pair to a track.

            boffset      - offset into current bar
            startRnd, endRnd  - rand val start adjustment
            duration  - note len
            note      - midi value of note
            v      - midi velocity
            unify      - if set attempt to unify/compress on/offs

            This function tries its best to handle overlapping events.
            Easy to show effect with a table of note ON/OFF pairs. Both
            events are for the same note pitch.

            Offsets     |     200  |      300  |  320  |  420
            ---------|--------|--------|-------|--------
            Pair1     |     on      |       |  off  |
            Pair2     |      |      on   |       |  off

            The logic here will delete the OFF event at 320 and
            insert a new OFF at 300. Result is that when playing
            Pair1 will turn off at 300 followed by the same note
            in Pair2 beginning sounded right after. Why the on/off?
            Remember: Velocities may be different!

            However, if the unify flag is set we should end up with:

            Offsets     |     200  |      300  |  320  |  420
            ---------|--------|--------|-------|--------
            Pair1     |     on      |       |       |
            Pair2     |      |       |       |  off

        """

        # Start/end offsets

        onOffset = getOffset(boffset, startRnd, endRnd)

        # If the ON or OFF offset is <0 we change it to 0 for a couple of
        # reasons:
        #   1. It'll be converted anyway in 'addToTrack', but that doesn't
        #      get back to us here.
        #   2. If we don't that wrong offset will be reported in lastOffEvent.

        if onOffset < 0:
            onOffset = 0
        offOffset = onOffset + duration
        if offOffset < 0:
            offOffset = 0

        # ON/OFF events

        onEvent = packBytes(0x90 | self.channel, note, v)
        offEvent = packBytes(onEvent[:-1], 0)

        noOnFlag = False

        f = self.lastOffEvent[note] 

        if f != None and f >= onOffset and f <= offOffset:
            # evlist is a delta-offset list. It should have a note off event
            # for the this note. Just in case, we do check; but it's probably
            # not necessary. The off event is deleted and the unify stuff is done
            if offEvent in self.miditrk[f]:
                self.miditrk[f].remove(offEvent)
                if not unify:
                    self.addToTrack(onOffset, offEvent, MIDI_NOTE)
                else:
                    noOnFlag = True

        if not noOnFlag:
            self.addToTrack(onOffset, onEvent, MIDI_NOTE)
        self.addToTrack(offOffset, offEvent, MIDI_NOTE)

        # Save the NOTE OFF time for the next loop.

        self.lastOffEvent[note] = offOffset
예제 #47
0
def insertNote(trk, ln):
    """ Insert specified (raw) MIDI notes into track. """

    if len(ln) != 4:
        error("Use: %s MidiNote: <offset> <note> <velocity> <duration>" % trk.name)

    acctable = keySig.accList  # keysig modifier, use for chord

    offset = getoffset(trk, ln[0])

    # Set a flag if this is a drum track.

    if trk.vtype == "DRUM":
        isdrum = 1
    elif trk.vtype in ("MELODY", "SOLO") and trk.drumType:
        isdrum = 1
    else:
        isdrum = 0

    notes = []
    for n in ln[1].split(","):
        if n[0] in "0123456789":
            n = stoi(n)
        else:
            if isdrum:
                if n == "*":
                    if trk.vtype in ("MELODY", "SOLO"):
                        n = trk.drumTone
                    else:
                        n = trk.toneList[gbl.seqCount]
                else:
                    n = MMA.midiC.drumToValue(n)
                    if n < 0:
                        error("MidiNote: unknown drum tone '%s' in %s." % (n, trk.name))
            else:
                n = note2val(trk, acctable, n)

        if n < 0 or n > 127:
            error("MidiNote: Notes must be in the range 0...127, not %s" % n)

        if trk.transpose and not isdrum:
            n += gbl.transpose
            while n < 0:
                n += 12
            while n > 127:
                n -= 12

        if trk.oadjust and not isdrum:
            n += trk.oadjust * 12
            while n < 0:
                n += 12
            while n > 127:
                n -= 12

        notes.append(n)

    velocity = stoi(ln[2])

    if velocity < 1 or velocity > 127:
        error("MidiNote: Note velocity must be in the range 1...127, not %s" % velocity)

    velocity *= trk.vadjust

    if velocity < 1:
        velocity = 1
    elif velocity > 127:
        velocity = 127

    velocity = int(velocity)  # trk.adjust can be a float

    if trk.tickdur:
        duration = stoi(ln[3])
    else:
        duration = MMA.notelen.getNoteLen(ln[3])
        if trk.articulate:
            duration = (duration * trk.artic[gbl.seqCount]) // 100
            if duration < 1:
                duration = 1

    channel = trk.channel
    track = gbl.mtrks[channel]

    for n in notes:
        onEvent = packBytes((0x90 | channel - 1, n, velocity))
        offEvent = packBytes(onEvent[:-1], 0)

        track.addToTrack(gbl.tickOffset + offset, onEvent)
        track.addToTrack(gbl.tickOffset + offset + duration, offEvent)

    if gbl.debug:
        print("MidiNote Note %s: inserted note %s at offset %s." % (trk.name, notes, offset))
예제 #48
0
    def addWheel(self, offset, v):
        """ Set lsb/msb for the modulation wheel. """

        self.addToTrack(offset, packBytes((0xe0 | self.channel), intTo14(v)))
예제 #49
0
    def addCuePoint(self, offset, msg):
        """ Create a MIDI cue pointr event. """

        self.addToTrack(offset, packBytes((0xff, 0x07), intToVarNumber(len(msg)), msg ))
예제 #50
0
    def addMarker(self, offset, msg):
        """ Create a midi MARKER event."""

        self.addToTrack(offset, packBytes((0xff, 0x06), intToVarNumber(len(msg)), msg))
예제 #51
0
    def addCtl(self, offset, l):
        """ Add arbitary control sequence to track."""

        self.addToTrack(offset, packBytes(0xb0 | self.channel, l))
예제 #52
0
    def addPan(self, offset, v):
        """ Set the lsb of the pan setting."""

        self.addToTrack(offset, packBytes((0xb0 | self.channel, 0x0a, v)))
예제 #53
0
파일: midiIn.py 프로젝트: rcook/mma
def midiinc(ln):
    """ Include a MIDI file into MMA generated files. """

    filename = ''
    doLyric = 0
    doText = 0
    channels = []
    transpose = None
    stripSilence = -1
    report = 0
    istart = 0          # istart/end are in ticks
    iend = 0xffffff     # but are set in options in Beats
    verbose = 0
    octAdjust = 0
    velAdjust = 100
    ignorePC = 1
    stretch = None

    notopt, ln = opt2pair(ln)

    if notopt:
        error("MidiInc: Expecting cmd=opt pairs, not '%s'." % ' '.join(notopt))

    for cmd, opt in ln:
        cmd = cmd.upper()

        if cmd == 'FILE':
            filename = MMA.file.fixfname(opt)

        elif cmd == 'VOLUME':
            velAdjust = stoi(opt)

        elif cmd == 'OCTAVE':
            octAdjust = stoi(opt)
            if octAdjust < -4 or octAdjust > 4:
                error("MidiInc: 'Octave' adjustment must be -4 to 4, not %s" % opt)
            octAdjust *= 12

        elif cmd == 'TRANSPOSE':
            transpose = stoi(opt)
            if transpose < -24 or transpose > 24:
                error("MidiInc: 'Transpose' must be -24 to 24, not %s" % opt)

        elif cmd == 'START':
            if opt[-1].upper() == 'M':   # measures
                istart = int(stof(opt[:-1]) * gbl.barLen)
            elif opt[-1].upper() == 'T':  # ticks
                istart = int(stof(opt[:-1]))
            else:  # must be digits, stof() catches errors
                istart = int((stof(opt)-1) * gbl.BperQ)

            if istart < 0:
                error("MidiInc: 'Start' must be > 0.")

        elif cmd == 'END':
            if opt[-1].upper() == 'M':
                iend = int((stof(opt[:-1])-1) * gbl.barLen)
            elif opt[-1].upper() == 'T':
                iend = int(stof(opt[:-1]))
            else:
                iend = int((stof(opt)-1) * gbl.BperQ)

            if iend < 0:
                error("MidiInc: 'End' must be > 0.")

        elif cmd == 'TEXT':
            opt = opt.upper()
            if opt in ("ON", '1'):
                doText = 1
            elif opt in ("OFF", '0'):
                doText = 0
            else:
                error("MidiInc: 'Text' expecting 'ON' or 'OFF'")

        elif cmd == 'LYRIC':
            opt = opt.upper()
            if opt in ("ON", '1'):
                doLyric = 1
            elif opt in ("OFF", '0'):
                doLyric = 0
            else:
                error("MidiInc: 'Lyric' expecting 'ON' or 'OFF'")

        elif cmd == "REPORT":
            opt = opt.upper()
            if opt in ("ON", '1'):
                report = 1
            elif opt in ("OFF", '0'):
                report = 0
            else:
                error("MidiInc: 'Report' expecting 'ON' or 'OFF'")

        elif cmd == "VERBOSE":
            opt = opt.upper()
            if opt in ("ON", '1'):
                verbose = 1
            elif opt in ("OFF", '0'):
                verbose = 0
            else:
                error("MidiInc: 'Verbose' expecting 'ON' or 'OFF'")

        elif cmd == "STRIPSILENCE":
            opt = opt.upper()
            if opt in ("OFF", '0'):
                stripSilence = 0
            elif opt == "ON":  # this is the default
                stripSilence = -1
            else:
                stripSilence = stoi(opt, "MIdiInc StripSilence= expecting "
                                    "'value', 'On' or 'Off', not %s" % opt)

        elif cmd == "IGNOREPC":
            opt = opt.upper()
            if opt in ("TRUE", "ON", "1"):   # default
                ignorePC = 1
            elif opt in ("FALSE", "OFF", "0"):  # use program change in imported
                ignorePC = 0
            else:
                error("MIdiInc: 'IncludePC' expecting 'True' or 'False', not %s" % opt)

        elif cmd == "STRETCH":
            v = stof(opt)
            if v < 1 or v > 500:
                error("MidiInc: 'Stretch' range of 1 to 500, not %s." % opt)
            stretch = v/100.

        # If none of the above matched a CMD we assume that it is
        # a trackname. Keep this as the last test!

        else:
            trackAlloc(cmd, 0)
            if not cmd in gbl.tnames:
                error("MidiInc: %s is not a valid MMA track" % cmd)

            opt = opt.split(',')
            riffmode = 0
            printriff = 0
            ch = None
            for o in opt:
                o = o.upper()
                if o == 'RIFF':
                    riffmode = 1
                elif o == 'SEQUENCE':
                    riffmode = 2
                elif o == 'PRINT':
                    printriff = 1
                    if not riffmode:
                        riffmode = 1
                else:
                    if ch is not None:
                        error("MidiInc: Only one channel assignment per track.")
                    ch = stoi(o)

            if ch < 1 or ch > 16:
                error("MidiInc: MIDI channel for import must be 1..16, not %s" % ch)

            channels.append((cmd, ch-1, riffmode, printriff))

    # If transpose was NOT set, use the global transpose value
    # Note special riff value as well. Need to double adjust since
    # the riff import will do its own adjustment.
    # this needs to be done BEFORE reading the midi file
    if transpose is None:
        transpose = gbl.transpose
        riffTranspose = -gbl.transpose
    else:
        riffTranspose = 0
    octAdjust += transpose    # this takes care of octave and transpose

    mf = MidiData()
    mf.octaveAdjust = octAdjust
    mf.velocityAdjust = velAdjust
    mf.ignorePC = ignorePC

    try:
        mf.readFile(filename)
    except RuntimeError as e:
        error("MidiInc: %s" % e)

    if mf.beatDivision != gbl.BperQ:
        warning("MIDI file '%s' tick/beat of %s differs from MMA's "
                "%s. Will try to compensate" % 
                (filename, mf.beatDivision, gbl.BperQ))
        mf.adjustBeats( gbl.BperQ / float(mf.beatDivision))

    if report or verbose:  # try to be helpful
        print("MIDI File %s successfully read." % filename)
        print("Total Text events: %s" % len(mf.textEvents))
        print("Total Lyric events: %s" % len(mf.lyricEvents))
        print('\n')

        for ch in sorted(mf.events.keys()):
            if not mf.events[ch]:
                continue

            if verbose and not report:   # in verbose mode only list info for tracks we're using
                doit = 0
                for z in channels:
                    if z[1] == ch:
                        doit = 1
                        break
                
                if not doit:
                    continue

            fnote = fevent = 0xffffff
            ncount = 0
            for ev in mf.events[ch]:
                delta = ev[0]
                if delta < fevent:
                    fevent = delta
                if ev[1] >> 4 == 0x9:
                    if delta < fnote:
                        fnote = delta
                    if ev[3]:
                        ncount += 1
            msg = ["Channel %2s: First event %-8s" % (ch+1, fevent)]
            if ncount:
                msg.append("First Note %-8s Total Notes %-4s" % (fnote, ncount))
            print(' '.join(msg))

        if report:
            print("\nNo data generated!")
            sys.exit(0)
        
    if not channels:
        if doLyric or doText:
            warning("MidiInc: no import channels specified, "
                    "only text or lyrics imported")
        else:
            error("MidiInc: A channel to import and a destination "
                  "track must be specified")

    if (istart >= iend) or (istart < 0) or (iend < 0):
        error("MidiInc: Range invalid, start=%s, end=%s" % (istart, iend))

    if gbl.debug:
        print("MidiInc: file=%s, Volume=%s, Octave=%s, Transpose=%s, Lyric=%s, " 
            "Text=%s, Range=%s..%s StripSilence=%s Verbose=%s" 
            % (filename, velAdjust, octAdjust, transpose, doLyric, doText,
               istart, iend, stripSilence, verbose))
        msg = []
        for t, ch, riffmode, riffprint in channels:
            o = ''
            if riffmode == 1:
                o = ',riff'
            elif riffmode == 2:
                o = ',sequence'
            elif printriff:
                o += ',print'
            msg.append("MidiInc: Channel %s-->%s%s" % (ch+1, t, o))
        print(' '.join(msg))

    if stretch:
        if verbose:
            print("Applying stretch to all events. Deltas will be multiplied by %s" % stretch)

        for tr in mf.events:
            for e in mf.events[tr]:
                e[0] = int(e[0] * stretch)   # e[0] is the offset

        for e in mf.textEvents:
            e[0] = int(e[0] * stretch)

        for e in mf.lyricEvents:
            e[0] = int(e[0] * stretch)

    # Midi file parsed, add selected events to mma data

    if stripSilence == 0:
        if verbose:
            print("Firstnote offset was %s. Being reset to start of file by StripSilence=Off." 
                % mf.firstNote)
        mf.firstNote = 0

    if verbose:
        print("First note offset: %s" % mf.firstNote)

    if doText:
        inst = 0
        disc = 0
        if verbose:
            print("Scanning %s textevents." % len(mf.textEvents))
        for tm, tx in mf.textEvents:
            delta = tm-mf.firstNote
            if delta >= istart and delta <= iend:
                gbl.mtrks[0].addText(gbl.tickOffset + delta, tx)
                inst += 1
            else:
                disc += 1

        if gbl.debug:
            print("MidiInc text events: %s inserted, %s out of range." % (inst, disc))

    if doLyric:
        inst = 0
        disc = 0
        if verbose:
            print("Scanning %s LyricEvents." % len(mf.lyricEvents))
        for tm, tx in mf.lyricEvents:
            delta = tm-mf.firstNote
            if delta >= istart and delta <= iend:
                gbl.mtrks[0].addLyric(gbl.tickOffset + delta, tx)
                inst += 1
            else:
                disc += 1
        if gbl.debug:
            print("MidiInc lyric events: %s inserted, %s out of range." % (inst, disc))

    for n, c, riffmode, printriff in channels:
        if not len(mf.events[c]):
            warning("No data to assign from imported channel %s to track %s" % (c+1, n))

    inst = 0
    disc = 0

    for tr, ch, riffmode, printriff in channels:
        onNotes = []
        if gbl.tnames[tr].disable:   # skip if disabled track

            if verbose:
                print("Skipping import of channel %s since track %s is disabled." 
                    % (ch, tr))
            continue

        t = gbl.tnames[tr]
        if not t.channel:
            t.setChannel()

        if riffmode:
            riff = []
            if t.vtype not in ('MELODY', 'SOLO'):
                error("MidiInc: Riff only works on Melody/Solo tracks, not '%s'." % t.name)

        t.clearPending()
        if t.voice[0] != t.ssvoice:
            gbl.mtrks[t.channel].addProgChange(gbl.tickOffset, t.voice[0], t.ssvoice)

        channel = t.channel
        track = gbl.mtrks[channel]

        if verbose:
                print("Parsing imported file. Channel=%s Track=%s MIDI Channel=%s" 
                    % (ch, tr, channel))
                if len(mf.events[ch]):
                    print(" Total events: %s; Event range: %s %s; Start/End Range: %s %s" 
                    % (len(mf.events[ch]), mf.events[ch][0][0], 
                       mf.events[ch][-1][0], istart, iend))
                else:
                    print("No events in Channel %s" % ch)

        
        # If we're processing midi voice changes (ignorePC=off) and there
        # are events BEFORE the first note, w eneed to insert
        # them before the notes. We put them all at the current midi offset.
        if ignorePC==0:
            for ev in mf.events[ch]:
                if ev[0] > mf.firstNote:
                    break
                if ev[1] >> 4 == 0xc:
                    track.addToTrack(gbl.tickOffset,
                                     packBytes(ev[1] | channel-1, *ev[2:]))
                    inst += 1
                    disc -= 1

        for ev in mf.events[ch]:
            delta = ev[0]-mf.firstNote
            
            if delta >= istart and delta <= iend:
                if riffmode:
                    offset = delta-istart
                    x = ev[1] >> 4
                    if x != 0x09 and x != 0x08:  # skip non note events
                        continue
                    pitch = ev[2]
                    velocity = ev[3]
                    if x == 0x8:
                        velocity = 0
                    riff.append([offset, pitch, velocity])

                else:
                    offset = gbl.tickOffset + (delta-istart)
                    # add note on/off, key pressure, etc.
                    track.addToTrack(offset, packBytes(ev[1] | channel-1, *ev[2:]))

                    # Track on/off events to avoid stuck midi notes.
                    x = ev[1] >> 4
                    if x == 0x09 and ev[3] and ev[2] not in onNotes:
                        onNotes.append(ev[2])  # append this pitch to onNotes
                    if x == 0x09 and not ev[3] or x == 0x08 and ev[2] in onNotes:
                        onNotes.remove(ev[2])  # remove this as being ON
                        
                inst += 1
            else:
                disc += 1

        if onNotes:
            for x in onNotes:
                track.addToTrack(offset, packBytes(0x90 | channel-1, [x,0]))
            warning("MidiINC: Stuck MIDI note(s) '%s' turned off in %s." % 
                    (', '.join([str(x) for x in onNotes]), tr))

        if riffmode:
            evlist = createRiff(riff, tr, riffTranspose)

            if riffmode == 2:
                txt = []
            for a in sorted(evlist):
                if printriff and riffmode == 1:
                    print("%s Riff %s" % (tr, evlist[a]))
                elif riffmode == 2:   # sequence mode, create sequence line and push into input
                    txt.append("{%s}" % evlist[a])
                else:   # riffmode==1, printriff=0 - just add to the riff stack
                    gbl.tnames[tr].setRiff(evlist[a])

            if riffmode == 2 and txt:
                if printriff:
                    print("%s Sequence %s" % (tr, ' '.join(txt)))
                else:
                    MMA.sequence.trackSequence(tr, txt)

    if gbl.debug:
            print("MidiInc events: %s inserted, %s out of range." % (inst, disc))
예제 #54
0
    def addCtl(self, offset, l):
        """ Add arbitary control sequence to track."""

        self.addToTrack(offset, packBytes(0xb0 | self.channel, l))
예제 #55
0
    def addKeySig(self, offset, n, mi):
        """ Set the midi key signature. """

        cmd = packBytes(0xff, 0x59)
        self.delDup(offset, cmd)
        self.addToTrack(offset, packBytes(cmd, (0x02, n, mi)))
예제 #56
0
    def writeMidiTrack(self, out):
        """ Create/write the MIDI track.

            We convert timing offsets to midi-deltas.
        """

        tr = self.miditrk

        """ If the -1 command line option is set we need to add a terminate
            to the end of each track. This is done to make looping
            software like seq24 happy. We do this by truncating all
            data in the file past the current tick pointer and inserting
            an all-notes-off at that position.
        """

        if gbl.endsync and self.channel >= 0:
            eof = gbl.tickOffset
            for offset in tr.keys():
                if offset > eof:
                    del tr[offset]
            self.addToTrack(eof, packBytes((0xb0 | self.channel, 0x7b, 0)))

        """ To every MIDI track we generate we add (if the -0 flag
            was set) an on/off beep at offset 0. This makes for
            easier sync in multi-tracks.
        """

        if gbl.synctick and self.channel >= 0:
            t, v = syncTone
            self.addToTrack(0, packBytes((0x90 | self.channel, t, v)))
            self.addToTrack(1, packBytes((0x90 | self.channel, t, 0)))

        if gbl.debug:
            ttl = 0
            lg = 1
            for t in tr:
                a = len(tr[t])
                if a > lg:
                    lg = a
                ttl += a
            if self.channel == -1:
                nm = "META"
            else:
                nm = self.trackname
            print( "<%s> Unique ts: %s; Ttl events %s; Average ev/ts %.2f" %
                (nm, len(tr), ttl, float(ttl)/len(tr)))

        last = 0

        # Convert all events to MIDI deltas and store in
        # the track array/list

        tdata = []        # empty track container
        lastSts = None    # Running status tracker

        for a in sorted(tr.keys()):
            delta = a-last

            if not tr[a]:
                continue  # this skips the delta offset update!

            for d in tr[a]:
                """ Running status check. For each packet compare
                    the first byte with the first byte of the previous
                    packet. If it is can be converted to running status
                    we strip out byte 0. Note that valid running status
                    byte are 0x80..0xef. 0xfx are system messages
                    and are not suitable for running status.
                """

                if len(d) > 1:
                    if d[0] == lastSts:
                        d = d[1:]
                    else:
                        lastSts = d[0]
                        if lastSts < 0x80 or lastSts > 0xef or not gbl.runningStatus:
                            lastSts = None

                tdata.extend([intToVarNumber(delta), d])
                delta = 0
            last = a

        # Add an EOF to the track (included in total track size)

        tdata.append(intToVarNumber(0))
        tdata.append(packBytes((0xff, 0x2f, 0x00)))

        tdata = bytearray(b'').join(tdata)
        totsize = len(tdata)

        out.write(b"MTrk")
        out.write(intToLong(totsize))
        out.write(tdata)
예제 #57
0
    def addWheel(self, offset, v):
        """ Set lsb/msb for the modulation wheel. """

        self.addToTrack(offset, packBytes((0xe0 | self.channel), intTo14(v)))
예제 #58
0
    def addPan(self, offset, v):
        """ Set the lsb of the pan setting."""

        self.addToTrack(offset, packBytes((0xb0 | self.channel, 0x0a, v)))