(self)
The main loop for a real-time call and response interaction.
The main loop for a real-time call and response interaction.
The main loop for a real - time call and response interaction .
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()
def run ( self ) : 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 ( )