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