Пример #1
0
def str_msg_to_dict(str_message):
    message = mido.parse_string(str_message)
    dict_message = {'type': message.type}

    for name in MIDI_MESSAGES.get(message.type, ()):
        dict_message[name] = getattr(message, name)

    return dict_message
Пример #2
0
def str_msg_to_dict(str_message):
    message = mido.parse_string(str_message)
    dict_msg = {'type': message.type}

    for name in message._spec.arguments:
        dict_msg[name] = getattr(message, name)

    return dict_msg
Пример #3
0
def readin_strings(infile, comment=None):
    """
    Read in string-encoded messages separated by line from a text mode file
    object. Similar to mido.parse_string_stream, except doesn't deal with the
    exceptions.
    Can also ignore simple comments, delimited by the comment parameter
    (uses str.partition internally).
    Generator, yields messages lazily.
    """
    if comment is not None:
        for line in infile:
            msgl, _, _ = line.partition(comment)
            msg = msgl.strip()
            if msg:
                yield mido.parse_string(msg)
    else:
        for line in infile:
            yield mido.parse_string(line)
Пример #4
0
 def __iter__(self):
     for line in sys.stdin:
         line = line.split('#', 1)[0].strip()
         if line.startswith('<message ') and line.endswith('>'):
             yield parse_string(line[9:-1])
         elif line.startswith('<meta message ') and line.endswith('>'):
             yield parse_meta_message(line[14:-1])
         elif line == '':
             pass
         else:
             raise ValueError("unrecognized line: " + repr(line))
Пример #5
0
def create_tiles(filepath):

    global tile_limit
    # check to see if tile limit has been set
    if tile_limit is not None:
        single_tile_limit = tile_limit

    # store file as MidiFile obj
    mid = MidiFile(filepath)
    msglist = []

    # append all messages beyond header track to list
    for track in mid.tracks[1:]:
        for msg in track:
            msglist.append(msg)

    # stores accumulated time of each message index
    acc_time_index = {}
    acc_time = 0
    for i in range(0, len(msglist)):
        time = int(re.search(r'time=(\d+)', str(msglist[i])).group(1))
        acc_time += time
        acc_time_index.update({i: acc_time})

    # check each offset of the message list
    for offset in range(0, len(msglist)):
        # check across a range of wavelengths
        for wavelength in range(lower_wavelength, upper_wavelength):
            # validate if the tile repeats
            isvalid = True
            for i in range(wavelength):
                if tile_limit is not None:
                    if single_tile_limit == 0:
                        break
                # ensure values do not exceed list index range
                if offset+i >= len(msglist) or offset+wavelength+i >= len(msglist):
                    isvalid = False
                    break
                if (msglist[offset+i] != msglist[offset+wavelength+i]):
                    isvalid = False
                    break
                # create tile from matching wavelength
                if (isvalid):
                    tile = []
                    for i in range(wavelength):
                        tile.append(msglist[offset+i])

                    # dummy MidiFile created to determine absolute time of tile for metadata
                    temp_mid = mido.MidiFile(type=1)
                    temp_mid.ticks_per_beat = mid.ticks_per_beat
                    track = mido.MidiTrack()
                    if find_program_change(filepath) is not None:
                        prog_change = mido.parse_string(re.sub(
                            r'time=(\d+)', r'time=0', str(find_program_change(filepath))))
                        track.append(prog_change)
                    for line in tile:
                        track.append(line)
                    temp_mid.tracks.append(track)

                    # save tile metadata to json formatted string
                    tick_time = acc_time_index[offset]
                    current_time = mido.tick2second(
                        tick_time, mid.ticks_per_beat, find_tempo(filepath))

                    tile_dict = {
                        'file': filepath,
                        'offset': offset,
                        'wavelength': wavelength,
                        'start_time_seconds': ('%.2f' % current_time),
                        'total_length_seconds': ('%.2f' % temp_mid.length)
                    }
                    meta_dict = json.dumps(tile_dict)

                    # MidiFile to be created as tile
                    new_mid = mido.MidiFile(type=1)
                    new_mid.ticks_per_beat = mid.ticks_per_beat

                    # header track containing same info as original file
                    header_track = mido.MidiTrack()
                    for msg in mid.tracks[0]:
                        if msg.type != 'end_of_track':
                            header_track.append(msg)
                    header_track.append(MetaMessage(
                        'text', text=str(tile_dict), time=0))
                    new_mid.tracks.append(header_track)

                    # music track containing the notes
                    music_track = mido.MidiTrack()
                    # add program change message
                    if find_program_change(filepath) is not None:
                        prog_change = mido.parse_string(re.sub(
                            r'time=(\d+)', r'time=0', str(find_program_change(filepath))))
                        music_track.append(prog_change)
                    # add notes from tile list
                    for line in tile:
                        music_track.append(line)
                    new_mid.tracks.append(music_track)

                    try:
                        # save to new file if tile within valid time range
                        if new_mid.length > 0 and new_mid.length <= maximum_time:
                            file_name = os.path.basename(filepath)
                            instrument = int(
                                re.search(r'program=(\d+)', str(find_program_change(filepath))).group(1))
                            tile_dir = '/tiles/' + \
                                instruments[instrument]+'/'
                            Path(
                                output_dir+tile_dir).mkdir(parents=True, exist_ok=True)

                            new_mid.save(output_dir+tile_dir+'%s_%s_%d_%d.mid' %
                                         (file_name[:-4], instruments[instrument], offset, wavelength))

                            # print info to screen for development
                            """
                            print('\nFile name: %s' % filepath)
                            for i, track in enumerate(new_mid.tracks):
                                print('Track number: %d' % (i+1))
                                for msg in track:
                                    print(msg)
                                print(
                                    '\n____________________________________________________________________________________________________________________________________\n')
                            """

                            try:
                                # save json file
                                json_dir = '/tile_metadata/' + \
                                    instruments[instrument]+'/'
                                Path(
                                    output_dir+json_dir).mkdir(parents=True, exist_ok=True)
                                f = open(output_dir+json_dir+'%s_%s_%d_%d.json' % (
                                    file_name[:-4], instruments[instrument], offset, wavelength), 'w')
                                f.write(meta_dict)
                            except:
                                print('JSON object not created for file: %s\n' %
                                      filepath)
                                for msg in new_mid.tracks[0]:
                                    print(msg)
                                print(
                                    '\n____________________________________________________________________________________________________________________________________\n____________________________________________________________________________________________________________________________________\n\n')

                            # dacrement tile limit for next loop
                            if tile_limit is not None:
                                single_tile_limit -= 1

                    except:
                        print('Error with tile creation for file: %s\nAttempted tile length %d' % (
                            filepath, new_mid.length))
                        for msg in new_mid.tracks[0]:
                            print(msg)
                        print(
                            '\n____________________________________________________________________________________________________________________________________\n____________________________________________________________________________________________________________________________________\n\n')
Пример #6
0
def create_words(filepath):

    global word_limit
    # check to see if word limit has been set
    if word_limit is not None:
        single_word_limit = word_limit

    # store file as MidiFile obj
    mid = MidiFile(filepath)
    note_on_list = []

    # new list of relevant messages
    for track in mid.tracks:
        if has_note_on(track):
            for msg in track:
                if msg.type == 'note_on' or msg.type == 'note_off':
                    note_on_list.append(str(msg))

    # to store accumulated time of message index
    acc_time = 0
    acc_time_seconds = 0

    # dictionary with each possible note stored,
    # to see what notes are on/off at any given offset
    note_dict = {}
    for i in range(0, 128):
        note_dict.update({i: {'on': False}})

    # temporary list to store annotated messages
    note_on_new = []

    for i in range(0, len(note_on_list)):
        # capture note number, velocity & time, note number in dictionary
        note = int(re.search(r'note=(\d+)', note_on_list[i]).group(1))
        velocity = int(re.search(r'velocity=(\d+)', note_on_list[i]).group(1))
        time = int(re.search(r'time=(\d+)', note_on_list[i]).group(1))
        note_info = note_dict[note]
        try:
            # find time delta of next message in index
            next_note_time = int(
                re.search(r'time=(\d+)', note_on_list[i + 1]).group(1))
        except:
            pass
        if i > 0:
            # find time delta of previous message in index
            previous_note = int(
                re.search(r'note=(\d+)', note_on_list[i - 1]).group(1))
        else:
            previous_note = note

        # increment accumulated time at offset, find time of note at offset and next offset
        acc_time += time
        note_time_seconds = mido.tick2second(time, mid.ticks_per_beat,
                                             find_tempo(filepath))
        next_note_time_seconds = mido.tick2second(next_note_time,
                                                  mid.ticks_per_beat,
                                                  find_tempo(filepath))
        acc_time_seconds = mido.tick2second(acc_time, mid.ticks_per_beat,
                                            find_tempo(filepath))

        # update dictionary with note status
        # if on, write to note_on_new with '-1'
        if 'note_on' in note_on_list[i] and velocity > 0:
            note_info['on'] = True
            note_on_new.append(note_on_list[i] + ' - 1')

        # if off, find out if there will be silence
        elif 'note_off' in note_on_list[i] or velocity == 0:
            note_info['on'] = False
            notes_on = []

            # find out how many notes are on at this offset
            for j in note_dict:
                if note_dict[j]['on'] == True:
                    notes_on.append(note_dict[j])

            if len(notes_on) == 0:
                # find length of silence if all notes off
                silence_start = acc_time_seconds - note_time_seconds
                next_note_start = acc_time_seconds + next_note_time_seconds
                silence_length = next_note_start - silence_start
                # append '-0' to new list if silence above minimum threshold
                if silence_length > minimum_silence:
                    note_on_new.append(note_on_list[i] + ' - 0')
                else:
                    note_on_new.append(note_on_list[i] + ' - 1')
            else:
                note_on_new.append(note_on_list[i] + ' - 1')

    # store accumulated time of each message index
    acc_time_index = {}
    acc_time = 0
    for i in range(0, len(note_on_list)):
        time = int(re.search(r'time=(\d+)', str(note_on_list[i])).group(1))
        acc_time += time
        acc_time_index.update({i: acc_time})

    for i in range(0, len(note_on_new)):
        # check if word limit is exceeded here
        if word_limit is not None and single_word_limit == 0:
            break

        previous_msg = note_on_new[i - 1]
        if int(previous_msg[-1]) == 0:
            # note ended with silence
            silence_time = int(
                re.search(r'time=(\d+)', note_on_new[i]).group(1))
            silence_time_sec = mido.tick2second(silence_time,
                                                mid.ticks_per_beat,
                                                find_tempo(filepath))
            if silence_time_sec > minimum_silence:
                # create word if silence above threshold
                word = []
                # set first note_on time attribute to 0 to trim any start silence
                count = 0
                for j in range(i, len(note_on_new)):
                    if count == 0:
                        word_lines = re.sub(r'time=(\d+)', r'time=0',
                                            note_on_new[j])
                        count += 1
                    else:
                        word_lines = note_on_new[j]

                    # remaining messages appended with '-1' or '-0' removed
                    if int(word_lines[-1]) == 1:
                        word.append(word_lines[:-4])
                    if int(word_lines[-1]) == 0:
                        word.append(word_lines[:-4])
                        break

                # dummy mid created to determine absolute time of word for metadata
                temp_mid = mido.MidiFile(type=1)
                temp_mid.ticks_per_beat = mid.ticks_per_beat
                track = mido.MidiTrack()
                if find_program_change(filepath) is not None:
                    prog_change = mido.parse_string(
                        re.sub(r'time=(\d+)', r'time=0',
                               str(find_program_change(file))))
                    track.append(prog_change)
                for line in word:
                    track.append(mido.parse_string(line))
                temp_mid.tracks.append(track)

                # save word metadata to json formatted string
                tick_time = acc_time_index[i]
                current_time = mido.tick2second(tick_time, mid.ticks_per_beat,
                                                find_tempo(filepath))

                word_dict = {
                    'file': filepath,
                    'offset': i,
                    'wavelength': len(word),
                    'start_time_seconds': ('%.2f' % current_time),
                    'total_length_seconds': ('%.2f' % temp_mid.length)
                }
                meta_dict = json.dumps(word_dict)

                # MidiFile to be created as tile
                new_mid = mido.MidiFile(type=1)
                new_mid.ticks_per_beat = mid.ticks_per_beat

                # header track containing same info as original file
                header_track = mido.MidiTrack()
                for msg in mid.tracks[0]:
                    header_track.append(msg)
                header_track.append(
                    MetaMessage('text', text=str(word_dict), time=0))
                new_mid.tracks.append(header_track)

                # music track containing the notes
                music_track = mido.MidiTrack()
                # add program change message
                if find_program_change(filepath) is not None:
                    prog_change = mido.parse_string(
                        re.sub(r'time=(\d+)', r'time=0',
                               str(find_program_change(filepath))))
                    music_track.append(prog_change)
                # add notes from word list
                for line in word:
                    music_track.append(mido.parse_string(line))
                new_mid.tracks.append(music_track)

                try:
                    # save to new file if word within valid time range
                    if new_mid.length > 0 and new_mid.length <= maximum_time:
                        file_name = os.path.basename(filepath)
                        instrument = int(
                            re.search(
                                r'program=(\d+)',
                                str(find_program_change(filepath))).group(1))
                        word_dir = '/words/' + instruments[instrument] + '/'
                        Path(output_dir + word_dir).mkdir(parents=True,
                                                          exist_ok=True)

                        new_mid.save(output_dir + word_dir +
                                     '%s_%s_%d_%d.mid' %
                                     (file_name[:-4], instruments[instrument],
                                      i, len(word)))

                        # print info to screen for development
                        """
                        print('\nFile name: %s' % filepath)
                        for i, track in enumerate(new_mid.tracks):
                            print('Track number: %d' % (i+1))
                            for msg in track:
                                print(msg)
                            print(
                                '\n____________________________________________________________________________________________________________________________________\n')
                        """

                        try:
                            # save json file
                            json_dir = '/word_metadata/' + \
                                instruments[instrument]+'/'
                            Path(output_dir + json_dir).mkdir(parents=True,
                                                              exist_ok=True)
                            f = open(
                                output_dir + json_dir + '%s_%s_%d_%d.json' %
                                (file_name[:-4], instruments[instrument], i,
                                 len(word)), 'w')
                            f.write(meta_dict)
                        except:
                            print('JSON object not created for file: %s\n' %
                                  filepath)
                            for msg in new_mid.tracks[0]:
                                print(msg)
                            print(
                                '\n____________________________________________________________________________________________________________________________________\n____________________________________________________________________________________________________________________________________\n\n'
                            )

                        # dacrement word limit for next loop
                        if word_limit is not None:
                            single_word_limit -= 1

                except:
                    print(
                        'Error with word creation for file: %s\nAttempted tile length %d'
                        % (filepath, new_mid.length))
                    for msg in new_mid.tracks[0]:
                        print(msg)
                    print(
                        '\n____________________________________________________________________________________________________________________________________\n____________________________________________________________________________________________________________________________________\n\n'
                    )
Пример #7
0
 def send_from_str(self, str_message):
     self.send(mido.parse_string(str_message))
Пример #8
0
def clean(filepath):

    # store file as MidiFile obj, create new obj to store output
    mid = MidiFile(filepath)
    ticksperbeat = mid.ticks_per_beat
    newmid = MidiFile(type=1)
    newmid.ticks_per_beat = ticksperbeat

    # in the event that an instrument-isolated file contains multiple tracks of the same instrument,
    # this will ensure that when the start time is trimmed, the timing is all kept correct
    start_times = []
    for track in mid.tracks:
        for msg in track:
            # finds first note_on message time, i.e. starting time
            # for all tracks in file
            if msg.type == 'note_on':
                start_time = msg.time
                start_times.append(start_time)
                break
    start_times.sort()
    if start_times:
        first_start_time = start_times[0]
    # if no start time, file has no note on messages and is invalid
    else:
        pass

    # iterate through tracks in file
    for i, track in enumerate(mid.tracks):
        # new track for cleaned output
        newtrack = mido.MidiTrack()
        # new list to store messages ready for processing
        msglist = [msg for msg in track]

        # each processed file will have one of each: tempo, time signature,
        # key signature and program change messages
        count = 0
        tempocount = 0
        timesigcount = 0
        keysigcount = 0
        progchangecount = 0
        # list of accepted messages to help filter any unwanted ones
        goodmessages = [
            'note_on', 'note_off', 'program_change', 'set_tempo',
            'time_signature', 'key_signature'
        ]

        # iterate through each message and determine if it will be kept or discarded
        for msg in msglist:
            if msg.type == 'program_change':
                # finds first program change message to store instrument number
                instrument = msg.program
                progchangecount += 1
            # ensures no unwanted messages are appended
            if str(msg.type) not in goodmessages:
                continue
            if msg.type == 'set_tempo':
                # appends one tempo message at time delta of 0 to new track
                if tempocount < 1 and msg.time == 0:
                    newtrack.append(
                        MetaMessage('set_tempo',
                                    tempo=msg.tempo,
                                    time=msg.time))
                    tempocount += 1
                else:
                    continue
            if msg.type == 'time_signature':
                # appends one time signature message at time delta of 0 to new track
                if timesigcount < 1 and msg.time == 0:
                    newtrack.append(
                        MetaMessage('time_signature',
                                    numerator=msg.numerator,
                                    denominator=msg.denominator,
                                    time=msg.time))
                    timesigcount += 1
                else:
                    continue
            if msg.type == 'key_signature':
                # appends one key signature message at time delta of 0 to new track
                if keysigcount < 1 and msg.time == 0:
                    newtrack.append(
                        MetaMessage('key_signature',
                                    key=msg.key,
                                    time=msg.time))
                    keysigcount += 1
                else:
                    continue
            # ensures first note on message has time delta of zero,
            # adjusts other potential tracks to equivalent time difference
            if count < 1:
                if msg.type == 'note_on':
                    start_time = msg.time
                    if start_time == first_start_time:
                        trimsilence = re.sub(r'time=(\d+)', r'time=0',
                                             str(msg))
                    else:
                        time_diff = 'time=%d' % (start_time - first_start_time)
                        trimsilence = re.sub(r'time=(\d+)', time_diff,
                                             str(msg))

                    newtrack.append(mido.parse_string(trimsilence))
                    count += 1
                    continue
            # append all other messages to new track
            if not msg.type == 'set_tempo' and not msg.type == 'time_signature' and not msg.type == 'key_signature':
                newtrack.append(msg)
        # append header track to new MidiFile obj
        if i == 0:
            newmid.tracks.append(newtrack)
            continue
        # append all other valid tracks to new MidiFile obj
        if progchangecount > 0 and i > 0:
            newmid.tracks.append(newtrack)

    # create new directory to store output
    new_dir = output_dir + '/cleaned/' + instruments[instrument] + '/'
    Path(new_dir).mkdir(parents=True, exist_ok=True)
    file_name = os.path.basename(filepath)

    # validates and saves new file
    if is_valid(newmid):
        newmid.save(new_dir + file_name)
    else:
        print('Invalid file not saved: %s' % filepath)