Spaces:
Sleeping
Sleeping
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 obtain_statistics(midi_file: str) -> pd.DataFrame: | |
# Convertir archivo MIDI a DataFrame de notas | |
notes_df = midi_to_notes(midi_file) | |
# Calcular estadísticas | |
mean_pitch = np.mean(notes_df['pitch']) | |
std_pitch = np.std(notes_df['pitch']) | |
median_pitch = np.median(notes_df['pitch']) | |
mean_step = np.mean(notes_df['step']) | |
std_step = np.std(notes_df['step']) | |
median_step = np.median(notes_df['step']) | |
mean_duration = np.mean(notes_df['duration']) | |
std_duration = np.std(notes_df['duration']) | |
median_duration = np.median(notes_df['duration']) | |
# Crear DataFrame con estadísticas | |
statistics_data = { | |
'Estadísticas': ['Media', 'Desviación Estandar', 'Mediana'], | |
'Tono': [mean_pitch, std_pitch, median_pitch], | |
'Paso': [mean_step, std_step, median_step], | |
'Duración': [mean_duration, std_duration, median_duration] | |
} | |
statistics_df = pd.DataFrame(statistics_data) | |
return statistics_df | |
def main(): | |
seed = 42 | |
tf.random.set_seed(seed) | |
np.random.seed(seed) | |
# Rutas de archivos | |
#sample_file = 'Preludes 2 Through Major keys 39.mid' | |
st.title('GENERADOR DE MELODIAS CON RNN') | |
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 qué modelo entrenar", | |
("Maestro", "Lakh") | |
) | |
option_musica = st.selectbox( | |
"Elige instrumento para generar las melodías", | |
("Piano", "Percusión cromática", "Organo", "Guitarra", "Bajo", "Instrumentos de cuerda", "Conjunto", "Laton", | |
"Junco", "Pipa", "Instrumento sintetizador", "Pad sintetizador", "Efecto sintetizador", "Etnico", "Percusion", "Efectos de sonido") | |
) | |
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") | |
with st.container(height = None, border = True): | |
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=="Percusión cromática": | |
instrument_name="Celesta" | |
elif option_musica=="Organo": | |
instrument_name="Hammond Organ" | |
elif option_musica=="Guitarra": | |
instrument_name="Acoustic Guitar (nylon)" | |
elif option_musica=="Bajo": | |
instrument_name="Acoustic Bass" | |
elif option_musica=="Instrumentos de cuerda": | |
instrument_name="Violin" | |
elif option_musica=="Conjunto": | |
instrument_name="String Ensemble 1" | |
elif option_musica=="Laton": | |
instrument_name="Trumpet" | |
elif option_musica=="Junco": | |
instrument_name="Soprano Sax" | |
elif option_musica=="Pipa": | |
instrument_name="Piccolo" | |
elif option_musica=="Instrumento sintetizador": | |
instrument_name="Lead 2 (sawtooth)" | |
elif option_musica=="Pad sintetizador": | |
instrument_name="Pad 2 (warm)" | |
elif option_musica=="Efecto sintetizador": | |
instrument_name="FX 2 (soundtrack)" | |
elif option_musica=="Etnico": | |
instrument_name="Banjo" | |
elif option_musica=="Percusion": | |
instrument_name="Melodic Tom" | |
elif option_musica=="Efectos de sonido": | |
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() | |
csv_file = obtain_statistics(out_file) | |
# Guardar el archivo temporalmente | |
out_file = 'temp.mid' | |
with open(out_file, 'wb') as f: | |
f.write(uploaded_file.getbuffer()) | |
# Obtener estadísticas | |
statistics_df = obtain_statistics(out_file) | |
# Mostrar estadísticas en Streamlit | |
st.write("### Estadísticas generadas:") | |
st.dataframe(statistics_df) | |
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' | |
) | |
# st.download_button( | |
# label="Descargar CSV", | |
# data=statistics_df.to_csv(index=False).encode('utf-8'), | |
# file_name='statistics.csv', | |
# mime='text/csv' | |
# ) | |
# 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() |