def play_a_note(midi_out: MidiOut, instrument: Instrument): note_on = midi_cmd(0x9, instrument.ch) note_off = midi_cmd(0x8, instrument.ch) midi_out.send_message([note_on, 60, 80]) time.sleep(0.5) midi_out.send_message([note_off, 60, 0]) time.sleep(0.1)
def silence(port: MidiOut, *, stop: bool = True, channels: Iterable[int] = ALL_CHANNELS) -> None: if stop: port.send_message([STOP]) for channel in channels: port.send_message([CONTROL_CHANGE | channel, ALL_NOTES_OFF, 0])
def send_config_to_device(config: MPK_MINI_MK2, preset: int, midi_out: MidiOut) -> None: config[0].preset = preset data = MPK_MINI_MK2.build(config) assert data[0] == 0xF0 and data[-1] == 0xF7 midi_out.send_message(data) data_hex = str(binascii.hexlify(bytearray(data)), 'utf-8') logging.debug(f'- SENT {len(data)} BYTES. SYSEX:\n{data_hex}')
def move_knob(midi_out: MidiOut, knob_message: list): for i in range(knob_message[2], -1, -1): knob_message[2] = i midi_out.send_message(knob_message) time.sleep(0.01) print('Up --> Down...DONE') for i in range(0, 128): knob_message[2] = i midi_out.send_message(knob_message) time.sleep(0.01) print('Down --> Up...DONE')
async def play(outport: rtmidi.MidiOut, inptable: MidiTable, noloops=1): """An asynchronous implementation of a table player. Multiple miditable objects and ports can be asynchronously targeted for concurrent output with proper synchronization.""" miditable = MidiTable(inptable) tempo = bpm2tempo(120) # default tempo for i in range(noloops): for msg in miditable: if msg['del_t'] > 0: delta = tick2second(msg['del_t'], tempo) await asyncio.sleep(delta) if msg['mtype'] == Mtypes.TEMPO: tempo = metaclasses.TempoSet.decoder(msg['data']) elif msg['mtype'] in CHAN_MTYPES: outport.send_message(msg['data'])
cursor_heap = [] tracks = {} cur_time = 0 _track_id = 0 for track in score.tracks: notes_sets_of_sylls = [s.notes for s in track.syllables] notes = [] for nset in notes_sets_of_sylls: notes += nset if len(notes) == 0: continue tracks[_track_id] = notes output.send_message([0xC0 + _track_id, 0]) #track.instrument]) heappush(cursor_heap, (tracks[_track_id][0].time_in_score, (0, _track_id))) if type(tracks[_track_id][0]) != Rest: output.send_message( [0x90 + _track_id, tracks[_track_id][0].midi_note, 0x40]) _track_id += 1 while len(cursor_heap) > 0: duration, nearest = heappop(cursor_heap) i, ti = nearest print("Track: %d, CurTime: %5d, Duration: %4d, Note: %d" % (ti, cur_time, duration, tracks[ti][i].midi_note)) if duration != 0:
class Player: def __init__(self, queue=Queue(), running=Value('i', False), tempo=Value('i', default_tempo), deadline=Value('f', 0)): self.midiout = MidiOut() self.midi_for_file = MidiFile() self.last_chord = empty_chord self.queue_out = queue self.running = running self.tempo = tempo self.deadline = deadline self.start_peak = Value('f', 0) self.start_chord = 0 def play_peak(self, number=default_peak_number, velocity=default_peak_velocity): note_on = Message('note_on', note=number, velocity=velocity, channel=default_ultrasound_channel).bytes() self.midiout.send_message(note_on) sleep(default_peak_time) note_off = Message('note_off', note=number, velocity=min_velocity, channel=default_ultrasound_channel).bytes() self.midiout.send_message(note_off) def play_chord_same_time(self): chord = self.queue_out.get() # print("player get", chord, "vel", chord.velocity, "queue", self.queue_out.qsize(), "time", time.monotonic()) if chord.velocity > 127: chord.velocity = 127 if chord.duration == 0: return for note in chord.notes: if note.number > 127: print("an incorrect note in player") return if self.last_chord != empty_chord: for note in self.last_chord.notes: note_off = Message('note_off', note=note.number, velocity=min_velocity, channel=default_channel).bytes() self.midiout.send_message(note_off) for note in chord.notes: note_on = Message('note_on', note=note.number, velocity=chord.velocity, channel=default_channel).bytes() self.midiout.send_message(note_on) self.last_chord = chord sleep(len_in_s(chord.duration, self.tempo.value)) if self.last_chord == chord: for note in chord.notes: note_off = Message('note_off', note=note.number, velocity=min_velocity, channel=default_channel).bytes() self.midiout.send_message(note_off) def play_chord_arpeggio(self, track=np.array([])): chord = self.queue_out.get() print("player get", chord, "vel", chord.velocity, "queue", self.queue_out.qsize(), "time", time.monotonic()) if chord.velocity > 127: chord.velocity = 127 if chord.duration == 0: return for note in chord.notes: if note.number > 127: print("an incorrect note in player") return chord.notes = sorted(chord.notes) if len(chord.notes) == 3: chord.notes.append(Note(chord.notes[0].number + 12)) if track == np.array([]): notes_numbers = np.arange(len(chord.notes)) notes_durations = np.array( [int(128 / len(chord.notes)) for i in range(len(chord.notes))]) track = np.column_stack((notes_numbers, notes_durations)) notes_sum_durations = np.cumsum(track.transpose(), axis=1)[1] if self.last_note_number is not None: note_off = Message('note_off', note=self.last_note_number, velocity=min_velocity, channel=default_channel).bytes() self.midiout.send_message(note_off) self.start_chord = time.monotonic() pair = 0 note_number = track[pair][0] note_on = Message('note_on', note=chord.notes[note_number].number, velocity=chord.velocity, channel=default_channel).bytes() self.midiout.send_message(note_on) while (pair < len(track) - 1): # TODO if time.monotonic() > self.start_chord + max( (self.deadline.value - self.start_chord) * notes_sum_durations[pair] / notes_sum_durations[-1], len_in_s(notes_sum_durations[pair], self.tempo.value)): note_off = Message('note_off', note=chord.notes[note_number].number, velocity=min_velocity, channel=default_channel).bytes() self.midiout.send_message(note_off) pair += 1 note_number = track[pair][0] note_on = Message('note_on', note=chord.notes[note_number].number, velocity=chord.velocity, channel=default_channel).bytes() self.midiout.send_message(note_on) self.last_note_number = chord.notes[note_number].number time.sleep(0.01) def put(self, chord): if type(chord) == Chord: self.queue_out.put(chord) return True return False def set_up_ports(self): """ This is necessary to HEAR the music """ available_ports = self.midiout.get_ports() if available_ports: self.midiout.open_port(default_port) else: self.midiout.open_virtual_port("Tmp virtual output") def set_up_instrument(self, program=default_instrument): program_change = Message('program_change', program=program, channel=default_channel).bytes() self.midiout.send_message(program_change) def set_up_ultrasound_instrument(self, program=default_ultrasound_instrument): program_change = Message('program_change', program=program, channel=default_ultrasound_channel).bytes() self.midiout.send_message(program_change) def set_up_midi_for_file(self): self.midi_for_file.tracks.append(MidiTrack()) def set_tempo(self, tempo=default_tempo): self.tempo.value = tempo def set_deadline(self, deadline=0): self.deadline.value = deadline def set_start_peak(self, start=max_time): self.start_peak.value = start def get_sleeping_time(self): return self.deadline.value - time.monotonic() def get_track(self): return self.midi_for_file.tracks[0] def save_file(self, filename='my track.mid'): self.midi_for_file.save(filename) return filename def run(self): self.running.value = True self.set_up_ports() self.set_up_midi_for_file() self.set_up_instrument() self.set_up_ultrasound_instrument() self.queue_process = Process(target=run_queue_out, args=(self, )) self.queue_process.start() self.queue_process = Process(target=run_peak, args=(self, )) self.queue_process.start() def stop(self): """ All chords that already sound will continue to sound """ self.running.value = False self.queue_process.join() self.queue_process.join() self.queue_out = Queue() queue_out = None running = None tempo = None deadline = None start_peak = None start_chord = None queue_process = None peak_process = None midiout = None midi_for_file = None last_chord = None last_note_number = None
class Device: MIDI_IN = None MIDI_OUT = None MIDI_PORT_COUNT = 0 IS_FIRST_CONNECTION = True UPDATE_LIST = [] KEY_BINDINGS = {} PULSE_AUDIO = PulseAudioHelper() XORG = XorgHelper() def __init__(self, name='Generic Device', regex=None): self.NAME = name self.DEVICE_REGEX = regex self.UPDATE_LIST.append(self.update_midi_connection_on_reconnect) def __enter__(self): self.MIDI_IN = MidiIn() self.MIDI_OUT = MidiOut() return self def __exit__(self, _type, _value, _traceback): del self.MIDI_IN, self.MIDI_OUT def __repr__(self): return self.NAME def update(self, ELAPSED_TIME): for update in self.UPDATE_LIST: update(ELAPSED_TIME) def send_signal(self, channel, key, payload): self.MIDI_OUT.send_message([channel, key, payload]) def update_midi_connection_on_reconnect(self, _elapsed_time): current_port_count = len([ x for x in self.MIDI_OUT.get_ports() if not re.search(r'[rR]t[Mm]idi', x) ]) if current_port_count != self.MIDI_PORT_COUNT: self.MIDI_PORT_COUNT = current_port_count self.activate() def activate(self): if self.DEVICE_REGEX is not None: self.activate_ports() def activate_ports(self): activated = False self.MIDI_IN.close_port() self.MIDI_OUT.close_port() if not self.IS_FIRST_CONNECTION: print(f'{self} disconnected!') sleep(1.0) for midi in (self.MIDI_IN, self.MIDI_OUT): port_number = None for i, port_name in enumerate(midi.get_ports()): if re.search(self.DEVICE_REGEX, port_name): port_number = i break if port_number is not None: midi.open_port(port_number) activated = True if activated: self.IS_FIRST_CONNECTION = False print(f'{self} connected!') self.MIDI_IN.set_callback(self.input_callback) def input_callback(self, midi_signal_in, *_args, **_kwargs): byte_signal = midi_signal_in[0] key = str(byte_signal[1]) if key in self.KEY_BINDINGS.keys(): self.KEY_BINDINGS[key](byte_signal) else: self.animation_callback(byte_signal) def animation_callback(self, byte_signal): print(f'{self}:{byte_signal}')
def send_sysex_from_hex_string(hex_string: str, midi_out: MidiOut) -> None: data = bytearray.fromhex(hex_string) assert data[0] == 0xF0 and data[-1] == 0xF7 midi_out.send_message(data) data_hex = str(binascii.hexlify(bytearray(data)), 'utf-8') logging.debug(f'- SENT {len(data)} BYTES. SYSEX:\n{data_hex}')
def send_all_notes_off(port: rtmidi.MidiOut): for ch in range(16): port.send_message((MTYPE_SPECS[Mtypes.CC].status | ch, ALL_NOTES_OFF, 0))
def send_pitch_bend(touchpad: TouchpadInputDevice, x: int, midiout: MidiOut) -> None: pbend = min(16383, int(16384 * touchpad.normalize_x(x))) msg = [PITCH_BEND, pbend & 0x7F, (pbend >> 7) & 0x7F] midiout.send_message(msg) print(pbend // 260 * " ", msg)
def set_instrument(midi_out: MidiOut, instrument: Instrument): cc = midi_cmd(0xB, instrument.ch) pc = midi_cmd(0xC, instrument.ch) midi_out.send_message([pc, instrument.pc]) midi_out.send_message([cc, 0, instrument.cc])