### /conv/ly.py

from object import *

lilypond_version = r'\version "2.8.5"'
# from 
# http://lilypond.org/doc/v2.8/Documentation/user/lilypond/MIDI-instruments.html#MIDI-instruments
midiInstrument = \
	[
	'acoustic grand',
	'bright acoustic',
	'electric grand',
	'honky-tonk',
	'electric piano 1',
	'electric piano 2',             
	'harpsichord',                   
	'clav',                             
	'celesta',                          
	'glockenspiel',                         
	'music box',                            
	'vibraphone',                          
	'marimba',                           
	'xylophone',                               
	'tubular bells',                         
	'dulcimer',                                   
	'drawbar organ',                     
	'percussive organ',                    
	'rock organ',                        
	'church organ',                      
	'reed organ',                         
	'accordion',                           
	'harmonica',                             
	'concertina',                            
	'acoustic guitar (nylon)',            
	'acoustic guitar (steel)',                    
	'electric guitar (jazz)',             
	'electric guitar (clean)',                 
	'electric guitar (muted)',                
	'overdriven guitar',                       
	'distorted guitar',                          
	'guitar harmonics',                       
	'acoustic bass',                         
	'electric bass (finger)',             
	'electric bass (pick)',                 
	'fretless bass',                           
	'slap bass 1',                             
	'slap bass 2',                     
	'synth bass 1',                  
	'synth bass 2',                  
	'violin',                           
	'viola',                          
	'cello',                     
	'contrabass',
	'tremolo strings',
	'pizzicato strings', 
	'orchestral strings',
	'timpani',
	'string ensemble 1',
	'string ensemble 2',
	'synthstrings 1',
	'synthstrings 2',
	'choir aahs',
	'voice oohs',
	'synth voice',
	'orchestra hit',
	'trumpet',
	'trombone',
	'tuba',
	'muted trumpet',
	'french horn',
	'brass section',
	'synthbrass 1',
	'synthbrass 2',
	'soprano sax',
	'alto sax',
	'tenor sax',
	'baritone sax',
	'oboe',
	'english horn',
	'bassoon',
	'clarinet',
	'piccolo',
	'flute',
	'recorder',
	'pan flute',
	'blown bottle',
	'shakuhachi',
	'whistle',
	'ocarina',
	'lead 1 (square)',
	'lead 2 (sawtooth)',
	'lead 3 (calliope)',
	'lead 4 (chiff)',
	'lead 5 (charang)',
	'lead 6 (voice)',
	'lead 7 (fifths)',
	'lead 8 (bass+lead)',
	'pad 1 (new age)',
	'pad 2 (warm)',
	'pad 3 (polysynth)',
	'pad 4 (choir)',
	'pad 5 (bowed)',
	'pad 6 (metallic)',
	'pad 7 (halo)',
	'pad 8 (sweep)',
	'fx 1 (rain)',
	'fx 2 (soundtrack)',
	'fx 3 (crystal)',
	'fx 4 (atmosphere)',
	'fx 5 (brightness)',
	'fx 6 (goblins)',
	'fx 7 (echoes)',
	'fx 8 (sci-fi)',
	'sitar',
	'banjo',
	'shamisen',
	'koto',
	'kalimba',
	'bagpipe',
	'fiddle',
	'shanai',
	'tinkle bell',
	'agogo',
	'steel drums',
	'woodblock',
	'taiko drum',
	'melodic tom',
	'synth drum',
	'reverse cymbal',
	'guitar fret noise',
	'breath noise',
	'seashore',
	'bird tweet',
	'telephone ring',
	'helicopter',
	'applause',
	'gunshot'
	]

def n(str):
	return str + "\n"

def gcd(m,n):
	while n != 0:
		t = m % n
		m = n
		n = t
	return abs(m)
	
def useful_multiple((a,b), (c,d)):
	q = gcd(a, b)
	a, b = a / q, b / q
	q = gcd(c, d)
	c, d = d / q, c / q
	#------------------
	q = gcd(a, d)
	a, d = a / q, d / q
	q = gcd(b, c)
	b, c = b / q, c / q
	#------------------	
	return (a * c, b * d)
	
def convert_score_midi(score): # midi
	out  = ""
	out += n("\\score {")
	out += n("  <<")
	
	# make new Staff with score's voices
	for i in range(len(score.voices)):
		out += convert_melody_newStaff_d(score.voices[i], score.voicetable[i], score.times)
	
	out += n("  >>")
	out += n("  \\midi {")
	out += n("    \\tempo %d=%d" % (score.times[1], score.tempo) )
	out += n("  }")
	out += n("}")
	return out
def convert_melody_newStaff_d(voice_pref, voice_body, maintimes):	# with dynamics
	tab  = space * 4
	out  = tab + n("\\new Staff {")
	tab  = space * 6
	out += tab + n("\\time %d/%d" % (maintimes[0], maintimes[1]))
	out += tab + n("\\set Staff.midiMinimumVolume = #0.0")
	out += tab + n("\\set Staff.midiMaximumVolume = #1.0")
	out += tab + n("\\set Staff.midiInstrument = %s" % (r'"' + \
		midiInstrument[voice_pref.midi] + r'"'))
	# compress Music
	if voice_pref.time <> maintimes:
		out += tab + n("\\set Staff.timeSignatureFraction = #\'(%d . %d)") % \
			(voice_pref.time[0], voice_pref.time[1])
		fraction = useful_multiple(maintimes, voice_pref.time)
		out += tab + n("\\compressMusic #\'(%d . %d) {") % fraction
	out += convert_melody_midi(voice_body, voice_pref.time)
	
	if voice_pref.time <> maintimes:
		out += "\n" + tab + n("}")
	
	tab  = space * 4
	out += "\n" + tab + n("}")
	return out

def convert_score_layout(score): # Layout
	out  = ""
	out += n("\\score {")
	out += n("  <<")
	
	# make new Staff with score's voices
	for i in range(len(score.voices)):
		out += convert_melody_newStaff(score.voices[i], score.voicetable[i], score.times)
	
	out += n("  >>")
	out += n("  \\layout {}")
	out += n("}")
	return out

def convert_melody_newStaff(voice_pref, voice_body, maintimes):	# with dynamics
	tab  = space * 4
	out  = tab + n("\\new Staff {")
	tab  = space * 6
	out += tab + n("\\time %d/%d" % (maintimes[0], maintimes[1]))
	# compress Music
	if voice_pref.time <> maintimes:
		out += tab + n("\\set Staff.timeSignatureFraction = #\'(%d . %d)") % \
			(voice_pref.time[0], voice_pref.time[1])
		fraction = useful_multiple(maintimes, voice_pref.time)
		out += tab + n("\\compressMusic #\'(%d . %d) {") % fraction
	out += convert_melody_midi(voice_body, voice_pref.time, False)
	
	if voice_pref.time <> maintimes:
		out += "\n" + tab + n("}")
	
	tab  = space * 4
	out += "\n" + tab + n("}")
	return out

def connect(strline, time):
	def get_length(note):
		tmp = ""
		for s in note:
			if s in '0123456789':
				tmp = tmp + s
		return int(tmp)
	
	def set_length(note, length):
		lnote = list(note)
		
		tmp = ""
		i1, i2 = -1,len(lnote)
		for i in range(len(lnote)):
			if lnote[i] in '0123456789':
				if i1 == -1: i1 = i
		for i in range(len(lnote) - 1, -1, -1):
			if lnote[i] in '0123456789':
				if i2 == len(lnote): i2 = i
		lnote[i1: i2 + 1] = list(str(length))
		
		s = ""
		for i in range(len(lnote)):
			s += lnote[i]
		return s
		
	def div2(integer):
		tmp, q = integer, []
		while tmp > 1:
			q.append(tmp % 2)
			tmp /= 2
		q.append(tmp)
		for i in range(len(q)):
			q[i] *= 2 ** (i)
		while q.count(0) > 0:
			q.remove(0)
		return q
	a, b = time[0], time[1]
	div = []
	# divide strline by time[0]
	for i in range(0, len(strline), a):
		div.append(strline[i: i + a])
	
	for d in div:
		# connect in div
		tmp, l = None, 0
		while l < len(d):
			if tmp == None:
				if d[l][-1] == "~" or d[l][0] == "r":
					tmp = d[l]
			else:
				if d[l] == tmp:
					d[l - 1: l + 1] = [set_length(d[l - 1], (get_length(d[l - 1]) + 1))]
					l -= 1
				else:
					if d[l][-1] == "~" or d[l][0] == "r":
						tmp = d[l]
			l += 1
		# divide div by time[1]
		l = 0
		while l < len(d):
			tmp = []
			q = div2(get_length(d[l]))
			for j in q:
				if d[l][0] <> "r":
					if d[l][-1] == "~":
						tmp.append(set_length(d[l], j))
					else:
						if q.index(j) <> len(q) - 1:
							tmp.append(set_length(d[l], j) + "~")
						else:
							tmp.append(set_length(d[l], j))
				else:
					tmp.append(set_length(d[l], j))					
			d[l: l + 1] = tmp
			l += len(tmp)
		# set real-number
		l = 0
		while l < len(d):
			tmp = int(b / get_length(d[l]))
			d[l] = set_length(d[l], tmp)
			l += 1
	# return
	r = []
	for d in div:
		for e in d:
			r.append(e)
	return r

def convert_melody_midi(melodies, times, withDynamics = True):
	def convert_p_string(p):
		octave, pitch = int(p / 12), p % 12
		if   octave == 0:
			oc = ",,,,"
		elif octave == 1:
			oc = ",,,"
		elif octave == 2:
			oc = ",,"
		elif octave == 3:
			oc = ","
		elif octave == 4:
			oc = ""
		elif octave == 5:
			oc = "'"
		elif octave == 6:
			oc = "''"
		elif octave == 7:
			oc = "'''"
		elif octave == 8:
			oc = "''''"
		elif octave == 9:
			oc = "'''''"
		elif octave == 10:
			oc = "''''''"
		
		if   pitch == 0:  pc = 'c'
		elif pitch == 1:  pc = 'cis'
		elif pitch == 2:  pc = 'd'
		elif pitch == 3:  pc = 'ees'
		elif pitch == 4:  pc = 'e'
		elif pitch == 5:  pc = 'f'
		elif pitch == 6:  pc = 'fis'
		elif pitch == 7:  pc = 'g'
		elif pitch == 8:  pc = 'gis'
		elif pitch == 9:  pc = 'a'
		elif pitch == 10: pc = 'bes'
		elif pitch == 11: pc = 'b'
		
		return pc + oc
		
	def convert_d_string(x):
		"""
		dynamics:	/scm/midi.scm
		
		   sf  1.00		13
		fffff  0.95		12
		 ffff  0.92		11
		  fff  0.85		 0
		   ff  0.80		 9
		   f  0.75		 8
		   mf  0.68		 7
		   mp  0.61		 6
		   p  0.55		 5
		   pp  0.49		 4
		  ppp  0.42		 3
		 pppp  0.34		 2
		ppppp  0.25		 1
		
		"""
		
		if   x < 0.25:
			out = None
		elif x < 0.34:
			out = "\\ppppp"
		elif x < 0.42:
			out = "\\pppp"
		elif x < 0.49:
			out = "\\ppp"
		elif x < 0.55:
			out = "\\pp"
		elif x < 0.61:
			out = "\\p"
		elif x < 0.68:
			out = "\\mp"
		elif x < 0.75:
			out = "\\mf"
		elif x < 0.80:
			out = "\\f"
		elif x < 0.85:
			out = "\\ff"
		elif x < 0.92:
			out = "\\fff"
		elif x < 0.95:
			out = "\\ffff"
		else:
			out = "\\sf"
		
		return out
	
	out  = ""
	out += space * 6 + "<<\n"
	tab  = space * 8
	
	# note's main length
	l = 1
	for melody in melodies:
		#--- convert numbers to strings
		# add out
		if out == (space * 6 + "<<\n"):
			out += tab + "{\n" + (tab + space * 2)
		else:
			out += " \\\\\n" + tab + "{\n" + (tab + space * 2)
		strline = []
		for i in range(len(melody)):
			es = melody[i]
			if len(es) == 0:
				tmp = "r" + str(l)
			elif len(es) == 1:
				e = es[0]
				p = convert_p_string(e[0])
				if withDynamics == True:
					d = convert_d_string(e[1])
				else:
					d = ""
				if e[-1] == 0:
					tmp = p + str(l) + d
				else:
					tmp = p + str(l) + d + "~"
			else:
				eline = "<"
				tmpp = ""
				for e in es:
					p = convert_p_string(e[0])
					if withDynamics == True:
						d = convert_d_string(e[1])
					else:
						d = ""
					tmpp += p + d + space
					eline += tmpp
				eline += ">"
				tmp = eline + str(l)
			strline.append(tmp)
		#--- continue strings
		strline = connect(strline, times)
		
		line_out = ""
		for e in strline:
			line_out += e + " "
		out += line_out + "\n" + tab + "}"
	out += "\n" + space * 6 + ">>\n"
	return out
