def musical_extract_midi(path: str): """Generates (BPM: float, duration since last: float, [pitch: int])""" f = MidiFile(path) assert f.type != 2, "asynchronous midi files are not supported yet" messages = [] for track in f.tracks: messages.extend(_to_abstime(track)) assert messages, "failed to find messages. Erroneous file?" messages.sort(key=attrgetter('time')) tempo = DEFAULT_TEMPO last = tick = 0 output = set() for msg in messages: if msg.type == 'note_on' and msg.velocity > 0: if msg.time == tick: # Current hit output.add(msg.note) elif output: # New hit yield tempo2bpm(tempo), (tick - last) / f.ticks_per_beat, list(output) output = {msg.note} last, tick = tick, msg.time else: # Non-0 Beginning last = tick = msg.time output.add(msg.note) elif msg.type == 'set_tempo': tempo = msg.tempo # Last hit if output: yield tempo2bpm(tempo), (tick - last) / f.ticks_per_beat, list(output)
def save_musical_file(path: str, out: str = None): if out is None: out = path f = MidiFile() ap = f.add_track('Main').append last_bpm = tempo2bpm(DEFAULT_TEMPO) ap(MetaMessage("set_tempo", tempo=DEFAULT_TEMPO)) notes_on = [] i = 0 for bpm, duration, notes in musical_extract_midi(path): i += 1 if notes_on: ap(Message(type='note_off', note=notes_on[0], time=int(duration * f.ticks_per_beat * 0.95))) for n in notes_on[1:]: ap(Message(type='note_off', note=n, time=0)) notes_on = notes ap(Message(type='note_on', note=notes_on[0], time=int(duration * f.ticks_per_beat * 0.05))) for n in notes_on: ap(Message(type='note_on', note=n, time=0)) if bpm != last_bpm: last_bpm = bpm ap(MetaMessage("set_tempo", tempo=bpm2tempo(bpm))) if notes_on: # Last note; just make it 1 beat long ap(Message(type='note_off', note=notes_on[0], time=f.ticks_per_beat)) for n in notes_on[1:]: ap(Message(type='note_off', note=n, time=0)) f.save(out) print(f"{i} hits wrote to {out}")
def _get_tempo(msgs): """Search through msgs for a meta message with type 'set_tempo' and return the tempo of the last one found.""" msgs = list(filter(lambda m: m.type == 'set_tempo', msgs)) if msgs: return mido.tempo2bpm(msgs[-1].tempo) return 120 # by the standard, default to 120
def open(self, midi_filename=None, midi_portname=None): """ open midifile and(or) port """ if midi_filename: clock = PrClock() clock.set_timer(1) # load midi file self.midi_file = mido.MidiFile(midi_filename) print("open midifile : ", clock.elapsed(1)) # set iter_by_tick self.iter_by_tick = True # load iteration of it self.midi_iter = iter(self.midi_file) # set relative variables clock.set_timer(1) self.tempo = self.get_tempo() print('get tempo : ', clock.elapsed(1)) self.bpm = mido.tempo2bpm(self.tempo) self.ppqn = self.get_ppqn() self.spt = self.get_spt() self.cur_evt_time = .0 self.cur_evt_tick = .0 self.next_evt_time = .0 self.next_evt_tick = .0 self.playing = False self.msg = None clock.set_timer(1) self.length = self.get_length(force=True) self.start_time = .0 print('get length : ', clock.elapsed(1)) if midi_portname: self.port = mido.open_output(midi_portname)
def feed(self, message): """ Feed a mido message into the object, updating the internal state. Returns wrapped message """ if (self.wrap_notes and message.type in ChannelState.NOTE_MESSAGE_TYPES): return self._channels[message.channel].feed(message) elif message.type == "control_change": if message.control == Control.LOCAL.value: # LOCAL message. value = SwitchBool.from_b(message.value) self.local(value) return WrappedMessage( message, Control.LOCAL, value, bonus_strings(('n', message.channel, 0x0, '1X'))) else: return self._channels[message.channel].feed(message) elif message.type in ChannelState.CONTROL_MESSAGE_TYPES: return self._channels[message.channel].feed(message) # System Exclusive / Sequencer Specific elif message.type in {"sysex", "sequencer_specific"}: return self._handle_sysex_seqspec(message) # User Song Polytouch Special Handling elif self.user_song and message.type in ChannelState.US_MESSAGE_TYPES: return self._channels[message.channel].feed(message) # Meta Messages. elif message.type == "set_tempo": # We use the bpm instead of the midi-tempo as value. return WrappedMessage( message, MessageType.TEMPO, mido.tempo2bpm(message.tempo)) return None
def change_midi_file_tempo(input_file, output_file, ratio=1.0): """ Change the tempo in a midi file """ infile = MidiFile(input_file) with MidiFile() as outfile: outfile.type = infile.type outfile.ticks_per_beat = infile.ticks_per_beat for msg_idx, track in enumerate(infile.tracks): out_track = MidiTrack() outfile.tracks.append(out_track) for message in track: if message.type == 'set_tempo': bpm = tempo2bpm(message.tempo) new_bpm = ratio * bpm message.tempo = bpm2tempo(new_bpm) out_track.append(message) outfile.save(output_file)
def reload(self): """ reload midi file """ # stop if it's needed self.stop() # reload if self.midi_file and self.port: # set iter_by_tick self.iter_by_tick = True # reset port self.port.reset() # reset iteration of midi file del self.midi_iter self.midi_iter = iter(self.midi_file) # set relative variables self.tempo = self.get_tempo() self.bpm = mido.tempo2bpm(self.tempo) self.ppqn = self.get_ppqn() self.spt = self.get_spt() self.cur_evt_time = .0 self.cur_evt_tick = .0 self.next_evt_time = .0 self.next_evt_tick = .0 self.playing = False self.msg = None self.length = self.get_length(force=True) self.clock.set_timer() self.start_time = .0 # helper reset self.helper.reset()
def testFindTempo(): tempo = findTempo(mid) print "how long 240 ticks",ticksToSeconds(240,mid) print "tempo (microseconds per beat?)",tempo print "bpm ",mido.tempo2bpm(tempo) print "ticks per beat", mid.ticks_per_beat print "total seconds",mid.length
def getTempoString(tempos, ticksPerBeat): string = '' for msg in tempos: string += '\t{} = B {}000\n'.format( int(msg.time * Track.RESOLUTION // ticksPerBeat), mido.tempo2bpm(msg.tempo)) return string
def play_file(filename, print_messages): midi_file = MidiFile(filename) print('Playing {}.'.format(midi_file.filename)) length = midi_file.length print('Song length: {} minutes, {} seconds.'.format( int(length / 60), int(length % 60))) print('Tracks:') for i, track in enumerate(midi_file.tracks): print(' {:2d}: {!r}'.format(i, track.name.strip())) for message in midi_file.play(meta_messages=True): if print_messages: sys.stdout.write(repr(message) + '\n') sys.stdout.flush() if isinstance(message, Message): sys.stdout.write("") sys.stdout.flush() #output.send(message) elif message.type == 'set_tempo': print('Tempo changed to {:.1f} BPM.'.format( tempo2bpm(message.tempo))) print()
def midi2pianoroll(mid): midd = mido.MidiFile(mid) midp = pretty_midi.PrettyMIDI(mid) # basic information tempo = get_tempo(midd) bpm = mido.tempo2bpm(tempo) sixteen_t = (1 / (bpm / 60)) / 4 midi_length = int(np.ceil(midd.length / sixteen_t)) # notes = np.zeros((midi_length, 13)) for instrument in midp.instruments: print(instrument) if instrument.is_drum == False: pr = instrument.get_piano_roll(1 / sixteen_t) cnt = 0 for i, ppr in enumerate(pr): # i: notes # j: time stamp for j, nt in enumerate(ppr): if nt > 0: notes[j][i % 12 + 1] += 1 for i, note in enumerate(notes): if np.count_nonzero(note) == 0: notes[i][0] = 1 return notes, sixteen_t
def midi2score(song, subdivision): mid = mido.MidiFile(song) tempo = 0 sec_per_tick = 0 length = mid.length time = 0 # set initial score len for msg in mid: if (msg.is_meta): if (msg.type == 'set_tempo'): tempo = msg.tempo else: if (msg.type == "note_on"): bpm = mido.tempo2bpm(tempo) sec_per_tick = 60 / bpm / subdivision break score = np.zeros((int(length / sec_per_tick) + 1, 90)) for msg in mid: time += msg.time pos = int(np.round(time / sec_per_tick)) if (pos + 1 > score.shape[0]): score = np.append(score, np.zeros((pos - score.shape[0] + 1, 90)), axis=0) if (msg.is_meta): if (msg.type == 'set_tempo'): tempo = mido.tempo2bpm(msg.tempo) sec_per_tick = 60 / tempo / subdivision elif (msg.type == 'note_on'): if (msg.velocity == 0): p = msg.note - 21 score[pos:, p] = 0 else: p = msg.note - 21 score[pos:, p] = 1 elif (msg.type == 'note_off'): p = msg.note - 21 score[pos:, p] = 0 return score
def getBpmSet(path: str) -> Set[float]: bpm = set() f = MidiFile(path) for track in f.tracks: for msg in track: if msg.type == 'set_tempo': bpm.add(tempo2bpm(msg.tempo)) return bpm
def secNum(path): midd = MidiFile(path) midp = pretty_midi.PrettyMIDI(path) tempo = get_tempo(midd) bpm = tempo2bpm(tempo) measure_t = (1 / (bpm / 60)) * 4 midi_sec = int(np.ceil(midd.length / measure_t)) return midi_sec
def get_bpm_from_track(track): for msg in track: if msg.type == 'set_tempo': try: return mido.tempo2bpm(msg.tempo) except: print(traceback.format_exc()) continue return None
def check_if_pattern_is_ok(self, pattern, tempo): """ Check if a pattern has right tempo and no change in tempo during time """ for track in pattern.tracks: for evt in track: if evt.type == 'set_tempo': if mido.tempo2bpm(evt.tempo) != tempo: return False elif evt.type == 'note_on': if not self.lowerbound < evt.note < self.upperbound: return False return True
def wait_for_messages(self): while 1: socket_list = [self.sock] # Get the list sockets which are readable read_sockets, write_sockets, error_sockets = select.select( socket_list, [], [], 0) for sock in read_sockets: # incoming message from remote server if sock == self.sock: data = sock.recv(4096) if not data: print('\nDisconnected from chat server') sys.exit() else: # print data #sys.stdout.write(">" + str(binascii.unhexlify(data.decode())) + "<") #print("Received1: " + data.decode()) messages = str(data.decode()).split("m") if len(messages) >= 2: messages.pop(0) for message in messages: message_header = message[:1] message_body = message[2:] print(message) if message_header == "n": notes_hex = message_body notes_bin = bytearray.fromhex(notes_hex) notes_mido = mido.parse_all(notes_bin) print(notes_mido) if self.midi_ports != None: for note in notes_mido: if isinstance(note, Message): self.midi_ports_out.send(note) elif note.type == 'set_tempo': print( 'Tempo changed to {:.1f} BPM.' .format( tempo2bpm(note.tempo))) if self.midi_ports != None: msg = '' bytes = bytearray() for message in mido.ports.multi_receive(self.midi_ports, block=False): print('Sending: {}'.format(message)) bytes = bytes + message.bin() if bytes != b'': message = "mn=" + bytes.hex() #hex = "mn=803c00" #hex = "mn=803c00mn=903740" print("Sent: " + message) self.sock.send(message.encode())
def __init__(self, composer, path): self.path = path self.notes = IntervalSet() self.tempos = IntervalSet() self.time_sigs = IntervalSet() self.keys = IntervalSet() self.composer = Composers.getIndex(composer) self.qnls = 0 # Default values for metadata self.tempos.addPosEdge('tp', 0, 120) self.time_sigs.addPosEdge('ts', 0, TimeSignatures.getIndex((4, 4))) self.keys.addPosEdge('key', 0, KeySignatures.getIndex('unknown')) mid = mido.MidiFile(path) for track in mid.tracks: time = 0 # In qnls for msg in track: time += float(msg.time) / mid.ticks_per_beat if msg.type == 'note_on': self.notes.addPosEdge(msg.note, time, msg.note) elif msg.type == 'note_off': self.notes.addNegEdge(msg.note, time) elif msg.type == 'set_tempo': self.tempos.addNegEdge('tp', time) self.tempos.addPosEdge('tp', time, mido.tempo2bpm(msg.tempo)) elif msg.type == 'time_signature': self.time_sigs.addNegEdge('ts', time) self.time_sigs.addPosEdge('ts', time, TimeSignatures.getIndex((msg.numerator, msg.denominator))) elif msg.type == 'key_signature': self.keys.addNegEdge('key', time) self.keys.addPosEdge('key', time, KeySignatures.getIndex(msg.key)) self.qnls = max(self.qnls, time) self.notes.finalize(self.qnls) self.tempos.finalize(self.qnls) self.time_sigs.finalize(self.qnls) self.keys.finalize(self.qnls) self.normalize_notes() self.examples = self.getTrainingExamples() print 'Processed %s (%d notes, %d keys, %d times, %d tempos, %d examples)' % ( self.path, len(self.notes.intervals), len(self.keys.intervals), len(self.time_sigs.intervals), len(self.tempos.intervals), len(self.examples) )
def findTempo(file): # returns tempo of song count = 0 for (i, msg) in enumerate(file): if isTempo(msg): tempo = msg.tempo count += 1 break if not count == 0: return int(mido.tempo2bpm(tempo)) else: return 120
def __init__(self, filename, track=0, resolution=4, bpm=None): self.filename = filename self.file = mido.MidiFile(filename) self.track = self.file.tracks[track] print(self.track.name) self.res = resolution self.bpm = bpm self.tpb = self.file.ticks_per_beat self.total_ticks = 0 for msg in self.track: if msg.type == 'set_tempo': self.bpm = mido.tempo2bpm(msg.tempo) if msg.type == 'note_on' or msg.type == 'note_off': self.total_ticks += msg.time # For now we only work with note_on and note_off messages total_notes = 127 self.min_note = total_notes self.max_note = 0 for msg in self.track: if msg.type == 'note_on' or msg.type == 'note_off': if msg.note < self.min_note: self.min_note = msg.note if msg.note > self.max_note: self.max_note = msg.note self.note_range = (self.max_note - self.min_note) + 1 self.roll = [[0 for j in range(self.note_range)] for i in range(self.total_ticks)] self.beat_roll = [[ 0 for j in range(self.note_range) ] for i in range(ceil(self.total_ticks * self.res / self.tpb))] current_tick = 0 last_tick_on = [0 for i in range(self.note_range)] for msg in self.track: if msg.type == 'note_on' and msg.velocity != 0: current_tick += msg.time last_tick_on[msg.note - self.min_note] = current_tick elif msg.type == 'note_off' or (msg.type == 'note_on' and msg.velocity == 0): current_tick += msg.time for tick in range(last_tick_on[msg.note - self.min_note], current_tick): self.roll[tick][msg.note - self.min_note] = 1 self.beat_roll[floor(tick * self.res / self.tpb)][msg.note - self.min_note] = 1
def formImg(tempo, tpb, notes): ar = np.zeros((6, 4, 1)) y = None temp = None for i, n in enumerate(notes): if i < 6 and n is not None: temp = [[n.note * 100], [tempo / tpb * n.length], [n.velocity * 10], [mido.tempo2bpm(tempo) * 10]] ar[i, :, :] = temp else: y = n #print ar return [ar, y]
def get_piece_info(mfile): whole_note = mfile.ticks_per_beat * 4 time_sign = None tempo = None for msg in mfile.tracks[0]: if msg.is_meta: if msg.type == 'time_signature': time_sign = (msg.numerator, msg.denominator) elif msg.type == 'set_tempo': tempo = round(mido.tempo2bpm(msg.tempo)) if time_sign == None: time_sign=(4, 4) if tempo == None: tempo = 500000 return time_sign, tempo, whole_note
def scan_rock_midi_files(): midi_table = get_midi_table() genre = 'rock' dir = 'E:/free_midi_library/raw_midi/' for midi in midi_table.find({'Genre': genre}): path = dir + genre + '/' + midi['md5'] + '.mid' mido_object = mido.MidiFile(path) for i, track in enumerate(mido_object.tracks): for msg in track: if msg.is_meta and msg.type == 'set_tempo': print(mido.tempo2bpm(msg.tempo)) print('\n')
def _parse_midi_messages(self): elapsed_ticks=0 for mdt in range(len(self.midifile.tracks)): for m,message in enumerate(self.midifile.tracks[mdt]): message_type=message.type elapsed_ticks+=message.time #print(message, elapsed_ticks) #print(f'message {m} , time= {message.time} elapsed_ticks={elapsed_ticks}') if message_type=="set_tempo": self.tempo=message.tempo self.bpm=round(mido.tempo2bpm(self.tempo)) elif message_type=="time_signature": self.rythm_numerator=message.numerator self.rythm_denominator=message.denominator self.clocks_per_click=message.clocks_per_click self.notated_32nd_notes_per_beat=message.notated_32nd_notes_per_beat elif message_type=="key_signature": self.key=message.key elif message_type=="end_of_track": self.ending_tick=message.time self.total_ticks=elapsed_ticks elif message_type=="note_on": channel=message.channel pitch=message.note velocity=message.velocity time=message.time #duration=self.midifile.tracks[0][m+1].time #print(f'duration={self.midifile.tracks[0][m+1].time}') timestamp=elapsed_ticks timestamp_sec=self.convert_tic2sec(timestamp) #print(pitch, timestamp_sec) if velocity!=0: temp_counter=1 duration=0 for msg in self.midifile.tracks[mdt][m+1:]: duration+=msg.time if msg.type=="note_on" and msg.note==pitch and msg.velocity==0: new_note=Note(channel,pitch,velocity,timestamp,timestamp_sec,duration,self.convert_tic2sec(duration)) self.notes.append(new_note) break else: #print(f"Unknown message {message}") pass
def read(filename): song = Song() song.segs.append(Seg()) mid = None if '.mid' not in filename: mid = MidiFile(filename + '.mid') else: mid = MidiFile(filename) song.tpb = mid.ticks_per_beat for i, track in enumerate(mid.tracks): if i != 1: for m in mid: #print m if m.type == 'set_tempo': song.tempo = m.tempo else: activeNotes = list() activeNotesData = dict() time = 0 tempNote = None for m in mid: #print m if m.type == 'set_tempo': song.tempo = m.tempo song.segs[0].notes.append(Note(999, 0, m.tempo, time)) #print m.tempo #print mid.length time = time + m.time * song.tpb * mido.tempo2bpm( song.tempo) / 60 #print time if m.type == 'note_on': if m.note in activeNotes and activeNotesData[ m.note] != None: tempNote = activeNotesData[m.note] song.segs[0].notes.append( Note(m.note, time - tempNote[1], tempNote[0].velocity, tempNote[1])) activeNotes.append(m.note) activeNotesData[m.note] = (m, time) elif m.type == 'note_off': if m.note in activeNotes and activeNotesData[ m.note] != None: tempNote = activeNotesData[m.note] song.segs[0].notes.append( Note(m.note, time - tempNote[1], tempNote[0].velocity, tempNote[1])) #print time - tempNote[1] activeNotes.remove(m.note) activeNotesData[m.note] = None #song.tempo = int(song.tempo * 1) return song
def _load_tempo_changes(self, midi_data, track_idx=0): tempo_changes = [TempoChange(DEFAUL_BPM, 0)] for event in midi_data.tracks[track_idx]: if event.type == 'set_tempo': bpm = mido.tempo2bpm(event.tempo) tick = event.time if tick == 0: tempo_changes = [TempoChange(bpm, 0)] else: last_bpm = tempo_changes[-1].tempo if bpm != last_bpm: tempo_changes.append(TempoChange(bpm, tick)) return tempo_changes
def get_tempo(cls, path): if not path: return None midi_file = urllib.urlopen(path) tempo = 40 meta_message = MyMetaMessage(midi_file.read()) midi_file.close() for message in meta_message.get_all_meta_message(): if message.type == 'set_tempo': tempo = int(tempo2bpm(message.tempo)) break return tempo
def _parse_notes(midi_file: mido.MidiFile) -> List[_MidiNote]: ticks_per_beat: int = midi_file.ticks_per_beat tempo_bpm: float = MidiParser._DEFAULT_TEMPO_BPM held_notes: List[_MidiNote] = [] completed_notes: List[_MidiNote] = [] current_tick: int = 0 current_time: float = 0.0 current_bar: float = 1.0 current_bar_factor: int = 1 for msg, track in MidiParser.__merge_tracks(midi_file.tracks): current_tick += msg.time current_time += mido.tick2second(msg.time, ticks_per_beat, mido.bpm2tempo(tempo_bpm)) current_bar += msg.time / ticks_per_beat * current_bar_factor track_name: str = track.name if track else "" if msg.type == 'set_tempo': tempo_bpm = mido.tempo2bpm(msg.tempo) elif msg.type == 'time_signature': current_bar_factor = msg.denominator / (4 * msg.numerator) elif msg.type == 'note_on' or msg.type == 'note_off': # if the note is already held, complete the note regardless of whether the input is a note_on or # note_off message. This is to avoid duplicate notes in a slice if re-triggered before its note_off # See test case "keithjarrett_kolnconcert_Right.mid" bars 311 to 330 for an example of this. completed: List[_MidiNote] = [ note for note in held_notes if note.matches(msg.note, msg.channel, track_name) ] for note in completed: note.end_tick = current_tick note.end_time = current_time completed_notes.extend(completed) held_notes = [ note for note in held_notes if not note.matches(msg.note, msg.channel, track_name) ] if msg.type == 'note_on' and msg.velocity > 0: held_notes.append( _MidiNote(msg.note, msg.velocity, msg.channel, current_tick, current_time, tempo_bpm, current_bar, track_name)) # elif msg.type == 'note_off' or (msg.type == 'note_on' and msg.velocity == 0): for note in held_notes: # If note ons still exist at end of midi file, generate note offs for all of them note.end_tick = current_tick note.end_time = current_time completed_notes.append(note) return completed_notes
def build_tempo_map_from_midi(druid): midi_filepath = Path(f"midi/{druid}.mid") midi = MidiFile(midi_filepath) tempo_map = [] current_tick = 0 for event in midi.tracks[0]: current_tick += event.time if event.type == "set_tempo": tempo_map.append((current_tick, tempo2bpm(event.tempo))) return tempo_map
def _load_track0(self, midi_data): self.key_signatures = [] self.time_signatures = [] self.tempo_changes = [] for msg in midi_data.tracks[0]: if msg.type == 'key_signature': self.key_signatures.append(KeySignature(msg.key, msg.time)) elif msg.type == 'time_signature': self.time_signatures.append( TimeSignature(msg.numerator, msg.denominator, msg.time)) elif msg.type == 'set_tempo': self.tempo_changes.append( TempoChange(tempo2bpm(msg.tempo), msg.time))
def musical_extract_midi_with_inst(path: str, offset=True): """Generates (BPM: float, duration since last: float, [(pitch: int, instrument_index: int)])""" if offset: offset = 1 else: offset = 0 f = MidiFile(path) assert f.type != 2, "asynchronous midi files are not supported yet" messages = [] for track in f.tracks: messages.extend(_to_abstime(track)) assert messages, "failed to find messages. Erroneous file?" messages.sort(key=attrgetter('time')) tempo = DEFAULT_TEMPO last = tick = 0 output = set() insts = dict() for msg in messages: if msg.type == 'note_on' and msg.velocity > 0: if msg.time == tick: # Current hit output.add((msg.note, insts.get(msg.channel, offset))) elif output: # New hit yield (tempo2bpm(tempo), (tick - last) / f.ticks_per_beat, list(output)) output = {(msg.note, insts.get(msg.channel, offset))} last, tick = tick, msg.time else: # Non-0 Beginning last = tick = msg.time output.add((msg.note, insts.get(msg.channel, offset))) elif msg.type == 'set_tempo': tempo = msg.tempo elif msg.type == 'program_change': insts[msg.channel] = msg.program + offset # Last hit if output: yield tempo2bpm(tempo), (tick - last) / f.ticks_per_beat, list(output)