def _generate(self, input_sequence, zero_time, response_start_time, response_end_time): """Generates a response sequence with the currently-selected generator. Args: input_sequence: The NoteSequence to use as a generation seed. zero_time: The float time in seconds to treat as the start of the input. response_start_time: The float time in seconds for the start of generation. response_end_time: The float time in seconds for the end of generation. Returns: The generated NoteSequence. """ # Generation is simplified if we always start at 0 time. response_start_time -= zero_time response_end_time -= zero_time generator_options = generator_pb2.GeneratorOptions() generator_options.input_sections.add( start_time=0, end_time=response_start_time) generator_options.generate_sections.add( start_time=response_start_time, end_time=response_end_time) # Get current temperature setting. generator_options.args['temperature'].float_value = self._temperature # Generate response. tf.logging.info( "Generating sequence using '%s' generator.", self._sequence_generator.details.id) tf.logging.debug('Generator Details: %s', self._sequence_generator.details) tf.logging.debug('Bundle Details: %s', self._sequence_generator.bundle_details) tf.logging.debug('Generator Options: %s', generator_options) response_sequence = self._sequence_generator.generate( adjust_sequence_times(input_sequence, -zero_time), generator_options) response_sequence = note_seq.trim_note_sequence(response_sequence, response_start_time, response_end_time) return adjust_sequence_times(response_sequence, zero_time)
def generate(unused_argv): """Generates a basic drum sequence of 4 seconds based on a hard coded primer""" # TODO doc 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")) generator_map = drums_rnn_sequence_generator.get_generator_map() generator = generator_map["drum_kit"](checkpoint=None, bundle=bundle) generator.initialize() 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 # Creates a primer sequence that is fed into the model for the generator, # which will generate a sequence based on this one # A DrumTrack models a drum sequence by step, so you have step 1 being the # midi note 36 (bass drum), followed by 3 steps of silence (those four steps # constitutes the first beat or quarter), followed by both notes 36 and 41 # being struck at the same time (followed by silence by these are optional) primer_drums = mm.DrumTrack( [frozenset(pitches) for pitches in [(38, 51), (), (36,), (), (38, 44, 51), (), (36,), (), (), (), (38,), (), (38, 44), (), (36, 51), (), ]]) primer_sequence = primer_drums.to_sequence(qpm=qpm) primer_start_time = 0 primer_end_time = primer_start_time + seconds_per_bar # Defines the start and end of the generation, which starts at the step # after the end of the primer (we'll see in 03.py this calculation makes # it harder to fall on proper beats) and ends at total seconds # The complete generation will thus contain the primer and the total length # needs to be at least the size of the primer # TODO doc 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) # We are using the primer sequence here instead of an empty sequence sequence = generator.generate(primer_sequence, generator_options) # TODO doc plot_file = os.path.join("output", "out.html") print("Generated plot file: " + str(os.path.abspath(plot_file))) pretty_midi = mm.midi_io.note_sequence_to_pretty_midi(sequence) plotter = Plotter() plotter.show(pretty_midi, plot_file) # TODO doc input_ports = [name for name in mido.get_output_names() if "VirtualMIDISynth" in name or "FLUID Synth" in name] if not input_ports: print("Cannot find proper input port in " + str(mido.get_output_names())) print("Playing generated MIDI in input port names: " + str(input_ports)) midi_hub = mh.MidiHub([], input_ports, None) # TODO doc empty_sequence = music_pb2.NoteSequence() player = midi_hub.start_playback(empty_sequence, allow_updates=True) player._channel = 9 # TODO doc wall_start_time = time.time() sequence_adjusted = music_pb2.NoteSequence() sequence_adjusted.CopyFrom(sequence) sequence_adjusted = adjust_sequence_times(sequence_adjusted, wall_start_time) player.update_sequence(sequence_adjusted, start_time=wall_start_time) # TODO doc try: player.join(generation_end_time) except KeyboardInterrupt: return 0 finally: return 0
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. We don't especially need that right # now, because we could play the sequence immediately, but # it will be useful for later examples to have a player to # update new sequences with. empty_sequence = music_pb2.NoteSequence() player = midi_hub.start_playback(empty_sequence, allow_updates=True) # Remember that GM 1 compatible synthesizer will play the drums # sound bank if the MIDI channel is 10 (but the channel is zero # indexed in Magenta MIDI hub so you have to use 9). player._channel = 9 # Now we can play our sequence, but we need to adjust it first. # The MIDI player will play the sequence according to wall time, # but our sequence starts at 0. # Create a new empty note sequence, copy the sequence # we want to play in the empty sequence, then move the # start of the sequence by wall_start_time amount wall_start_time = time.time() sequence_adjusted = music_pb2.NoteSequence() sequence_adjusted.CopyFrom(sequence) sequence_adjusted = adjust_sequence_times(sequence_adjusted, wall_start_time) # The update sequence is the equivalent of "play" player.update_sequence(sequence_adjusted, start_time=wall_start_time) # We "join" on the thread, meaning the call will block # until the player has finished. Because the thread # never stops, this call will block indefinitely. By # adding a timeout of generation_end_time, the call will # return after the end of the sequence being played. try: print(f"Playing for {generation_end_time}") player.join(generation_end_time) except KeyboardInterrupt: # The KeyboardInterrupt is important if you want to press # CTRL-C during the playback to stop the player. print(f"Stopping") return 0
def generate(unused_argv): 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")) generator_map = drums_rnn_sequence_generator.get_generator_map() generator = generator_map["drum_kit"](checkpoint=None, bundle=bundle) generator.initialize() 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 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 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) sequence = generator.generate(primer_sequence, generator_options) 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)}") input_ports = [ name for name in mido.get_output_names() if "VirtualMIDISynth" in name or "FLUID Synth" in name ] if not input_ports: raise Exception(f"Cannot find proper input port in: " f"{mido.get_output_names()}") print(f"Playing generated MIDI in input port names: {input_ports}") midi_hub = mh.MidiHub([], input_ports, None) empty_sequence = music_pb2.NoteSequence() player = midi_hub.start_playback(empty_sequence, allow_updates=True) player._channel = 9 # We calculate the length of the generated sequence in seconds, # which gives up the loop time in seconds loop_time = generation_end_time - primer_start_time print(f"Loop time is {loop_time}") # We get the current wall time before the loop starts wall_start_time = time.time() while True: try: # We get the current wall time for this loop start tick_wall_start_time = time.time() sequence_adjusted = music_pb2.NoteSequence() sequence_adjusted.CopyFrom(sequence) sequence_adjusted = adjust_sequence_times(sequence_adjusted, tick_wall_start_time) player.update_sequence(sequence_adjusted, start_time=tick_wall_start_time) # We calculate the elapsed time from the start of the program tick_start_time = time.time() - wall_start_time # We sleep for the remaining time in the loop. It means that whatever # how much time this loop took, we'll be waking up at the proper # next bar. # For example, if the loop needs to be 8 seconds, and we took 2.4 seconds # executing and arriving here, then we'll sleep only 5.6 seconds to wake # up with proper timing. sleep_time = loop_time - (tick_start_time % loop_time) print(f"Sleeping for {sleep_time}") time.sleep(sleep_time) except KeyboardInterrupt: print(f"Stopping") return 0
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
def run(self): sequence = music_pb2.NoteSequence() player = self._midi_hub.start_playback(sequence, allow_updates=True) player._channel = self._midi_channel pretty_midi = pm.PrettyMIDI() pretty_midi.instruments.append(pm.Instrument(0)) # Wait for the dreamer and store the time with the delta wall_start_time = time.time() self._bar_start_event.wait() bar_count = 0 while not self._stop_signal: # Number of seconds we should be at the beginning of this loop expected_start_time = self._timing.get_expected_start_time(bar_count) # Number of actual seconds since we started this thread from wall clock, # which is smaller then the expected start time # The difference is between: the actual wakeup time and the expected # (calculated) start time. By keeping this we can adjust the sequence # according to the drift. diff_time = self._timing.get_diff_time(wall_start_time, bar_count) tf.logging.debug("Playing " + str(self._timing.get_timing_args( wall_start_time, bar_count))) # Player sequence_adjusted = music_pb2.NoteSequence() sequence_adjusted.CopyFrom(sequence) sequence_adjusted = adjust_sequence_times(sequence_adjusted, wall_start_time - diff_time) player.update_sequence(sequence_adjusted, start_time=expected_start_time) # Plotter pretty_midi = mm.midi_io.note_sequence_to_pretty_midi(sequence) self._plotter.save(pretty_midi, self._output_plot) pretty_midi.write(self._output_midi) # Sets timing seconds_per_bar = self._timing.get_seconds_per_bar() seconds_per_loop = self._bar_per_loop * seconds_per_bar loop_start_time = expected_start_time loop_end_time = loop_start_time + seconds_per_loop generation_start_time = loop_end_time generation_end_time = generation_start_time + seconds_per_loop action = self._action_server.context.get(self.name, None) tf.logging.debug(str(action) + " " + str([ ("expected_start_time", expected_start_time), ("loop_start_time", loop_start_time), ("loop_end_time", loop_end_time), ("generation_start_time", generation_start_time), ("generation_end_time", generation_end_time)])) if not action: pass elif action is ActionType.LOOP: sequence = sequences.loop(sequence, loop_start_time, loop_end_time, seconds_per_loop) elif action is ActionType.GENERATE: sequence = sequences.generate(sequence, self.name, self._bundle_filename, self._config_name, generation_start_time, generation_end_time) elif action is ActionType.GENERATE_ONCE: sequence = sequences.generate(sequence, self.name, self._bundle_filename, self._config_name, generation_start_time, generation_end_time) self._action_server.context[self.name] = ActionType.LOOP elif action is ActionType.RESET_ONCE: sequence = sequences.reset(sequence, loop_start_time, loop_end_time, seconds_per_loop) self._action_server.context[self.name] = ActionType.LOOP elif action is ActionType.RESET_GENERATE: sequence = sequences.reset(sequence, loop_start_time, loop_end_time, seconds_per_loop) self._action_server.context[self.name] = ActionType.GENERATE_ONCE else: raise Exception(f"Unknown action {action}") while True: # Unlock at the start of the bar self._bar_start_event.wait() bar_count += 1 if bar_count % self._bar_per_loop == 0: break
def run(self): magenta.music.DrumTrack([frozenset([36])]) primer_drums = magenta.music.DrumTrack( [frozenset(pitches) for pitches in [(36, ), (), (), (), (46, )]]) sequence = primer_drums.to_sequence(qpm=120) player = self._midi_hub.start_playback(sequence, allow_updates=True) wall_start_time = time.time() # TODO describe qpm = 120 # TODO describe # steps_per_quarter = 4 seconds_per_step = 60.0 / qpm / self._sequence_generator.steps_per_quarter # TODO describe num_steps = 32 # TODO describe total_seconds = num_steps * seconds_per_step # TODO describe primer_end_time = sequence.total_time # TODO describe start_time = primer_end_time + seconds_per_step end_time = total_seconds for index in range(0, sys.maxsize): tick_wall_start_time = time.time() # TODO describe start_time2 = start_time + index * total_seconds end_time2 = end_time + index * total_seconds # TODO describe generator_options = generator_pb2.GeneratorOptions() generator_options.generate_sections.add(start_time=start_time2, end_time=end_time2) # TODO describe generator_options.args['temperature'].float_value = 0.1 generator_options.args['beam_size'].int_value = 1 generator_options.args['branch_factor'].int_value = 1 generator_options.args['steps_per_iteration'].int_value = 1 # TODO describe print("index: " + str(index)) print("primer_end_time: " + str(primer_end_time)) print("start_time: " + str(start_time)) print("start_time2: " + str(start_time2)) print("end_time: " + str(end_time)) print("end_time2: " + str(end_time2)) print("wall_start_time: " + str(wall_start_time)) print("tick_wall_start_time: " + str(tick_wall_start_time)) print("tick_wall_start_time (adjusted): " + str(tick_wall_start_time - wall_start_time)) sequence = self._sequence_generator.generate( sequence, generator_options) sequence_adjusted = music_pb2.NoteSequence() sequence_adjusted.CopyFrom(sequence) sequence_adjusted = adjust_sequence_times(sequence_adjusted, wall_start_time) player.update_sequence(sequence_adjusted, start_time=tick_wall_start_time) pm = midi_io.note_sequence_to_pretty_midi(sequence) output_file(self._output_file) plot = plot_midi(pm) show(plot) time.sleep(total_seconds - ((time.time() - wall_start_time) % total_seconds))
def generate(unused_argv): 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")) generator_map = drums_rnn_sequence_generator.get_generator_map() generator = generator_map["drum_kit"](checkpoint=None, bundle=bundle) generator.initialize() 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 # This time we get the primer from disk instead of hard coding it 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 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) sequence = generator.generate(primer_sequence, generator_options) 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("Generated plot file: " + str(os.path.abspath(plot_file))) # We find the proper input port for the software synth, # this should work on all platforms (if you followed the # installation instructions) input_ports = [ name for name in mido.get_output_names() if "VirtualMIDISynth" in name or "FLUID Synth" in name ] if not input_ports: print("Cannot find proper input port in " + str(mido.get_output_names())) print("Playing generated MIDI in input port names: " + str(input_ports)) # Start a new MIDI hub on that port (incoming only) midi_hub = mh.MidiHub([], input_ports, None) # Start on a empty sequence, allowing the update of the # sequence for later. We don't especially need that right # now, because we could play the sequence immediately, but # it will be useful for later examples to have a player to # update new sequences with. empty_sequence = music_pb2.NoteSequence() player = midi_hub.start_playback(empty_sequence, allow_updates=True) # Remember that GM 1 compatible synthesizer will play the drums # sound bank if the MIDI channel is 10 (but the channel is zero # indexed in Magenta MIDI hub so you have to use 9). player._channel = 9 # Now we can play our sequence, but we need to adjust it first. # The MIDI player will play the sequence according to wall time, # but our sequence starts at 0. # Create a new empty note sequence, copy the sequence # we want to play in the empty sequence, then move the # start of the sequence by wall_start_time amount wall_start_time = time.time() sequence_adjusted = music_pb2.NoteSequence() sequence_adjusted.CopyFrom(sequence) sequence_adjusted = adjust_sequence_times(sequence_adjusted, wall_start_time) # The update sequence is the equivalent of "play" player.update_sequence(sequence_adjusted, start_time=wall_start_time) # We "join" on the thread, meaning the call will block # until the player has finished. Because the thread # never stops, this call will block indefinitely. By # adding a timeout of generation_end_time, the call will # return after the end of the sequence being played. try: player.join(generation_end_time) except KeyboardInterrupt: # The KeyboardInterrupt is important if you want to press # CTRL-C during the playback to stop the player. return 0 finally: return 0
def run(self): # TODO sequence = self._primer_sequence player = self._midi_hub.start_playback(sequence, allow_updates=True) # TODO seconds_per_step = 60.0 / self._qpm / self._sequence_generator.steps_per_quarter num_steps_per_bar = self._sequence_generator.steps_per_quarter * 2 seconds_per_bar = num_steps_per_bar * seconds_per_step seconds_per_loop = self._bar_per_loop * seconds_per_bar # TODO MOVE plotter = Plotter(max_bar=16, live_reload=True) pretty_midi = pm.PrettyMIDI() pretty_midi.instruments.append(pm.Instrument(0)) pretty_midi.instruments[0].append(pm.Note(100, 36, 0, 1)) # TODO wall_start_time = time.time() # TODO for bar_count in range(0, sys.maxsize): # TODO cursor_time = bar_count * seconds_per_loop # TODO sequence_adjusted = music_pb2.NoteSequence() sequence_adjusted.CopyFrom(sequence) sequence_adjusted = adjust_sequence_times(sequence_adjusted, wall_start_time) player.update_sequence(sequence_adjusted, start_time=cursor_time) # TODO MOVE TO UTILS pretty_sequence = mm.midi_io.note_sequence_to_pretty_midi(sequence) for instrument in pretty_sequence.instruments: for note in instrument.notes: pretty_midi.instruments[0].notes.append(note) plotter.show(pretty_midi, self._output_file) # TODO loop_start_time = cursor_time loop_end_time = loop_start_time + seconds_per_loop generation_start_time = loop_end_time generation_end_time = generation_start_time + seconds_per_loop generator_options = generator_pb2.GeneratorOptions() generator_options.args['temperature'].float_value = 1 generator_options.generate_sections.add( start_time=generation_start_time, end_time=generation_end_time) # TODO if bar_count % self._num_loops == 0: print("GENERATING") sequence = self._sequence_generator.generate( sequence, generator_options) sequence = sequences_lib.trim_note_sequence( sequence, generation_start_time, generation_end_time) else: print("LOOPING") sequence = sequences_lib.trim_note_sequence( sequence, loop_start_time, loop_end_time) sequence = sequences_lib.shift_sequence_times( sequence, seconds_per_loop) # TODO 1 wake up per bar sleep_time = seconds_per_loop - ( (time.time() - wall_start_time) % seconds_per_loop) time.sleep(sleep_time)
def run(self): """The main loop for a real-time call and response interaction.""" start_time = time.time() self._captor = self._midi_hub.start_capture(self._qpm, start_time) if not self._clock_signal and self._metronome_channel is not None: self._midi_hub.start_metronome( self._qpm, start_time, channel=self._metronome_channel) # Set callback for end call signal. if self._end_call_signal is not None: self._captor.register_callback(self._end_call_callback, signal=self._end_call_signal) if self._panic_signal is not None: self._captor.register_callback(self._panic_callback, signal=self._panic_signal) if self._mutate_signal is not None: self._captor.register_callback(self._mutate_callback, signal=self._mutate_signal) # Keep track of the end of the previous tick time. last_tick_time = time.time() # Keep track of the duration of a listen state. listen_ticks = 0 # Start with an empty response sequence. response_sequence = music_pb2.NoteSequence() response_start_time = 0 response_duration = 0 player = self._midi_hub.start_playback( response_sequence, allow_updates=True) # Enter loop at each clock tick. for captured_sequence in self._captor.iterate(signal=self._clock_signal, period=self._tick_duration): if self._stop_signal.is_set(): break if self._panic.is_set(): response_sequence = music_pb2.NoteSequence() player.update_sequence(response_sequence) self._panic.clear() tick_time = captured_sequence.total_time # Set to current QPM, since it might have changed. if not self._clock_signal and self._metronome_channel is not None: self._midi_hub.start_metronome( self._qpm, tick_time, channel=self._metronome_channel) captured_sequence.tempos[0].qpm = self._qpm tick_duration = tick_time - last_tick_time if captured_sequence.notes: last_end_time = max(note.end_time for note in captured_sequence.notes) else: last_end_time = 0.0 # True iff there was no input captured during the last tick. silent_tick = last_end_time <= last_tick_time if not silent_tick: listen_ticks += 1 if not captured_sequence.notes: # Reset captured sequence since we are still idling. if response_sequence.total_time <= tick_time: self._update_state(self.State.IDLE) if self._captor.start_time < tick_time: self._captor.start_time = tick_time self._end_call.clear() listen_ticks = 0 elif (self._end_call.is_set() or silent_tick or listen_ticks >= self._max_listen_ticks): if listen_ticks < self._min_listen_ticks: tf.logging.info( 'Input too short (%d vs %d). Skipping.', listen_ticks, self._min_listen_ticks) self._captor.start_time = tick_time else: # Create response and start playback. self._update_state(self.State.RESPONDING) capture_start_time = self._captor.start_time if silent_tick: # Move the sequence forward one tick in time. captured_sequence = adjust_sequence_times( captured_sequence, tick_duration) captured_sequence.total_time = tick_time capture_start_time += tick_duration # Compute duration of response. num_ticks = self._midi_hub.control_value( self._response_ticks_control_number) if num_ticks: response_duration = num_ticks * tick_duration else: # Use capture duration. response_duration = tick_time - capture_start_time response_start_time = tick_time response_sequence = self._generate( captured_sequence, capture_start_time, response_start_time, response_start_time + response_duration) # If it took too long to generate, push response to next tick. if (time.time() - response_start_time) >= tick_duration / 4: push_ticks = ( (time.time() - response_start_time) // tick_duration + 1) response_start_time += push_ticks * tick_duration response_sequence = adjust_sequence_times( response_sequence, push_ticks * tick_duration) tf.logging.warn( 'Response too late. Pushing back %d ticks.', push_ticks) # Start response playback. Specify the start_time to avoid stripping # initial events due to generation lag. player.update_sequence( response_sequence, start_time=response_start_time) # Optionally capture during playback. if self._allow_overlap: self._captor.start_time = response_start_time else: self._captor.start_time = response_start_time + response_duration # Clear end signal and reset listen_ticks. self._end_call.clear() listen_ticks = 0 else: # Continue listening. self._update_state(self.State.LISTENING) # Potentially loop or mutate previous response. if self._mutate.is_set() and not response_sequence.notes: self._mutate.clear() tf.logging.warn('Ignoring mutate request with nothing to mutate.') if (response_sequence.total_time <= tick_time and (self._should_loop or self._mutate.is_set())): if self._mutate.is_set(): new_start_time = response_start_time + response_duration new_end_time = new_start_time + response_duration response_sequence = self._generate( response_sequence, response_start_time, new_start_time, new_end_time) response_start_time = new_start_time self._mutate.clear() response_sequence = adjust_sequence_times( response_sequence, tick_time - response_start_time) response_start_time = tick_time player.update_sequence( response_sequence, start_time=tick_time) last_tick_time = tick_time player.stop()