Lectura y escritura de música por frecuencia y tiempo
Presentación
Este proyecto nace a partir de la lectura del Articulo publicado por Kiko Correoso en la pagina Haciendo música con python que me dio la idea de diseñar un programa que permita a traves de la lectura de un archivo que contiene los valores de frecuencia, tiempo y escala repoducir música, como asi tambien escribir estos archivos.
El programa que aca pongo a consideración lee un archivo de texto donde se han escrito en cada linea los valores de la frecuencia, tiempo y escala separados por comas de las notas musicales que se van emitiendo secuencialmente.
El funcionamiento es bien intuitivo iniciado el programa aparece la siguiente pantalla:
En ella debemos elegir el boton "Seleccionar Tema" y se desplegara la siguiente ventana:
Que nos muestra todos los archivos txt que hay en el directorio donde esta el programa, alli seleccionaremos el archivo que deseamos que sea leido/reproducido y se desplegara la siguiente ventana:
Al hacer clic en "Ejecutar Tema" se iniciara la reproducción del tema mientras vemos moverse la imagen de una bailarina. al terminar el tema regresa a la pantala inicial.
Para escribir un nuevo tema, en la pantalla inicial elegimos el boton "Agregar Tema" con lo que se nos presentara la siguiente ventana:
En ella podemos ir ingresando una por una las notas del tema y al hacer clic en "Guardar" se iran escribiendo en un archivo llamado tema.txt. Debe tenerse en cuenta que la cuarta octava se corresponde con las notas de la clave de sol.
Para terminar solo se debe salir y volveremos a la pagina inicial.
En virtud que siempre las nuevas notas musicales se agregaran al archivo tema.txt resulta necesario cuando terminamos de escribir un tema musical renombrar el archivo.
Código
El código es el siguiente.
import tkinter as tk
from tkinter import filedialog
#from tkinter import *
from tkinter import ttk
import numpy as np
import sounddevice as sd
from PIL import Image, ImageTk
import sys
def botonera():
for widget in ventana.winfo_children():
widget.destroy()
boton_seleccionar = tk.Button(ventana, text="Seleccionar Tema", command=seleccionar_tema)
boton_seleccionar.configure(width=30, height=1, bg="#FDEDEC", font="Arial 14 bold italic", fg="#C0392B")
boton_seleccionar.place(x=150, y=100)
boton_agregar = tk.Button(ventana, text="Agregar Tema", command=cargar_temas)
boton_agregar.configure(width=30, height=1, bg="#FDEDEC", font="Arial 14 bold italic", fg="#C0392B")
boton_agregar.place(x=150, y=200)
boton_salir = tk.Button(ventana, text="Salir", command=salir)
boton_salir.configure(width=30, height=1, bg="#76D7C4", font="Arial 14 bold italic", fg="#229954")
boton_salir.place(x=150, y=300)
#Salida del programa
def salir():
sys.exit()
#Definir nota
def frec(nota: int, octava: int) -> int:
expo = octava * 12 + (nota - 58)
return int(640 * ((2 ** (1 / 12)) ** expo))
#Ejecutar nota
def beep(nota: int, octava: int, duracion: int) -> None:
cuadros = 44100
t = np.linspace(0, duracion / 1000, int(cuadros * duracion / 1000))
frecuencia = frec(nota, octava)
data = np.sin(2 * np.pi * frecuencia * t)
sd.play(data, cuadros)
sd.wait()
#Seleccionar archivo a ejecutar
def seleccionar_tema():
global temas
temas = filedialog.askopenfilename(filetypes=[("Temas Musicales", "*.txt")])
for widget in ventana.winfo_children():
widget.destroy()
eti_tema = tk.Label(ventana, text="Tema Musical")
eti_tema.configure(width=15, height=1, bg="#FDEDEC", font="Arial 14 bold italic", fg="#EC7063")
eti_tema.place(x=100, y=50)
titulo = temas.split("/")
x = len(titulo) - 1
cont_titulo = tk.Label(ventana, text=titulo[x][:-4])
cont_titulo.configure(width=30, height=1, bg="#FDEDEC", font="Arial 14 bold italic", fg="#C0392B")
cont_titulo.place(x=250, y=50)
boton_ejecutar = tk.Button(ventana, text="Ejecutar Tema", command=ejecutar_tema)
boton_ejecutar.configure(width=30, height=1, bg="#FDEDEC", font="Arial 14 bold italic", fg="#C0392B")
boton_ejecutar.place(x=150, y=200)
boton_salir = tk.Button(ventana, text="Salir", command=salir)
boton_salir.configure(width=30, height=1, bg="#76D7C4", font="Arial 14 bold italic", fg="#229954")
boton_salir.place(x=150, y=300)
#Ejecutar sonidos del archivo
def ejecutar_tema():
for widget in ventana.winfo_children():
widget.destroy()
eti_tema = tk.Label(ventana, text="Tema Musical")
eti_tema.configure(width=15, height=1, bg="#FDEDEC", font="Arial 14 bold italic", fg="#EC7063")
eti_tema.place(x=100, y=50)
titulo = temas.split("/")
x = len(titulo) - 1
cont_titulo = tk.Label(ventana, text=titulo[x][:-4])
cont_titulo.configure(width=30, height=1, bg="#FDEDEC", font="Arial 14 bold italic", fg="#C0392B")
cont_titulo.place(x=250, y=50)
# Cargar los cuadros del GIF de la imagen a mostrar
gif = Image.open("musica.gif")
cuadros = []
try:
while True:
cuadros.append(ImageTk.PhotoImage(gif))
gif.seek(len(cuadros))
except EOFError:
pass
# Mostrar la imagen en un canvas
canvas = tk.Canvas(ventana, width=200, height=200)
canvas.place(x=200, y=100)
canvas.create_image(0, 0, image=cuadros[0], anchor=tk.NW)
x = 0
with open(temas, 'r') as musica:
notas = musica.readlines()
for nota in notas:
x = x + 1
valores = nota.split(",")
beep(int(valores[0]), int(valores[1]), int(valores[2]))
print(valores[0])
print(valores[1])
print(valores[2])
# Actualizar el cuadro del GIF en el canvas
canvas.itemconfig(1, image=cuadros[x])
ventana.update()
if x == 9:
x = -1
botonera()
def cargar_temas():
global entrada_octava
global lista_notas
global lista_tipos
for widget in ventana.winfo_children():
widget.destroy()
# Etiqueta para la selección de la nota musical
etiqueta_notas = tk.Label(ventana, text="Nota musical:")
etiqueta_notas.configure(height=1, bg="#E67E22", font="Arial 14", fg="#7B241C")
etiqueta_notas.place(x = 35, y = 50)
# Lista de selección múltiple para la nota musical
notas = ["Do", "Re", "Mi", "Fa", "Sol", "La", "Si"]
lista_notas = ttk.Combobox(ventana)
lista_notas['values'] = notas
lista_notas.place(x = 150, y = 50, width=50, height=26)
# Etiqueta para la selección del tipo de nota
etiqueta_tipos = tk.Label(ventana, text="Tipo de nota:")
etiqueta_tipos.configure(height=1, bg="#E67E22", font="Arial 14", fg="#7B241C")
etiqueta_tipos.place(x = 210, y = 50)
# Lista de selección múltiple para el tipo de nota
tipos = ["Redonda", "Blanca", "Negra", "Corchea", "Semicorchea", "Fusa", "Semifusa"]
lista_tipos = ttk.Combobox(ventana)
lista_tipos['values'] = tipos
lista_tipos.place(x = 320, y = 50, width=100, height=26)
# Etiqueta y entrada para ingresar la octava
etiqueta_octava = tk.Label(ventana, text="Octava:")
etiqueta_octava.configure(height=1, bg="#E67E22", font="Arial 14", fg="#7B241C")
etiqueta_octava.place(x = 430, y = 50)
entrada_octava = tk.Entry(ventana)
entrada_octava.insert(0, "4")
entrada_octava.place(x = 490, y = 50, height=26, width=30)
# Botón para guardar la información
boton_guardar = tk.Button(ventana, text="Guardar", command=guardar_informacion)
boton_guardar.configure(width=30, height=1, bg="#FDEDEC", font="Arial 14 bold italic", fg="#C0392B")
boton_guardar.place(x = 150, y = 150)
boton_salir = tk.Button(ventana, text="Salir", command=botonera)
boton_salir.configure(width=30, height=1, bg="#76D7C4", font="Arial 14 bold italic", fg="#229954")
boton_salir.place(x=150, y=300)
def guardar_informacion():
val_nota= ["1", "3", "5", "6", "8", "10", "12"]
val_fig = ["4000", "2000", "1000", "500", "250", "125", "62"]
octava = entrada_octava.get()
sel_nota = lista_notas.get()
ind_nota = lista_notas.current()
sel_tipo = lista_tipos.get()
ind_tipo = lista_tipos.current()
archivo = open("tema.txt", "a")
archivo.write(f"{val_nota[ind_nota]}, {octava}, {val_fig[ind_tipo]}\n")
archivo.close()
print(sel_nota)
print(val_nota[ind_nota])
print(sel_tipo)
print(val_fig[ind_tipo])
print(octava)
# Reiniciar los campos
lista_notas.set('')
lista_tipos.set('')
entrada_octava.delete(0, tk.END)
entrada_octava.insert(0, "4")
ventana = tk.Tk()
ventana.title("Lector de música")
ventana.geometry('600x400')
ventana.configure(bg='#A2D9CE')
botonera()
ventana.mainloop()
Un ejemplo de los archivos de texto que el programa lee, es el siguiente con la melodía del Feliz Cumpleaños.
8,4,500
8,4,500
10,4,500
8,4,500
1,5,500
12,4,1000
8,4,500
8,4,500
10,4,500
8,4,500
3,5,500
1,5,1000
1,5,250
5,5,250
8,5,500
5,5,500
1,5,500
12,4,500
10,4,500
6,5,250
6,5,250
5,5,500
1,5,500
3,5,500
Instalacion del programa
Para instalar el programa se debe tener instalado Python 3, y en una carpeta se debe colocar un archivo con extensión "*.py" que contenga el código
En la misma capeta se debe descargar el archivo *.gif de la presentación del programa y renombrarlo como musica.gif
Finalmente en la misma carpeta se creara un archivo con el nombre "Feliz Cumpleaños.txt" en el que se copiara el ejemplo transcrito mas arriba
Como siempre espero que el proyecto sea de interes y quedo a disposición para comentarios y consultas en mi correo electronico carlosvaccaro1960@gmail.com