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 music_gen(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 = 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)