File size: 6,683 Bytes
e849e3b
1d4f575
 
 
 
e849e3b
1d4f575
 
eada2cd
 
 
 
1d4f575
 
 
3b240b2
 
eada2cd
3b240b2
eada2cd
 
3b240b2
 
eada2cd
1d4f575
 
 
 
 
 
 
 
 
eada2cd
 
e849e3b
1d4f575
 
 
 
 
 
e08994d
e849e3b
 
 
 
 
eada2cd
 
 
 
 
1d4f575
 
eada2cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3b240b2
eada2cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2a6e99c
eada2cd
 
 
 
 
 
 
 
2a6e99c
eada2cd
 
8f37861
 
eada2cd
 
 
da72176
eada2cd
 
 
 
e08994d
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
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()
	# 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)