Ejemplo n.º 1
0
 def __init__(self,
              name: str,
              bar_start_event,
              action_server: ActionServer,
              midi_hub,
              bundle_name: str,
              config_name: str,
              timing: Timing,
              midi_channel: int = 1,
              bar_per_loop: int = 2):
   super(SequenceLooper, self).__init__()
   self.name = name
   self._stop_signal = False
   self._bar_start_event = bar_start_event
   self._action_server = action_server
   self._midi_hub = midi_hub
   self._bundle_filename = bundle_name + ".mag"
   self._config_name = config_name
   self._timing = timing
   self._midi_channel = midi_channel
   self._bar_per_loop = bar_per_loop
   model_dir = os.path.join("output", "models")
   if not os.path.exists(model_dir):
     os.makedirs(model_dir)
   self._output_plot = os.path.join("output", "models", self.name + ".html")
   self._output_midi = os.path.join("output", "models", self.name + ".mid")
   self._plotter = Plotter(plot_max_length_bar=bar_per_loop,
                           live_reload=True,
                           preset_name="PRESET_SMALL")
Ejemplo n.º 2
0
def save_plot(sequences: Union[NoteSequence, List[NoteSequence]],
              output_dir: Optional[str] = None,
              prefix: str = "sequence",
              **kwargs):
    """
  Writes the sequences as HTML plot files to the "output" directory, with the
  filename pattern "<prefix>_<index>_<date_time>" and "html" as extension.

      :param sequences: a NoteSequence or list of NoteSequence to be saved
      :param output_dir: an optional subdirectory in the output directory
      :param prefix: an optional prefix for each file
      :param kwargs: the keyword arguments to pass to the Plotter instance
  """
    output_dir = os.path.join("output", output_dir) if output_dir else "output"
    os.makedirs(output_dir, exist_ok=True)
    if not isinstance(sequences, list):
        sequences = [sequences]
    for (index, sequence) in enumerate(sequences):
        date_and_time = time.strftime("%Y-%m-%d_%H%M%S")
        filename = f"{prefix}_{index:02}_{date_and_time}.html"
        path = os.path.join(output_dir, filename)
        midi = mm.midi_io.note_sequence_to_pretty_midi(sequence)
        plotter = Plotter(**kwargs)
        plotter.save(midi, path)
        print(f"Generated plot file: {os.path.abspath(path)}")
Ejemplo n.º 3
0
def generate_plot_from_midi(sequence, file_name, file_id):
    # Writes the resulting plot file to the output directory
    date_and_time = time.strftime('%Y-%m-%d_%H%M%S')
    plot_filename = "%s_%s_%s.html" % (file_name, file_id, date_and_time)
    plot_path = os.path.join(
        "/Users/reiners/Dropbox/projects/human-drum-grooves/output",
        plot_filename)
    pretty_midi = mm.midi_io.note_sequence_to_pretty_midi(sequence)
    plotter = Plotter()
    plotter.save(pretty_midi, plot_path)
    print(f"Generated plot file: {os.path.abspath(plot_path)}")
Ejemplo n.º 4
0
def convert_midi_to_image(url):
    file = requests.get(url)
    open('input.mid', 'wb').write(file.content)
    pm = PrettyMIDI("input.mid")
    preset = Preset(plot_width=1000, plot_height=500)
    plotter = Plotter(preset=preset)
    plotter.save(pm, 'imageHtml.html')
    hti = Html2Image()
    hti.screenshot(html_file='imageHtml.html',
                   save_as='image.png',
                   size=(1000, 500))
    return discord.File(
        r'image.png') if os.path.getsize('image.png') <= 16000000 else None
def save_plot(sequences: Union[NoteSequence, List[NoteSequence]], output_dir: Optional[str] = None, prefix: str = "sequence", **kwargs):

    output_dir = os.path.join("output", output_dir) if output_dir else "output"
    os.makedirs(output_dir, exist_ok=True)

    if not isinstance(sequences, list):
        sequences = [sequences]

    for (index, sequence) in enumerate(sequences):

        date_time = time.strftime("%Y-%m-%d_%H%M%S")
        filename = f"{prefix}_{index:02}_{date_time}.html"

        path = os.path.join(output_dir, filename)
        midi = mm.midi_io.note_sequence_to_pretty_midi(sequence)

        plotter = Plotter(**kwargs)
        plotter.save(midi, path)
        print("Generated plot : {}".format(os.path.abspath(path)))
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.º 7
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 generate(unused_argv):
    """Generates a basic drum sequence of 4 seconds"""

    # Downloads the bundle from the magenta website, a bundle (.mag file) is a
    # trained model that is used by magenta
    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", this need to fit the bundle we
    # downloaded before
    generator_map = drums_rnn_sequence_generator.get_generator_map()
    generator = generator_map["drum_kit"](checkpoint=None, bundle=bundle)
    generator.initialize()

    # Change the number of steps to be generated, which will define the length
    # of the generated sequence, used with the qpm. At 120 qpm, this
    # is equal to 4 seconds
    num_steps = 32
    # Change the temperature: the bigger this value, the more random is the
    # generation, with 1 being the default value, 1.25 being more random,
    # 0.75 being less random
    temperature = 1
    # Change the quarter per minute (also called BPM or beat per minute), a
    # measure of tempo. At 120 qpm, you have two beat per seconds
    qpm = 120

    # Calculate the number of seconds per step, useful to find the total time
    # of the generation in seconds. The steps per quarter in the generator is by
    # default 4, so the generator will generate 8 steps per bar
    # TODO why 8 steps per bar
    seconds_per_step = 60.0 / qpm / generator.steps_per_quarter
    total_seconds = num_steps * seconds_per_step

    # The generator options are the parameters passed to the generator, why is
    # the temperature and the generation section, from 0 to the total of seconds,
    # where the generator will generate notes
    generator_options = generator_pb2.GeneratorOptions()
    generator_options.args['temperature'].float_value = temperature
    generator_options.generate_sections.add(start_time=0,
                                            end_time=total_seconds)

    # Generates the notes from the arguments, starting with an empty note
    # sequence (no primer)
    empty_note_sequence = music_pb2.NoteSequence()
    sequence = generator.generate(empty_note_sequence, generator_options)

    # Outputs the midi file in the output directory
    midi_file = os.path.join("output", "out.mid")
    mm.midi_io.note_sequence_to_midi_file(sequence, midi_file)
    print("Generated midi file: " + str(os.path.abspath(midi_file)))

    # Outputs the plot file in the output directory and opens your browser for
    # visualisation
    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)

    return 0
def generate(bundle_name: str,
             sequence_generator,
             generator_id: str,
             qpm: float = constants.DEFAULT_QUARTERS_PER_MINUTE,
             primer_filename: str = None,
             condition_on_primer: bool = False,
             inject_primer_during_generation: bool = False,
             total_length_bars: int = 4,
             temperature: float = 1.0,
             beam_size: int = 1,
             branch_factor: int = 1,
             steps_per_iteration: int = 1,
             write_midi_to_disk: bool = False,
             write_plot_to_disk: bool = False) -> music_pb2.NoteSequence:
    """Generates and returns a new sequence given the sequence generator.

  Uses the bundle name to download the bundle in the "bundles" directory if it
  doesn't already exist, then uses the sequence generator and the generator id
  to get the generator. Parameters can be provided for the generation phase.
  The MIDI and plot files can be written to disk.

      :param bundle_name: The bundle name to be downloaded and generated with.

      :param sequence_generator: The sequence generator module, which is the
      python module in the corresponding models subfolder.

      :param generator_id: The id of the generator configuration, this is the
      model's configuration.

      :param qpm: The QPM for the generated sequence. If a primer is provided,
      the primer QPM will be used and this parameter ignored.

      :param primer_filename: The filename for the primer, which will be taken
      from the "primers" directory. If left empty, and empty note sequence will
      be used.

      :param condition_on_primer: https://github.com/tensorflow/magenta/tree/master/magenta/models/polyphony_rnn#generate-a-polyphonic-sequence

      :param inject_primer_during_generation: https://github.com/tensorflow/magenta/tree/master/magenta/models/polyphony_rnn#generate-a-polyphonic-sequence

      :param total_length_bars: The total length of the sequence, which contains
      the added length of the primer and the generated sequence together. This
      value need to be bigger than the primer length in bars.

      :param temperature: The temperature value for the generation algorithm,
      lesser than 1 is less random (closer to the primer), bigger than 1 is
      more random

      :param beam_size: The beam size for the generation algorithm, a bigger
      branch size means the generation algorithm will generate more sequence
      each iteration, meaning a less random sequence at the cost of more time.

      :param branch_factor: The branch factor for the generation algorithm,
      a bigger branch factor means the generation algorithm will keep more
      sequence candidates at each iteration, meaning a less random sequence
      at the cost of more time.

      :param steps_per_iteration: The number of steps the generation algorithm
      generates at each iteration, a bigger steps per iteration meaning there
      are less iterations in total because more steps gets generated each time.

      :param write_midi_to_disk: True to write the resulting sequence to disk as
      a MIDI file in the "output" folder. The filename naming pattern is:
      "GeneratorName_GeneratorId_DateTime.mid".

      :param write_plot_to_disk: True to write the resulting plot to disk as
      a HTML file in the "output" folder. The filename naming pattern is:
      "GeneratorName_GeneratorId_DateTime.html".

      :returns The generated NoteSequence
  """

    # Downloads the bundle from the magenta website, a bundle (.mag file) is a
    # trained model that is used by magenta
    mm.notebook_utils.download_bundle(bundle_name, "bundles")
    bundle = mm.sequence_generator_bundle.read_bundle_file(
        os.path.join("bundles", bundle_name))

    # Initialize the generator from the generator id, this need to fit the
    # bundle we downloaded before, and choose the model's configuration.
    generator_map = sequence_generator.get_generator_map()
    generator = generator_map[generator_id](checkpoint=None, bundle=bundle)
    generator.initialize()

    # Gets the primer sequence that is fed into the model for the generator,
    # which will generate a sequence based on this one.
    # If no primer sequence is given, the primer sequence is initialized
    # to an empty note sequence
    if primer_filename:
        primer_sequence = mm.midi_io.midi_file_to_note_sequence(
            os.path.join("primers", primer_filename))
    else:
        primer_sequence = music_pb2.NoteSequence()

    # Gets the QPM from the primer sequence. If it wasn't provided, take the
    # parameters that defaults to Magenta's default
    if primer_sequence.tempos:
        if len(primer_sequence.tempos) > 1:
            raise Exception("No support for multiple tempos")
        qpm = primer_sequence.tempos[0].qpm
    else:
        qpm = qpm

    # Gets the time signature from the primer sequence. If it wasn't provided,
    # we initialize it to a default 4/4
    if primer_sequence.time_signatures:
        if len(primer_sequence.time_signatures) > 1:
            raise Exception("No support for multiple time signatures")
        primer_time_signature = primer_sequence.time_signatures[0]
    else:
        primer_time_signature = primer_sequence.time_signatures.add()
        primer_time_signature.time = 0
        primer_time_signature.numerator = 4
        primer_time_signature.denominator = 4

    # Calculates the seconds per 1 step, which changes depending on the QPM value
    # (steps per quarter in generators are mostly 4)
    seconds_per_step = 60.0 / qpm / generator.steps_per_quarter

    # Calculate the number of steps per bar, which changes from the time
    # signature. If we have a 3/4 time signature, steps per bar is 12,
    # if 4/4, steps per bar is 16.
    steps_per_quarter_note = constants.DEFAULT_STEPS_PER_QUARTER
    num_steps_per_bar = (primer_time_signature.numerator *
                         steps_per_quarter_note)

    # Calculate how many seconds per bar for the generation time
    seconds_per_bar = num_steps_per_bar * seconds_per_step

    print("Seconds per step: " + str(seconds_per_step))
    print("Steps per bar: " + str(num_steps_per_bar))
    print("Seconds per bar: " + str(seconds_per_bar))

    # Calculates the primer sequence length in bars and time by taking the
    # total time (which is the end of the last note) and finding the next bar
    # start time.
    primer_sequence_length_bars = math.ceil(primer_sequence.total_time /
                                            seconds_per_bar)
    primer_sequence_length_time = primer_sequence_length_bars * seconds_per_bar

    # Calculates the start and the end of the primer sequence.
    # We add a negative delta to the end, because if we don't some generators
    # won't start the generation right at the beginning of the bar, they will
    # start at the next step, meaning we'll have a small gap between the primer
    # and the generated sequence.
    primer_end_adjust = (0.00001 if primer_sequence_length_time > 0 else 0)
    primer_start_time = 0
    primer_end_time = (primer_start_time + primer_sequence_length_time -
                       primer_end_adjust)

    # Calculates the generation time by taking the total time and substracting
    # the primer time. The resulting generation time needs to be bigger than zero.
    generation_length_bars = total_length_bars - primer_sequence_length_bars
    if generation_length_bars <= 0:
        raise Exception("Total length in bars too small " + "(" +
                        str(total_length_bars) + ")" +
                        ", needs to be at least one bar bigger than primer " +
                        "(" + str(primer_sequence_length_bars) + ")")
    generation_length_time = generation_length_bars * seconds_per_bar

    # Calculates the generate start and end time, the start time will contain
    # the previously added negative delta from the primer end time.
    # We remove the generation end time delta to end the generation
    # on the last bar.
    generation_start_time = primer_end_time
    generation_end_time = (generation_start_time + generation_length_time +
                           primer_end_adjust)

    # Showtime
    print("Primer time: [" + str(primer_start_time) + ", " +
          str(primer_end_time) + "]")
    print("Generation time: [" + str(generation_start_time) + ", " +
          str(generation_end_time) + "]")

    # Pass the given parameters, the generator options are common for all models,
    # except for condition_on_primer and no_inject_primer_during_generation
    # which are specific to polyphonic models
    generator_options = generator_pb2.GeneratorOptions()
    generator_options.args['temperature'].float_value = temperature
    generator_options.args['beam_size'].int_value = beam_size
    generator_options.args['branch_factor'].int_value = branch_factor
    generator_options.args[
        'steps_per_iteration'].int_value = steps_per_iteration
    generator_options.args[
        'condition_on_primer'].bool_value = condition_on_primer
    generator_options.args['no_inject_primer_during_generation'].bool_value = (
        not inject_primer_during_generation)
    generator_options.generate_sections.add(start_time=generation_start_time,
                                            end_time=generation_end_time)

    # Generates the sequence. The resulting sequence do not have time signature
    # so we add the same as the primer.
    sequence = generator.generate(primer_sequence, generator_options)
    time_signature = sequence.time_signatures.add()
    time_signature.time = 0
    time_signature.numerator = primer_time_signature.numerator
    time_signature.denominator = primer_time_signature.denominator

    # Writes the resulting midi file to the output directory
    if write_midi_to_disk:
        date_and_time = time.strftime('%Y-%m-%d_%H%M%S')
        generator_name = str(generator.__class__).split(".")[2]
        midi_filename = "%s_%s_%s.mid" % (generator_name, generator_id,
                                          date_and_time)
        midi_path = os.path.join("output", midi_filename)
        mm.midi_io.note_sequence_to_midi_file(sequence, midi_path)
        print("Generated midi file: " + str(os.path.abspath(midi_path)))

    # Writes the resulting plot file to the output directory
    if write_plot_to_disk:
        date_and_time = time.strftime('%Y-%m-%d_%H%M%S')
        generator_name = str(generator.__class__).split(".")[2]
        plot_filename = "%s_%s_%s.html" % (generator_name, generator_id,
                                           date_and_time)
        plot_path = os.path.join("output", plot_filename)
        pretty_midi = mm.midi_io.note_sequence_to_pretty_midi(sequence)
        plotter = Plotter()
        plotter.save(pretty_midi, plot_path)
        print("Generated plot file: " + str(os.path.abspath(plot_path)))

    return sequence
Ejemplo n.º 10
0
def generate(bundle_name: str,
             sequence_generator,
             generator_id: str,
             primer_filename: str = None,
             qpm: float = DEFAULT_QUARTERS_PER_MINUTE,
             total_length_steps: int = 64,
             temperature: float = 1.0,
             beam_size: int = 1,
             branch_factor: int = 1,
             steps_per_iteration: int = 1) -> NoteSequence:
  """Generates and returns a new sequence given the sequence generator.

  Uses the bundle name to download the bundle in the "bundles" directory if it
  doesn't already exist, then uses the sequence generator and the generator id
  to get the generator. Parameters can be provided for the generation phase.
  The MIDI and plot files are written to disk in the "output" folder, with the
  filename pattern "<generator_name>_<generator_id>_<date_time>" with "mid" or
  "html" as extension respectively.

      :param bundle_name: The bundle name to be downloaded and generated with.

      :param sequence_generator: The sequence generator module, which is the
      python module in the corresponding models subfolder.

      :param generator_id: The id of the generator configuration, this is the
      model's configuration.

      :param primer_filename: The filename for the primer, which will be taken
      from the "primers" directory. If left empty, and empty note sequence will
      be used.

      :param qpm: The QPM for the generated sequence. If a primer is provided,
      the primer QPM will be used and this parameter ignored.

      :param total_length_steps: The total length of the sequence, which
      contains the added length of the primer and the generated sequence
      together. This value need to be bigger than the primer length in bars.

      :param temperature: The temperature value for the generation algorithm,
      lesser than 1 is less random (closer to the primer), bigger than 1 is
      more random

      :param beam_size: The beam size for the generation algorithm, a bigger
      branch size means the generation algorithm will generate more sequence
      each iteration, meaning a less random sequence at the cost of more time.

      :param branch_factor: The branch factor for the generation algorithm,
      a bigger branch factor means the generation algorithm will keep more
      sequence candidates at each iteration, meaning a less random sequence
      at the cost of more time.

      :param steps_per_iteration: The number of steps the generation algorithm
      generates at each iteration, a bigger steps per iteration meaning there
      are less iterations in total because more steps gets generated each time.

      :returns The generated NoteSequence
  """

  # Downloads the bundle from the magenta website, a bundle (.mag file) is a
  # trained model that is used by magenta
  mm.notebook_utils.download_bundle(bundle_name, "bundles")
  bundle = mm.sequence_generator_bundle.read_bundle_file(
    os.path.join("bundles", bundle_name))

  # Initialize the generator from the generator id, this need to fit the
  # bundle we downloaded before, and choose the model's configuration.
  generator_map = sequence_generator.get_generator_map()
  generator = generator_map[generator_id](checkpoint=None, bundle=bundle)
  generator.initialize()

  # Gets the primer sequence that is fed into the model for the generator,
  # which will generate a sequence based on this one.
  # If no primer sequence is given, the primer sequence is initialized
  # to an empty note sequence
  if primer_filename:
    primer_sequence = mm.midi_io.midi_file_to_note_sequence(
      os.path.join("primers", primer_filename))
  else:
    primer_sequence = NoteSequence()

  # Gets the QPM from the primer sequence. If it wasn't provided, take the
  # parameters that defaults to Magenta's default
  if primer_sequence.tempos:
    if len(primer_sequence.tempos) > 1:
      raise Exception("No support for multiple tempos")
    qpm = primer_sequence.tempos[0].qpm

  # Calculates the seconds per 1 step, which changes depending on the QPM value
  # (steps per quarter in generators are mostly 4)
  seconds_per_step = 60.0 / qpm / getattr(generator, "steps_per_quarter", 4)

  # Calculates the primer sequence length in steps and time by taking the
  # total time (which is the end of the last note) and finding the next step
  # start time.
  primer_sequence_length_steps = math.ceil(primer_sequence.total_time
                                           / seconds_per_step)
  primer_sequence_length_time = primer_sequence_length_steps * seconds_per_step

  # Calculates the start and the end of the primer sequence.
  # We add a negative delta to the end, because if we don't some generators
  # won't start the generation right at the beginning of the bar, they will
  # start at the next step, meaning we'll have a small gap between the primer
  # and the generated sequence.
  primer_end_adjust = (0.00001 if primer_sequence_length_time > 0 else 0)
  primer_start_time = 0
  primer_end_time = (primer_start_time
                     + primer_sequence_length_time
                     - primer_end_adjust)

  # Calculates the generation time by taking the total time and substracting
  # the primer time. The resulting generation time needs to be bigger than zero.
  generation_length_steps = total_length_steps - primer_sequence_length_steps
  if generation_length_steps <= 0:
    raise Exception("Total length in steps too small "
                    + "(" + str(total_length_steps) + ")"
                    + ", needs to be at least one bar bigger than primer "
                    + "(" + str(primer_sequence_length_steps) + ")")
  generation_length_time = generation_length_steps * seconds_per_step

  # Calculates the generate start and end time, the start time will contain
  # the previously added negative delta from the primer end time.
  # We remove the generation end time delta to end the generation
  # on the last bar.
  generation_start_time = primer_end_time
  generation_end_time = (generation_start_time
                         + generation_length_time
                         + primer_end_adjust)

  # Showtime
  print(f"Primer time: [{primer_start_time}, {primer_end_time}]")
  print(f"Generation time: [{generation_start_time}, {generation_end_time}]")

  # Pass the given parameters, the generator options are common for all models
  generator_options = GeneratorOptions()
  generator_options.args['temperature'].float_value = temperature
  generator_options.args['beam_size'].int_value = beam_size
  generator_options.args['branch_factor'].int_value = branch_factor
  generator_options.args['steps_per_iteration'].int_value = steps_per_iteration
  generator_options.generate_sections.add(
    start_time=generation_start_time,
    end_time=generation_end_time)

  # Generates the sequence, add add the time signature
  # back to the generated sequence
  sequence = generator.generate(primer_sequence, generator_options)

  # Writes the resulting midi file to the output directory
  date_and_time = time.strftime('%Y-%m-%d_%H%M%S')
  generator_name = str(generator.__class__).split(".")[2]
  midi_filename = "%s_%s_%s.mid" % (generator_name, generator_id,
                                    date_and_time)
  midi_path = os.path.join("output", midi_filename)
  mm.midi_io.note_sequence_to_midi_file(sequence, midi_path)
  print(f"Generated midi file: {os.path.abspath(midi_path)}")

  # Writes the resulting plot file to the output directory
  date_and_time = time.strftime('%Y-%m-%d_%H%M%S')
  generator_name = str(generator.__class__).split(".")[2]
  plot_filename = "%s_%s_%s.html" % (generator_name, generator_id,
                                     date_and_time)
  plot_path = os.path.join("output", plot_filename)
  pretty_midi = mm.midi_io.note_sequence_to_pretty_midi(sequence)
  plotter = Plotter()
  plotter.save(pretty_midi, plot_path)
  print(f"Generated plot file: {os.path.abspath(plot_path)}")

  return sequence
def generate(unused_argv):
    """Generates a basic drum sequence of 4 seconds based on a hard coded
  primer"""

    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()

    num_steps = 32
    temperature = 1
    qpm = 120

    seconds_per_step = 60.0 / qpm / generator.steps_per_quarter
    total_seconds = num_steps * 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)
    # TODO why quarter
    # TODO better example
    # TODO check midi note meaning
    primer_drums = magenta.music.DrumTrack([
        frozenset(pitches)
        for pitches in [(36, ), (), (), (), (36, 41), (), (), ()]
    ])
    primer_sequence = primer_drums.to_sequence(qpm=120)
    primer_end_time = primer_sequence.total_time

    # 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
    generation_start_time = primer_end_time + seconds_per_step
    generation_end_time = total_seconds

    generator_options = generator_pb2.GeneratorOptions()
    generator_options.args['temperature'].float_value = temperature
    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)

    midi_file = os.path.join("output", "out.mid")
    mm.midi_io.note_sequence_to_midi_file(sequence, midi_file)
    print("Generated midi file: " + str(os.path.abspath(midi_file)))

    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)

    return 0
Ejemplo n.º 12
0
from visual_midi import Plotter
import magenta.music as mm
import os
import sys
import glob

f = '*.mid'
d = os.getcwd()
for file in glob.glob(os.path.join(d, f)):
    sequence = mm.midi_io.midi_file_to_note_sequence(
        os.path.join("simplemidi", file))
    plot_filename = "%s.html" % (file)
    plot_path = os.path.join("output", plot_filename)
    pretty_midi = mm.midi_io.note_sequence_to_pretty_midi(sequence)
    plotter = Plotter()
    plotter.show(pretty_midi, plot_path)
    plotter.save(pretty_midi, plot_path)
    print("Generated plot file: " + str(os.path.abspath(plot_path)))
Ejemplo n.º 13
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.º 14
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 generate(unused_argv):
    # Downloads the bundle from the magenta website, a bundle (.mag file) is a
    # trained model that is used by magenta
    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", this need to fit the bundle we
    # downloaded before
    generator_map = drums_rnn_sequence_generator.get_generator_map()
    generator = generator_map["drum_kit"](checkpoint=None, bundle=bundle)
    generator.initialize()

    # We will generate 3 bars, so with a
    # 1 bar primer we'll have 4 bars total
    num_bars = 3
    qpm = 120

    # The steps per quarter for this generator
    # is 4 steps per quarter
    seconds_per_step = 60.0 / qpm / generator.steps_per_quarter

    # We are using a default 16 steps per bar, which is
    # 4/4 music sampled at 4 steps per quarter note
    num_steps_per_bar = constants.DEFAULT_STEPS_PER_BAR

    # We calculate how many seconds per bar for
    # the generation time
    seconds_per_bar = num_steps_per_bar * seconds_per_step

    print(f"Seconds per step: {seconds_per_step}")
    print(f"Seconds per bar: {seconds_per_bar}")

    # 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)

    # We store those time because the generation
    # will start after the end of the primer
    primer_start_time = 0
    primer_end_time = primer_start_time + seconds_per_bar

    # We calculate the generation start and end
    # for a duration of num_bars
    generation_start_time = primer_end_time
    generation_end_time = generation_start_time + (seconds_per_bar * num_bars)

    print(f"Primer start and end:" f"[{primer_start_time}, {primer_end_time}]")
    print(f"Generation start and end:"
          f"[{generation_start_time}, {generation_end_time}]")

    # The generator interface is common for all models
    generator_options = generator_pb2.GeneratorOptions()

    # Add a bit of temperature for more flavor
    temperature = 1.1
    print(f"Temperature: {temperature}")
    generator_options.args['temperature'].float_value = temperature

    # Defines the generation section
    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,
    # the resulting sequence is a NoteSequence instance
    sequence = generator.generate(primer_sequence, generator_options)

    # Write the resulting midi file to the output directory
    midi_file = os.path.join("output", "out.mid")
    mm.midi_io.note_sequence_to_midi_file(sequence, midi_file)
    print(f"Generated midi file: {os.path.abspath(midi_file)}")

    # Write the resulting plot file to the output directory
    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)}")

    return 0
Ejemplo n.º 16
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
Ejemplo n.º 17
0
 def _save_plot(sequence, path):
     midi = mm.midi_io.note_sequence_to_pretty_midi(sequence)
     plotter = Plotter(**plot_kwargs)
     plotter.save(midi, path)
     logger.info(
         self._logging_format.format(file_type="plot", path=path))
Ejemplo n.º 18
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.º 19
0
class SequenceLooper(threading.Thread):
  def __init__(self,
               name: str,
               bar_start_event,
               action_server: ActionServer,
               midi_hub,
               bundle_name: str,
               config_name: str,
               timing: Timing,
               midi_channel: int = 1,
               bar_per_loop: int = 2):
    super(SequenceLooper, self).__init__()
    self.name = name
    self._stop_signal = False
    self._bar_start_event = bar_start_event
    self._action_server = action_server
    self._midi_hub = midi_hub
    self._bundle_filename = bundle_name + ".mag"
    self._config_name = config_name
    self._timing = timing
    self._midi_channel = midi_channel
    self._bar_per_loop = bar_per_loop
    model_dir = os.path.join("output", "models")
    if not os.path.exists(model_dir):
      os.makedirs(model_dir)
    self._output_plot = os.path.join("output", "models", self.name + ".html")
    self._output_midi = os.path.join("output", "models", self.name + ".mid")
    self._plotter = Plotter(plot_max_length_bar=bar_per_loop,
                            live_reload=True,
                            preset_name="PRESET_SMALL")

  def stop(self):
    self._stop_signal = True

  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