def testSleeper_SleepUntil(self): # Burn in. for _ in range(10): concurrency.Sleeper().sleep(.01) future_time = time.time() + 0.5 concurrency.Sleeper().sleep_until(future_time) self.assertAlmostEqual(time.time(), future_time, delta=0.005)
def testStartPlayback_Updates(self): start_time = time.time() + 0.1 seq = music_pb2.NoteSequence() notes = [ Note(0, 100, start_time, start_time + 101), Note(1, 100, start_time, start_time + 101) ] testing_lib.add_track_to_sequence(seq, 0, notes) player = self.midi_hub.start_playback(seq, allow_updates=True) # Sleep past first note start. concurrency.Sleeper().sleep_until(start_time + 0.2) new_seq = music_pb2.NoteSequence() notes = [ Note(1, 100, 0.0, 0.8), Note(2, 100, 0.0, 1.0), Note(11, 55, 0.3, 0.5), Note(40, 45, 0.4, 0.6) ] notes = [ Note(note.pitch, note.velocity, note.start + start_time, note.end + start_time) for note in notes ] testing_lib.add_track_to_sequence(new_seq, 0, notes) player.update_sequence(new_seq) # Finish playing sequence. concurrency.Sleeper().sleep(0.8) # Start and end the unclosed note from the first sequence. note_events = [(start_time, 'note_on', 0), (start_time + 0.3, 'note_off', 0)] # The second note will not be played since it started before the update # and was not in the original sequence. del notes[1] for note in notes: note_events.append((note.start, 'note_on', note.pitch)) note_events.append((note.end, 'note_off', note.pitch)) note_events = collections.deque(sorted(note_events)) while not self.port.message_queue.empty(): msg = self.port.message_queue.get() note_event = note_events.popleft() self.assertEqual(msg.type, note_event[1]) self.assertEqual(msg.note, note_event[2]) self.assertAlmostEqual(msg.time, note_event[0], delta=0.01) self.assertTrue(not note_events) player.stop()
def run(self): """Sends message on the qpm interval until stop signal received.""" sleeper = concurrency.Sleeper() while True: now = time.time() tick_number = max( 0, int((now - self._start_time) // self._period) + 1) tick_time = tick_number * self._period + self._start_time if self._stop_time is not None and self._stop_time < tick_time: break sleeper.sleep_until(tick_time) metric_position = tick_number % len(self._messages) tick_message = self._messages[metric_position] if tick_message is None: continue tick_message.channel = self._channel self._outport.send(tick_message) if tick_message.type == 'note_on': sleeper.sleep(self._duration) end_tick_message = mido.Message('note_off', note=tick_message.note, channel=self._channel) self._outport.send(end_tick_message)
def wait_for_event(self, signal=None, timeout=None): """Blocks until a matching mido.Message arrives or the timeout occurs. Exactly one of `signal` or `timeout` must be specified. Using a timeout with a threading.Condition object causes additional delays when notified. Args: signal: A MidiSignal to use as a signal to stop waiting, or None. timeout: A float timeout in seconds, or None. Raises: MidiHubError: If neither `signal` nor `timeout` or both are specified. """ if (signal, timeout).count(None) != 1: raise MidiHubError( 'Exactly one of `signal` or `timeout` must be provided to ' '`wait_for_event` call.') if signal is None: concurrency.Sleeper().sleep(timeout) return signal_pattern = str(signal) cond_var = None for regex, cond_var in self._signals: if regex.pattern == signal_pattern: break if cond_var is None: cond_var = threading.Condition(self._lock) self._signals[re.compile(signal_pattern)] = cond_var cond_var.wait()
def run(self): """Outputs metronome tone on the qpm interval until stop signal received.""" period = 60. / self._qpm sleeper = concurrency.Sleeper() now = time.time() next_tick_time = max( self._start_time, now + period - ((now - self._start_time) % period)) while self._stop_time is None or self._stop_time > next_tick_time: sleeper.sleep_until(next_tick_time) self._outport.send( mido.Message(type='note_on', note=self._pitch, channel=_METRONOME_CHANNEL, velocity=self._velocity)) sleeper.sleep(self._duration) self._outport.send( mido.Message(type='note_off', note=self._pitch, channel=_METRONOME_CHANNEL)) now = time.time() next_tick_time = now + period - ((now - self._start_time) % period)
def run(self): """Outputs metronome tone on the qpm interval until stop signal received.""" sleeper = concurrency.Sleeper() while True: now = time.time() tick_number = max( 0, int((now - self._start_time) // self._period) + 1) tick_time = tick_number * self._period + self._start_time if self._stop_time is not None and self._stop_time < tick_time: break sleeper.sleep_until(tick_time) metric_position = tick_number % len(self._pitches) self._outport.send( mido.Message(type='note_on', note=self._pitches[metric_position], channel=_METRONOME_CHANNEL, velocity=self._velocity)) sleeper.sleep(self._duration) self._outport.send( mido.Message(type='note_off', note=self._pitches[metric_position], channel=_METRONOME_CHANNEL))
def testSleeper_Sleep(self): # Burn in. for _ in range(10): concurrency.Sleeper().sleep(.01) def sleep_test_thread(duration): start_time = time.time() concurrency.Sleeper().sleep(duration) self.assertAlmostEqual(time.time(), start_time + duration, delta=0.005) threads = [threading.Thread(target=sleep_test_thread, args=[i * 0.1]) for i in range(10)] for t in threads: t.start() for t in threads: t.join()
def iterate(self, signal=None, period=None): if (signal, period).count(None) != 1: raise MidiHubException( 'Exactly one of `signal` or `period` must be provided to `iterate` ' 'call.') if signal is None: sleeper = concurrency.Sleeper() next_yield_time = time.time() + period else: regex = re.compile(str(signal)) queue = Queue() with self._lock: self._iter_signals.append((regex, queue)) while self.is_alive(): if signal is None: skipped_periods = (time.time() - next_yield_time) // period if skipped_periods > 0: tf.logging.warn( 'Skipping %d %.3fs period(s) to catch up on iteration.', skipped_periods, period) next_yield_time += skipped_periods * period else: sleeper.sleep_until(next_yield_time) end_time = next_yield_time next_yield_time += period else: signal_msg = queue.get() if signal_msg is MidiCaptor._WAKE_MESSAGE: # This is only recieved when the thread is in the process of # terminating. Wait until it is done before yielding the final # sequence. self.join() break end_time = signal_msg.time # Acquire lock so that `captured_sequence` will be called before thread # terminates, if it has not already done so. with self._lock: if not self.is_alive(): break captured_sequence = self.captured_sequence(end_time) yield captured_sequence yield self.captured_sequence()
def wait_for_event(self, signal=None, timeout=None): if (signal, timeout).count(None) != 1: raise MidiHubException( 'Exactly one of `signal` or `timeout` must be provided to ' '`wait_for_event` call.') if signal is None: concurrency.Sleeper().sleep(timeout) return signal_pattern = str(signal) cond_var = None for regex, cond_var in self._signals: if regex.pattern == signal_pattern: break if cond_var is None: cond_var = threading.Condition(self._lock) self._signals[re.compile(signal_pattern)] = cond_var cond_var.wait()
def setUp(self): self.maxDiff = None self.capture_messages = [ mido.Message(type='note_on', note=0, time=0.01), mido.Message(type='control_change', control=1, value=1, time=0.02), mido.Message(type='note_on', note=1, time=2.0), mido.Message(type='note_off', note=0, time=3.0), mido.Message(type='note_on', note=2, time=3.0), mido.Message(type='note_on', note=3, time=4.0), mido.Message(type='note_off', note=2, time=4.0), mido.Message(type='note_off', note=1, time=5.0), mido.Message(type='control_change', control=1, value=1, time=6.0), mido.Message(type='note_off', note=3, time=100)] self.port = MockMidiPort() self.midi_hub = midi_hub.MidiHub([self.port], [self.port], midi_hub.TextureType.POLYPHONIC) # Burn in Sleeper for calibration. for _ in range(5): concurrency.Sleeper().sleep(0.05)
def iterate(self, signal=None, period=None): """Yields the captured sequence at every signal message or time period. Exactly one of `signal` or `period` must be specified. Continues until the captor terminates, at which point the final captured sequence is yielded before returning. If consecutive calls to iterate are longer than the period, immediately yields and logs a warning. Args: signal: A MidiSignal to use as a signal to yield, or None. period: A float period in seconds, or None. Yields: The captured NoteSequence at event time. Raises: MidiHubError: If neither `signal` nor `period` or both are specified. """ if (signal, period).count(None) != 1: raise MidiHubError( 'Exactly one of `signal` or `period` must be provided to `iterate` ' 'call.') if signal is None: sleeper = concurrency.Sleeper() next_yield_time = time.time() + period else: regex = re.compile(str(signal)) queue = Queue.Queue() with self._lock: self._iter_signals.append((regex, queue)) while self.is_alive(): if signal is None: skipped_periods = (time.time() - next_yield_time) // period if skipped_periods > 0: tf.logging.warn( 'Skipping %d %.3fs period(s) to catch up on iteration.', skipped_periods, period) next_yield_time += skipped_periods * period else: sleeper.sleep_until(next_yield_time) end_time = next_yield_time next_yield_time += period else: signal_msg = queue.get() if signal_msg is MidiCaptor._WAKE_MESSAGE: # This is only recieved when the thread is in the process of # terminating. Wait until it is done before yielding the final # sequence. self.join() break end_time = signal_msg.time # Acquire lock so that `captured_sequence` will be called before thread # terminates, if it has not already done so. with self._lock: if not self.is_alive(): break captured_sequence = self.captured_sequence(end_time) yield captured_sequence yield self.captured_sequence()
def sleep_test_thread(duration): start_time = time.time() concurrency.Sleeper().sleep(duration) self.assertAlmostEqual(time.time(), start_time + duration, delta=0.005)
def generate(unused_argv): # Downloads the bundle from the magenta website mm.notebook_utils.download_bundle("drum_kit_rnn.mag", "bundles") bundle = mm.sequence_generator_bundle.read_bundle_file( os.path.join("bundles", "drum_kit_rnn.mag")) # Initialize the generator "drum_kit" generator_map = drums_rnn_sequence_generator.get_generator_map() generator = generator_map["drum_kit"](checkpoint=None, bundle=bundle) generator.initialize() # Define constants qpm = 120 num_bars = 3 seconds_per_step = 60.0 / qpm / generator.steps_per_quarter num_steps_per_bar = constants.DEFAULT_STEPS_PER_BAR seconds_per_bar = num_steps_per_bar * seconds_per_step # Use a priming sequence primer_sequence = mm.midi_io.midi_file_to_note_sequence( os.path.join("primers", "Jazz_Drum_Basic_1_bar.mid")) primer_start_time = 0 primer_end_time = primer_start_time + seconds_per_bar # Calculates the generation start and end time generation_start_time = primer_end_time generation_end_time = generation_start_time + (seconds_per_bar * num_bars) generator_options = generator_pb2.GeneratorOptions() generator_options.args['temperature'].float_value = 1.1 generator_options.generate_sections.add(start_time=generation_start_time, end_time=generation_end_time) # Generates on primer sequence sequence = generator.generate(primer_sequence, generator_options) # Outputs the plot os.makedirs("output", exist_ok=True) plot_file = os.path.join("output", "out.html") pretty_midi = mm.midi_io.note_sequence_to_pretty_midi(sequence) plotter = Plotter() plotter.show(pretty_midi, plot_file) print(f"Generated plot file: {os.path.abspath(plot_file)}") # We find the proper input port for the software synth # (which is the output port for Magenta) output_ports = [ name for name in mido.get_output_names() if args.midi_port in name ] if not output_ports: raise Exception(f"Cannot find proper output ports in: " f"{mido.get_output_names()}") print(f"Playing generated MIDI in output port names: {output_ports}") # Start a new MIDI hub on that port (output only) midi_hub = MidiHub(input_midi_ports=[], output_midi_ports=output_ports, texture_type=None) # Start on a empty sequence, allowing the update of the # sequence for later. empty_sequence = music_pb2.NoteSequence() player = midi_hub.start_playback(empty_sequence, allow_updates=True) player._channel = 9 # We want a period in seconds of 4 bars (which is the loop # length). Using 240 / qpm, we have a period of 1 bar, or # 2 seconds at 120 qpm. We multiply that by 4 bars. # (using the Decimal class for more accuracy) period = Decimal(240) / qpm period = period * (num_bars + 1) sleeper = concurrency.Sleeper() while True: try: # We get the next tick time by using the period # to find the absolute tick number (since epoch), # and multiplying by the period length. This is # used to sleep until that time. # We also find the current tick time for the player # to update. # (using the Decimal class for more accuracy) now = Decimal(time.time()) tick_number = int(now // period) tick_number_next = tick_number + 1 tick_time = tick_number * period tick_time_next = tick_number_next * period print( f"now {now} tick_time {tick_time} tick_time_next {tick_time_next}" ) # Update the player time to the current tick time sequence_adjusted = music_pb2.NoteSequence() sequence_adjusted.CopyFrom(sequence) sequence_adjusted = adjust_sequence_times(sequence_adjusted, float(tick_time)) player.update_sequence(sequence_adjusted, start_time=float(tick_time)) # Sleep until the next tick time sleeper.sleep_until(float(tick_time_next)) except KeyboardInterrupt: print(f"Stopping") return 0