Ejemplo n.º 1
0
    def testPassThrough_Mono(self):
        self.midi_hub = midi_hub.MidiHub([self.port], [self.port],
                                         midi_hub.TextureType.MONOPHONIC)
        self.midi_hub.passthrough = False
        self.send_capture_messages()
        self.assertTrue(self.port.message_queue.empty())
        self.midi_hub.passthrough = True
        self.send_capture_messages()

        passed_messages = []
        while not self.port.message_queue.empty():
            passed_messages.append(self.port.message_queue.get())
            passed_messages[-1].time = 0
        expected_messages = [
            mido.Message(type='note_on', note=0),
            mido.Message(type='control_change', control=1, value=1),
            mido.Message(type='note_off', note=0),
            mido.Message(type='note_on', note=1),
            mido.Message(type='note_off', note=1),
            mido.Message(type='note_on', note=2),
            mido.Message(type='note_off', note=2),
            mido.Message(type='note_on', note=3),
            mido.Message(type='control_change', control=1, value=1),
            mido.Message(type='note_off', note=3)
        ]

        self.assertListEqual(passed_messages, expected_messages)
Ejemplo n.º 2
0
def main(unused_argv):
    tf.logging.set_verbosity(FLAGS.log)

    # Initialize MidiHub.
    hub = midi_hub.MidiHub(None, FLAGS.output_ports.split(','),
                           midi_hub.TextureType.MONOPHONIC)

    cc = FLAGS.clock_control_number

    # Assumes 4 beats per bar.
    metronome_signals = ([midi_hub.MidiSignal(control=cc, value=127)] +
                         [midi_hub.MidiSignal(control=cc, value=0)] * 3)

    hub.start_metronome(FLAGS.qpm,
                        start_time=0,
                        signals=metronome_signals,
                        channel=FLAGS.channel)

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        hub.stop_metronome()

    print('Clock stopped.')
Ejemplo n.º 3
0
def main(unused_argv):
  tf.logging.set_verbosity(FLAGS.log)

  if not _validate_flags():
    return

  # Load generators.
  generators = []
  for bundle_file in FLAGS.bundle_files.split(','):
    generators.append(_load_generator_from_bundle_file(bundle_file))
    if generators[-1] is None:
      return

  # Initialize MidiHub.
  if FLAGS.input_port not in midi_hub.get_available_input_ports():
    print "Opening '%s' as a virtual MIDI port for input." % FLAGS.input_port
  if FLAGS.output_port not in midi_hub.get_available_output_ports():
    print "Opening '%s' as a virtual MIDI port for output." % FLAGS.output_port
  hub = midi_hub.MidiHub(FLAGS.input_port, FLAGS.output_port,
                         midi_hub.TextureType.MONOPHONIC)

  start_call_signal = (
      None if FLAGS.start_call_control_number is None else
      midi_hub.MidiSignal(control=FLAGS.start_call_control_number, value=0))
  end_call_signal = (
      None if FLAGS.end_call_control_number is None else
      midi_hub.MidiSignal(control=FLAGS.end_call_control_number, value=0))
  interaction = midi_interaction.CallAndResponseMidiInteraction(
      hub,
      generators,
      FLAGS.qpm,
      generator_select_control_number=FLAGS.generator_select_control_number,
      phrase_bars=FLAGS.phrase_bars,
      start_call_signal=start_call_signal,
      end_call_signal=end_call_signal,
      temperature_control_number=FLAGS.temperature_control_number)

  _print_instructions()

  interaction.start()
  try:
    while True:
      time.sleep(1)
  except KeyboardInterrupt:
    interaction.stop()

  print 'Interaction stopped.'
Ejemplo n.º 4
0
  def testCaptureSequence_Mono(self):
    start_time = 1.0

    threading.Timer(0.1, self.send_capture_messages).start()
    self.midi_hub = midi_hub.MidiHub([self.port], [self.port],
                                     midi_hub.TextureType.MONOPHONIC)
    captured_seq = self.midi_hub.capture_sequence(
        120, start_time,
        stop_signal=midi_hub.MidiSignal(type='control_change', control=1))

    expected_seq = music_pb2.NoteSequence()
    expected_seq.tempos.add(qpm=120)
    expected_seq.total_time = 6
    testing_lib.add_track_to_sequence(
        expected_seq, 0,
        [Note(1, 64, 2, 3), Note(2, 64, 3, 4), Note(3, 64, 4, 6)])
    self.assertProtoEquals(captured_seq, expected_seq)
Ejemplo n.º 5
0
  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)
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
def main(unused_argv):
    tf.logging.set_verbosity(FLAGS.log)

    if not _validate_flags():
        return

    # Load generators.
    generators = []
    for bundle_file in FLAGS.bundle_files.split(','):
        generators.append(_load_generator_from_bundle_file(bundle_file))
        if generators[-1] is None:
            return

    # Initialize MidiHub.
    if FLAGS.input_port not in midi_hub.get_available_input_ports():
        print "Opening '%s' as a virtual MIDI port for input." % FLAGS.input_port
    if FLAGS.output_port not in midi_hub.get_available_output_ports():
        print "Opening '%s' as a virtual MIDI port for output." % FLAGS.output_port
    hub = midi_hub.MidiHub(FLAGS.input_port,
                           FLAGS.output_port,
                           midi_hub.TextureType.MONOPHONIC,
                           passthrough=FLAGS.passthrough,
                           playback_channel=FLAGS.playback_channel,
                           playback_offset=FLAGS.playback_offset)

    if FLAGS.clock_control_number is None:
        # Set the tick duration to be a single bar, assuming a 4/4 time signature.
        clock_signal = None
        tick_duration = 4 * (60. / FLAGS.qpm)
    else:
        clock_signal = midi_hub.MidiSignal(control=FLAGS.clock_control_number,
                                           value=127)
        tick_duration = None

    end_call_signal = (None if FLAGS.end_call_control_number is None else
                       midi_hub.MidiSignal(
                           control=FLAGS.end_call_control_number, value=127))
    panic_signal = (None if FLAGS.panic_control_number is None else
                    midi_hub.MidiSignal(control=FLAGS.panic_control_number,
                                        value=127))
    mutate_signal = (None if FLAGS.mutate_control_number is None else
                     midi_hub.MidiSignal(control=FLAGS.mutate_control_number,
                                         value=127))
    interaction = midi_interaction.CallAndResponseMidiInteraction(
        hub,
        generators,
        FLAGS.qpm,
        FLAGS.generator_select_control_number,
        clock_signal=clock_signal,
        tick_duration=tick_duration,
        end_call_signal=end_call_signal,
        panic_signal=panic_signal,
        mutate_signal=mutate_signal,
        allow_overlap=FLAGS.allow_overlap,
        enable_metronome=FLAGS.enable_metronome,
        min_listen_ticks_control_number=FLAGS.min_listen_ticks_control_number,
        max_listen_ticks_control_number=FLAGS.max_listen_ticks_control_number,
        response_ticks_control_number=FLAGS.response_ticks_control_number,
        tempo_control_number=FLAGS.tempo_control_number,
        temperature_control_number=FLAGS.temperature_control_number,
        loop_control_number=FLAGS.loop_control_number,
        state_control_number=FLAGS.state_control_number)

    _print_instructions()

    interaction.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        interaction.stop()

    print 'Interaction stopped.'
Ejemplo n.º 8
0
def main(unused_argv):
    tf.logging.set_verbosity(FLAGS.log)

    if not _validate_flags():
        return

    # Load generators.
    generators = []
    for bundle_file in FLAGS.bundle_files.split(','):
        generators.append(_load_generator_from_bundle_file(bundle_file))
        if generators[-1] is None:
            return

    # Initialize MidiHub.
    hub = midi_hub.MidiHub(FLAGS.input_ports.split(','),
                           FLAGS.output_ports.split(','),
                           midi_hub.TextureType.POLYPHONIC,
                           passthrough=FLAGS.passthrough,
                           playback_channel=FLAGS.playback_channel,
                           playback_offset=FLAGS.playback_offset)

    control_map = {
        re.sub('_control_number$', '', f): FLAGS.__getattr__(f)
        for f in _CONTROL_FLAGS
    }
    if FLAGS.learn_controls:
        CCMapper(control_map, hub).update_map()

    if control_map['clock'] is None:
        # Set the tick duration to be a single bar, assuming a 4/4 time signature.
        clock_signal = None
        tick_duration = 4 * (60. / FLAGS.qpm)
    else:
        clock_signal = midi_hub.MidiSignal(control=control_map['clock'],
                                           value=127)
        tick_duration = None

    end_call_signal = (None if control_map['end_call'] is None else
                       midi_hub.MidiSignal(control=control_map['end_call'],
                                           value=127))
    panic_signal = (None if control_map['panic'] is None else
                    midi_hub.MidiSignal(control=control_map['panic'],
                                        value=127))
    mutate_signal = (None if control_map['mutate'] is None else
                     midi_hub.MidiSignal(control=control_map['mutate'],
                                         value=127))
    metronome_channel = (FLAGS.metronome_channel
                         if FLAGS.enable_metronome else None)
    interaction = midi_interaction.CallAndResponseMidiInteraction(
        hub,
        generators,
        FLAGS.qpm,
        FLAGS.generator_select_control_number,
        clock_signal=clock_signal,
        tick_duration=tick_duration,
        end_call_signal=end_call_signal,
        panic_signal=panic_signal,
        mutate_signal=mutate_signal,
        allow_overlap=FLAGS.allow_overlap,
        metronome_channel=metronome_channel,
        min_listen_ticks_control_number=control_map['min_listen_ticks'],
        max_listen_ticks_control_number=control_map['max_listen_ticks'],
        response_ticks_control_number=control_map['response_ticks'],
        tempo_control_number=control_map['tempo'],
        temperature_control_number=control_map['temperature'],
        loop_control_number=control_map['loop'],
        state_control_number=control_map['state'])

    _print_instructions()

    interaction.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        interaction.stop()

    print 'Interaction stopped.'
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
Ejemplo n.º 10
0
def main(unused_argv):
    if FLAGS.list_ports:
        print "Input ports: '%s'" % ("', '".join(
            midi_hub.get_available_input_ports()))
        print "Ouput ports: '%s'" % ("', '".join(
            midi_hub.get_available_output_ports()))
        return

    if FLAGS.bundle_file is None:
        print '--bundle_file must be specified.'
        return

    if (FLAGS.end_call_control_number, FLAGS.phrase_bars).count(None) != 1:
        print(
            'Exactly one of --end_call_control_number or --phrase_bars should be '
            'specified.')
        return

    try:
        bundle = magenta.music.sequence_generator_bundle.read_bundle_file(
            FLAGS.bundle_file)
    except magenta.music.sequence_generator_bundle.GeneratorBundleParseException:
        print 'Failed to parse bundle file: %s' % FLAGS.bundle_file
        return

    generator_id = bundle.generator_details.id
    if generator_id not in _GENERATOR_FACTORY_MAP:
        print "Unrecognized SequenceGenerator ID '%s' in bundle file: %s" % (
            generator_id, FLAGS.bundle_file)
        return
    generator = _GENERATOR_FACTORY_MAP[generator_id].create_generator(
        checkpoint=None, bundle=bundle)
    generator.initialize()
    print "Loaded '%s' generator bundle from file '%s'." % (
        bundle.generator_details.id, FLAGS.bundle_file)

    if FLAGS.input_port not in midi_hub.get_available_input_ports():
        print "Opening '%s' as a virtual MIDI port for input." % FLAGS.input_port
    if FLAGS.output_port not in midi_hub.get_available_output_ports():
        print "Opening '%s' as a virtual MIDI port for output." % FLAGS.output_port
    hub = midi_hub.MidiHub(FLAGS.input_port, FLAGS.output_port,
                           midi_hub.TextureType.MONOPHONIC)

    end_call_signal = (None if FLAGS.end_call_control_number is None else
                       midi_hub.MidiSignal(
                           control=FLAGS.end_call_control_number, value=0))
    interaction = midi_interaction.CallAndResponseMidiInteraction(
        hub,
        FLAGS.qpm,
        generator,
        phrase_bars=FLAGS.phrase_bars,
        end_call_signal=end_call_signal)

    print ''
    print 'Instructions:'
    print 'Play when you hear the metronome ticking.'
    if FLAGS.phrase_bars is not None:
        print('After %d bars (4 beats), Magenta will play its response.' %
              FLAGS.phrase_bars)
        print(
            'Once the response completes, the metronome will tick and you can '
            'play again.')
    else:
        print(
            'When you want to end the call phrase, signal control number %d '
            'with value 0' % FLAGS.end_call_control_number)
        print(
            'At the end of the current bar (4 beats), Magenta will play its '
            'response.')
        print(
            'Once the response completes, the metronome will tick and you can '
            'play again.')
    print ''
    print 'To end the interaction, press CTRL-C.'

    interaction.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        interaction.stop()

    print 'Interaction stopped.'
Ejemplo n.º 11
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

    # 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
Ejemplo n.º 12
0
def app(unused_argv):
    tf.logging.debug("Starting app")

    # Start action server
    action_server = ActionServer()
    action_server.start()

    # Init midi ports, keep direct references to output_ports for
    # direct sending without the hub player
    if platform.system() == "Windows":
        input_ports = [
            port for port in midi_hub.get_available_input_ports()
            if MIDI_INPUT_PORT in port
        ]
        output_ports = [
            port for port in midi_hub.get_available_output_ports()
            if MIDI_OUTPUT_PORT in port
        ]
        if len(input_ports) is not 1 or len(output_ports) is not 1:
            raise Exception(f"Need exactly 1 midi input ({input_ports}) "
                            f"matching {MIDI_INPUT_PORT}"
                            f"and 1 midi output port ({output_ports}) "
                            f"matching {MIDI_OUTPUT_PORT},"
                            f"you can use LoopMIDI for that")
    else:
        input_ports = [MIDI_INPUT_PORT]
        output_ports = [MIDI_OUTPUT_PORT]
    hub = midi_hub.MidiHub(input_ports, output_ports, None)
    output_port = hub._outport.ports[0]

    # Panic to stop all current messages (note off everywhere)
    [output_port.send(message) for message in mido.ports.panic_messages()]

    # Synchronise event for all the loopers, controlled by the metronome
    bar_start_event = threading.Event()

    # Common stuff
    qpm = 80
    timing = Timing(qpm)

    loopers = []
    try:
        # Init and start the loopers, they block on the event
        drum_looper = SequenceLooper("drums",
                                     bar_start_event,
                                     action_server,
                                     hub,
                                     "drum_kit_rnn",
                                     "drum_kit",
                                     timing,
                                     midi_channel=9,
                                     bar_per_loop=2)
        melody_looper = SequenceLooper("melody",
                                       bar_start_event,
                                       action_server,
                                       hub,
                                       "attention_rnn",
                                       "attention_rnn",
                                       timing,
                                       midi_channel=0,
                                       bar_per_loop=4)

        loopers.append(drum_looper)
        loopers.append(melody_looper)
        [looper.start() for looper in loopers]

        tf.logging.debug("Loopers started " +
                         str([("drum_looper", drum_looper),
                              ("melody_looper", melody_looper)]))

        # Start metronome (wait to make sure everything is started)
        time.sleep(1)
        metronome = Metronome(bar_start_event, timing)
        loopers.append(metronome)
        metronome.start()

        tf.logging.debug("Metronome started " +
                         str([("metronome", metronome)]))

        # Wait for the loopers
        [looper.join() for looper in loopers]
    except KeyboardInterrupt:
        print("SIGINT received, stopping action server, loopers and stuff")
        action_server.stop()
        [looper.stop() for looper in loopers]
        return 1

    return 0