visakh7843's picture
Song playback feature added
8f37861
raw
history blame
6.28 kB
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
#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 main(difficulty,time_Signature, Key_Signature):
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 = "/usr/bin/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(['/usr/bin/lilypond','-fpng','-o',path,song_path+".ly"]).communicate()
# cmd2.wait()
#fluidsynth() dependency
subprocess.Popen(['midi2audio',song_path+'.midi',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 = main,
inputs=[gr.Radio(difficulty),
gr.Radio(timesignature),
gr.Dropdown(keysignature)],
outputs = ["image","audio"])
interface.launch()