import collections #import datetime #import glob import numpy as np #import pathlib import pandas as pd import pretty_midi import seaborn as sns from matplotlib import pyplot as plt from typing import Optional import tensorflow as tf import keras from tensorflow.keras.utils import custom_object_scope import streamlit as st from midi2audio import FluidSynth import tempfile import os import base64 def midi_to_notes(midi_file: str) -> pd.DataFrame: pm = pretty_midi.PrettyMIDI(midi_file) instrument = pm.instruments[0] notes = collections.defaultdict(list) sorted_notes = sorted(instrument.notes, key=lambda note: note.start) prev_start = sorted_notes[0].start for note in sorted_notes: start = note.start end = note.end notes['pitch'].append(note.pitch) notes['start'].append(start) notes['end'].append(end) notes['step'].append(start - prev_start) notes['duration'].append(end - start) prev_start = start return pd.DataFrame({name: np.array(value) for name, value in notes.items()}) def notes_to_midi( notes: pd.DataFrame, out_file: str, instrument_name: str, velocity: int = 100, ) -> pretty_midi.PrettyMIDI: pm = pretty_midi.PrettyMIDI() instrument = pretty_midi.Instrument( program=pretty_midi.instrument_name_to_program( instrument_name)) prev_start = 0 for i, note in notes.iterrows(): start = float(prev_start + note['step']) end = float(start + note['duration']) note = pretty_midi.Note( velocity=velocity, pitch=int(note['pitch']), start=start, end=end, ) instrument.notes.append(note) prev_start = start pm.instruments.append(instrument) pm.write(out_file) return pm def plot_roll(notes: pd.DataFrame, count: Optional[int] = None): if count: title = f'First {count} notes' else: title = f'Whole track' count = len(notes['pitch']) plt.figure(figsize=(20, 4)) plot_pitch = np.stack([notes['pitch'], notes['pitch']], axis=0) plot_start_stop = np.stack([notes['start'], notes['end']], axis=0) plt.plot( plot_start_stop[:, :count], plot_pitch[:, :count], color="b", marker=".") plt.xlabel('Time [s]') plt.ylabel('Pitch') _ = plt.title(title) def plot_distributions(notes: pd.DataFrame, drop_percentile=2.5): plt.figure(figsize=[15, 5]) plt.subplot(1, 3, 1) sns.histplot(notes, x="pitch", bins=20) plt.subplot(1, 3, 2) max_step = np.percentile(notes['step'], 100 - drop_percentile) sns.histplot(notes, x="step", bins=np.linspace(0, max_step, 21)) def predict_next_note( notes: np.ndarray, model: tf.keras.Model, temperature: float = 1.0) -> tuple[int, float, float]: assert temperature > 0 inputs = tf.expand_dims(notes, 0) predictions = model.predict(inputs) pitch_logits = predictions['pitch'] step = predictions['step'] duration = predictions['duration'] pitch_logits /= temperature pitch = tf.random.categorical(pitch_logits, num_samples=1) pitch = tf.squeeze(pitch, axis=-1) duration = tf.squeeze(duration, axis=-1) step = tf.squeeze(step, axis=-1) step = tf.maximum(0, step) duration = tf.maximum(0, duration) return int(pitch), float(step), float(duration) def mse_with_positive_pressure(y_true: tf.Tensor, y_pred: tf.Tensor): mse = (y_true - y_pred) ** 2 positive_pressure = 10 * tf.maximum(-y_pred, 0.0) return tf.reduce_mean(mse + positive_pressure) def calcular_duracion_midi(archivo_midi): midi = pretty_midi.PrettyMIDI(archivo_midi) return midi.get_end_time() def main(): seed = 42 tf.random.set_seed(seed) np.random.seed(seed) st.title('GENERADOR DE MELODIAS CON RNN') # Rutas de archivos #sample_file = 'Preludes 2 Through Major keys 39.mid' out_file = 'output.mid' uploaded_file = st.file_uploader("Sube un archivo MIDI") model='' pesos='' with st.container(height = None, border = True): option = st.selectbox( "Elige con que modelo entrenar", ("Maestro", "Lakh")) option_musica = st.selectbox( "Elige instrumento a generar las melodias", ("Piano", "Chromatic Percussion", "Organ", "Guitar", "Bass", "Strings", "Ensemble", "Brass", "Reed", "Pipe", "Synth Lead", "Synth Pad", "Synth Effects", "Ethnic", "Percussive", "Sound Effects")) num_predictions = st.number_input("Ingrese el número de notas:", min_value=100, max_value=150, value=120, step=1) if uploaded_file and option is not None: if option=="Maestro": model="mi_modelo_music.h5" pesos="mi_pesos_music.h5" else: model="mi_modelo03_music.h5" pesos="mi_pesos03_music.h5" st.subheader("Archivo cargado:") st.write(uploaded_file.name) # Guardar el archivo en una ubicación temporal with open(uploaded_file.name, 'wb') as f: f.write(uploaded_file.getbuffer()) sample_file=uploaded_file.name # Duracion del MIDI duracion = calcular_duracion_midi(sample_file) minutos, segundos = divmod(duracion, 60) st.write(f"La duración del archivo MIDI subido es: {int(minutos)}:{int(segundos):02d}") st.subheader("Modelo elegido:") with st.container(height = None, border = True): st.write(option, f" de tipo instrumental ", option_musica) # Cargar modelo y pesos with custom_object_scope({'mse_with_positive_pressure': mse_with_positive_pressure}): model = keras.models.load_model(model) model.load_weights(pesos, skip_mismatch=False, by_name=False, options=None) # Convertir MIDI generado por el modelo a archivo WAV pm = pretty_midi.PrettyMIDI(sample_file) instrument_name = "" if option_musica is not None: if option_musica=="Piano": instrument_name="Acoustic Grand Piano" elif option_musica=="Chromatic Percussion": instrument_name="Celesta" elif option_musica=="Organ": instrument_name="Hammond Organ" elif option_musica=="Guitar": instrument_name="Acoustic Guitar (nylon)" elif option_musica=="Bass": instrument_name="Acoustic Bass" elif option_musica=="Strings": instrument_name="Violin" elif option_musica=="Ensemble": instrument_name="String Ensemble 1" elif option_musica=="Brass": instrument_name="Trumpet" elif option_musica=="Reed": instrument_name="Soprano Sax" elif option_musica=="Pipe": instrument_name="Piccolo" elif option_musica=="Synth Lead": instrument_name="Lead 2 (sawtooth)" elif option_musica=="Synth Pad": instrument_name="Pad 2 (warm)" elif option_musica=="Synth Effects": instrument_name="FX 2 (soundtrack)" elif option_musica=="Ethnic": instrument_name="Banjo" elif option_musica=="Percussive": instrument_name="Melodic Tom" elif option_musica=="Sound Effects": instrument_name="Guitar Fret Noise" else: instrument_name=pretty_midi.program_to_instrument_name(pm.instruments[0].program) raw_notes = midi_to_notes(sample_file) key_order = ['pitch', 'step', 'duration'] seq_length = 25 vocab_size = 128 temperature = 2.0 sample_notes = np.stack([raw_notes[key] for key in key_order], axis=1) input_notes = (sample_notes[:seq_length] / np.array([vocab_size, 1, 1])) generated_notes = [] prev_start = 0 for _ in range(num_predictions): pitch, step, duration = predict_next_note(input_notes, model, temperature) start = prev_start + step end = start + duration input_note = (pitch, step, duration) generated_notes.append((*input_note, start, end)) input_notes = np.delete(input_notes, 0, axis=0) input_notes = np.append(input_notes, np.expand_dims(input_note, 0), axis=0) prev_start = start generated_notes = pd.DataFrame( generated_notes, columns=(*key_order, 'start', 'end')) notes_to_midi( generated_notes, out_file=out_file, instrument_name=instrument_name) # Interfaz de Streamlit st.title("Generador de notas musicales") archivo_midi = open(out_file, 'rb').read() with st.container(height = None, border = True): st.download_button( label="Descargar MIDI", data=archivo_midi, file_name=out_file, # Nombre del archivo que se descargará mime='audio/midi' ) # Duracion del MIDI resultante duracion_f = calcular_duracion_midi(out_file) minutos_f, segundos_f = divmod(duracion_f, 60) st.write(f"La duración del archivo MIDI resultante es: {int(minutos_f)}:{int(segundos_f):02d}") if __name__ == "__main__": main()