示例#1
0
文件: chunks.py 项目: oli-hall/pymidi
def process_track_chunk(data):
    log.info("Parsing Track Chunk...")

    events = []
    running_status = None
    while len(data) > 0:
        data, delta = variable_length_field(data)
        prefix = data[:8]
        if prefix == F0_SYSEX_EVENT_PREFIX or prefix == F7_SYSEX_EVENT_PREFIX:
            data, event = process_sysex_event(prefix, data[8:])
            events.append((delta, event))
        elif prefix == META_EVENT_PREFIX:
            data, event = process_meta_event(data[8:])
            events.append((delta, event))
        else:
            data, event, running_status = process_midi_event(data, running_status)
            events.append((delta, event))

    if events[-1][1]["sub_type"] != "End of Track":
        raise Exception("End of Track event missing")

    return {
        "type": TRACK,
        "events": events
    }
示例#2
0
    def test_variable_length_decoding_of_0_returns_correct_result(self):
        input = BitArray("0x00")

        remainder, extracted = variable_length_field(input)

        self.assertEqual(extracted, 0)
        self.assertEqual(remainder, BitArray())
示例#3
0
    def test_variable_length_decoding_of_268435455_with_excess_data_returns_correct_result_and_correct_remainder(
            self):
        input = BitArray("0xFFFFFF7F12304FABC")

        remainder, extracted = variable_length_field(input)

        self.assertEqual(extracted, 268435455)
        self.assertEqual(remainder, BitArray("0x12304FABC"))
示例#4
0
文件: events.py 项目: oli-hall/pymidi
def process_sysex_event(prefix, data):
    data, length = variable_length_field(data)
    if prefix == F0_SYSEX_EVENT_PREFIX:
        subtype = "F0"
    elif prefix == F7_SYSEX_EVENT_PREFIX:
        subtype = "F7"
    else:
        raise Exception(
            "Tried to process Sysex event but invalid prefix {} found.\nExiting..."
            .format(prefix))

    event = {"type": "SYSEX", "sub_type": subtype, "data": data[:8 * length]}

    return data[:8 * length], event
示例#5
0
文件: events.py 项目: oli-hall/pymidi
def process_meta_event(data):
    type = data[:8].hex
    data, length = variable_length_field(data[8:])

    event_data = data[:length * 8]
    remainder = data[length * 8:]

    event = {"type": META, "sub_type": "Unknown", "data": event_data}

    # TODO make sure that these comparisons are case correct/case-insensitive
    if type == "00":
        # This is an optional event, which must occur only at the start of a track, before any non-zero delta-time.
        #
        # For Format 2 MIDI files, this is used to identify each track.If omitted, the sequences are numbered
        # sequentially in the order the tracks appear.
        #
        # For Format 1 files, this event should occur on the first track only.
        event["sub_type"] = "Sequence Number"
        event["sequence_number"] = event_data[:length * 8].bytes
    elif type == "01":
        event["sub_type"] = "Text Event"
        event["text"] = event_data[:length * 8].bytes
    elif type == "02":
        event["sub_type"] = "Copyright Notice"
        event["text"] = event_data[:length * 8].bytes
    elif type == "03":
        event["sub_type"] = "Sequence/Track Name"
        event["text"] = event_data[:length * 8].bytes
    elif type == "04":
        event["sub_type"] = "Instrument Name"
        event["text"] = event_data[:length * 8].bytes
    elif type == "05":
        event["sub_type"] = "Lyric"
        event["text"] = event_data[:length * 8].bytes
    elif type == "06":
        event["sub_type"] = "Marker"
        event["text"] = event_data[:length * 8].bytes
    elif type == "07":
        event["sub_type"] = "Cue Point"
        event["text"] = event_data[:length * 8].bytes
    elif type == "20":
        # MIDI Channel Prefix
        # Associate all following meta-events and sysex-events with the specified MIDI channel, until the next
        # <midi_event> (which must contain MIDI channel information).
        if length != 1:
            log.error("Channel Prefix Event has the wrong length!\nExiting...")
            exit(1)

        event["sub_type"] = "MIDI Channel Prefix"
        event["channel"] = event_data[:8].hex
    elif type == "21":
        # MIDI Prefix Port
        if length != 1:
            print("MIDI Prefix Port event has the wrong length!\nExiting...")
            exit(1)

        event["sub_type"] = "MIDI Prefix Port"
        event["device"] = event_data[:8]
    elif type == "2f":
        print("End of Track")
        # This event is not optional.
        # It is used to give the track a clearly defined length, which is essential information if the track is looped
        # or concatenated with another track
        if length:
            print("End of Track event should not have any length!\nExiting...")
            exit(1)

        event["sub_type"] = "End of Track"
    elif type == "51":
        # Set Tempo
        # This sets the tempo in microseconds per quarter note. This means a change in the unit-length of a delta-time
        # tick. (note 1)
        # If not specified, the default tempo is 120 beats/minute, which is equivalent to tttttt=500000
        if length != 3:
            print("Set Tempo event has the wrong length!\nExiting...")
            exit(1)

        event["sub_type"] = "Set Tempo"
        event["new_tempo"] = event_data[:8 * 3]
    elif type == "54":
        # SMTPE Offset
        # This (optional) event specifies the SMTPE time at which the track is to start.
        # This event must occur before any non-zero delta-times, and before any MIDI events.
        # In a format 1 MIDI file, this event must be on the first track (the tempo map).
        if length != 5:
            print("SMTPE Offset event has the wrong length!\nExiting...")
            exit(1)

        event["sub_type"] = "SMTPE Offset"
        event["hours"] = event_data[:8]
        event["minutes"] = event_data[8:16]
        event["seconds"] = event_data[16:24]
        event["frames"] = event_data[24:32]
        event["fractional_frames"] = event_data[32:40]
    elif type == "58":
        # Time Signature
        if length != 4:
            print("Time Signature event has the wrong length!\nExiting...")
            exit(1)

        event["sub_type"] = "Time Signature"
        event["numerator"] = event_data[:8].int
        event["denominator"] = event_data[8:16].int
        # MIDI clocks per metronome click
        event["clocks_per_tick"] = event_data[16:24].int
        # number of 1/32 notes per 24 MIDI clocks (8 is standard)
        event["32nd_notes_per_24_clocks"] = event_data[24:32].int
    elif type == "59":
        # Key Signature
        # Key Signature, expressed as the number of sharps or flats, and a major/minor flag.
        # 0 represents a key of C, negative numbers represent 'flats', while positive numbers represent 'sharps'.
        if length != 2:
            print("Key Signature event has the wrong length!\nExiting...")
            exit(1)

        event["sub_type"] = "Key Signature"
        event["sharps_flats"] = event_data[:8].int
        event["major_minor"] = event_data[8:16].int
    elif type == "7F":
        # This is the MIDI-file equivalent of the System Exclusive Message.
        # A manufacturer may incorporate sequencer-specific directives into a MIDI file using this event.
        # consists of <id> + <data>, length is length of both of these fields combined
        # <id> is either one or three bytes, and is the Manufacturer ID
        # This value is the same as is used for MIDI System Exclusive messages
        # <data> 8-bit binary data

        event["sub_type"] = "Sequencer-Specific Meta-event"
        event["data"] = event_data[:length * 8]
    else:
        log.warning("Unrecognised Meta event: {}".format(type))

    return remainder, event