visakh7843's picture
Changed midi2audio to timidity,reduces depedencies
d020229
raw
history blame
6.78 kB
from distutils.log import debug
import os, sys
import random
import datetime
import glob
# from xml.dom.minidom import Document
import markov
import pickle
import subprocess
import gradio as gr
import time
#TODO: convert these into inputs
# lengthofsong = 10 Should we control this? Setting it to random now
timesignature = ['3/4','4/4','1/8','C|'] #Sometimes the letter “C” (meaning common time) will be used in place of 4/4.
#Both C and 4/4 indicate that there are four quarter note beats in each measure.
keysignature = ["C","G","D","No selection"]
difficulty = ["beginner","intermediate","expert"]
key_enforced = False
key_enforced = True #Set to true if user wants in specific key
# get the list of filenames (abc files downloaded from http://www.norbeck.nu/abc/)
# getdirs = []
# dirs = ["hn201612/i/*.abc", "hn201612/s/*.abc"]
# dirs = ["data/*.abc"]
# dirs = ["data"]
# for dir1 in dirs:
# for filename in glob.iglob(dir1):
# getdirs += [filename]
selected_timeSign = '3/4' #Default values
selected_keySign = 'C' #Default Values
deployed = True
#Finds all absolute paths in directory
#https://stackoverflow.com/questions/9816816/get-absolute-paths-of-all-files-in-a-directory
def abs_paths(dir):
for dir_path,_,filenames in os.walk(dir):
for f in filenames:
yield os.path.abspath(os.path.join(dir_path, f))
def music_gen(difficulty,time_Signature, Key_Signature):
if deployed:
#delete all files stored in gen_songs_abc
command = "rm -r gen_songs_abc/*"
subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE).communicate()
corpus = []
song = []
selected_timeSign = time_Signature
selected_keySign = Key_Signature
data_path = "data/"+str(difficulty)
# ex_filename = "hn201612/i/hnsong1.abc"
# parsing on file to extract songs and add them to corpus
for filename in abs_paths(data_path):
with open(filename) as f:
lines = f.readlines()
last = len(lines)
accepted = False
for index, line in enumerate(lines):
if (line.find("|") < 0 and index - 1 == last):
# if the next line does not have pipes add song to corpus and then set song variable empty again
if accepted and key_enforced and key_accepted:
corpus.append(song)
accepted = False
key_accepted = False
if accepted:
corpus.append(song)
accepted = False
song = []
else:
if line.find("|") > -1:
# a line should be split on "|" and copied to the corpus if it has pipes
sline = line.split("|")
# add the list of measures to the song
song += [x.strip("\r\n") for x in sline if len(x.strip("\r\n")) > 0]
last = index
elif "M:" in line:
#time signature
if selected_timeSign == "4/4":
if "4/4" in line or "C|" in line:
accepted = True
elif selected_timeSign in line:
accepted = True
elif line.find("K:") and key_enforced:
#key signature
if selected_keySign in line:
key_accepted = True
print("Training on {} songs...".format(len(corpus)))
# MARKOV PART
# n-gram length for markov model
n = 1
model = markov.generate_model_from_token_lists(corpus, n)
# save pickle
# with open('markov_chain.pickle', 'wb') as handle:
# pickle.dump(model, handle)
def nextword(word):
return markov.generate(model, 3, seed=word, max_iterations=1)
def writesong(songlength, first):
song = [first]
for i in range(songlength):
song += nextword(str(song[-1]))
return song
# choose a random song length from list of song lengths in corpus
lengthofsong = random.choice([len(x) for x in corpus if len(x) > 10])
print("Song length will be {}".format(lengthofsong))
song_len = [len(x) for x in corpus if len(x)>10]
song_len.sort()
print("Song lengths",song_len)
firstnote = markov.generate(model, n, max_iterations=3)[0]
# print "first note: {}".format(firstnote)
print("Here is the song in abc format:")
song = writesong(lengthofsong, firstnote)
dob = datetime.datetime.now().strftime('%H%M%S')
print(dob)
print(song)
modifier = format(dob)
path = "gen_songs_abc/song_"+modifier
# make song file
# songname = "./gen_songs_abc/gen_song_{}.abc".modifier
song_path = path+"/gen_song_"+modifier #without extension
songname = path+"/gen_song_"+modifier+".abc"
print("\n\nYou can find the song in {}".format(songname))
lastpart = lengthofsong - lengthofsong%4
# hack to include dictionary at the beginning of every abc file
# will add a more sophisticated way to generate the values in the future
title = "Markov Song {}".format(dob)
songbeginning = ['X:1','T:' + title, 'R:song', 'C:Visakh Ajith', 'Z:id:hn-song-111', 'M:3/4', 'L:1/8', 'Q:1/4=120', 'K:G'
]
songbeginning = [x+"\n" for x in songbeginning]
# convert song to abc format and write to file
if not os.path.exists(path):
os.makedirs("gen_songs_abc/song_"+modifier)
newsong = open(os.path.abspath(songname), 'w')
print(songname)
newsong.writelines(songbeginning)
for i in range(lastpart):
newsong.write(" | ".join(song[i:i+3]) + "\n")
newsong.write(" | ".join(song[lastpart:lengthofsong]))
newsong.close()
#abc2ly markov.abc
# lilypond -fpng markov.ly
#convert abc to markov
#create folder with that name and push .ly, midi and abc there?
f = open(song_path+".ly","w")
# subprocess.Popen(['/usr/bin/abc2midi',songname],stdout=subprocess.PIPE).communicate()
command = "abc2ly "+"-o "+song_path+".ly"+" "+songname
# cmd1 = subprocess.Popen(['/usr/bin/abc2ly','-o',song_path+".ly",songname],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
# cmd1 = subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE).communicate()
# os.system(command)
f.close()
# out, err = cmd1.communicate()
# # time.sleep(2)
cmd2 = subprocess.Popen(['lilypond','-fpng','-o',path,song_path+".ly"]).communicate()
# cmd2.wait()
#fluidsynth() dependency
# subprocess.Popen(['midi2audio',song_path+'.midi',song_path+'.wav']).communicate()
subprocess.Popen(['timidity',song_path+'.midi','-Ow','-o',song_path+'.wav']).communicate()
# output = str(temp.communicate())
#Introduces this wait time as we were returning file path even before lilypond converted the abc file
# final_path = os.path.abspath(song_path+".png")
return song_path+'.png',song_path+".wav"
#UI SECTION : Build using Gradio.
#Documentation :
# interface = gr.Interface(fn = music_gen,
# inputs=[gr.Radio(difficulty,label="Difficulty"),
# gr.Radio(timesignature,label="Time Signature"),
# gr.Dropdown(keysignature,label="Key Signature")],
# outputs = ["image","audio"],
# title="Sheet Music Generation for Sight-Reading",
# description="TO be added")
# interface.launch(inline=False)