3 # Copyright (c) 2006--2010 Brailcom, o.p.s.
5 # Author: Milan Zamazal <pdm@brailcom.org>
7 # This file is part of LilyPond, the GNU music typesetter.
9 # LilyPond is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
14 # LilyPond is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
35 FESTIVAL_COMMAND
= 'festival --pipe'
36 VOICE_CODINGS
= {'voice_czech_ph': 'iso-8859-2'}
38 _USAGE
= """lilysong [-p PLAY-PROGRAM] FILE.xml [LANGUAGE-CODE-OR-VOICE [SPEEDUP]]
39 lilysong FILE.ly [LANGUAGE-CODE-OR-VOICE]
40 lilysong --list-voices
41 lilysong --list-languages
45 print 'Usage:', _USAGE
48 def process_options (args
):
49 parser
= optparse
.OptionParser (usage
=_USAGE
, version
="@TOPLEVEL_VERSION@")
50 parser
.add_option ('', '--list-voices', action
='store_true', dest
='list_voices',
51 help="list available Festival voices")
52 parser
.add_option ('', '--list-languages', action
='store_true', dest
='list_languages',
53 help="list available Festival languages")
54 parser
.add_option ('-p', '--play-program', metavar
='PROGRAM',
55 action
='store', type='string', dest
='play_program',
56 help="use PROGRAM to play song immediately")
57 options
, args
= parser
.parse_args (args
)
60 def call_festival (scheme_code
):
61 in_
, out
= popen2
.popen2 (FESTIVAL_COMMAND
)
62 out
.write (scheme_code
)
66 process_output
= in_
.read ()
67 if not process_output
:
69 answer
= answer
+ process_output
72 def select_voice (language_or_voice
):
73 if language_or_voice
[:6] == 'voice_':
74 voice
= language_or_voice
76 voice
= call_festival ('''
77 (let ((candidates '()))
79 (if (eq (cadr (assoc 'language (cadr (voice.description v)))) '%s)
80 (set! candidates (cons v candidates))))
81 (append (voice.list) (mapcar car Voice_descriptions)))
83 (format t "voice_%%s" (car candidates))
85 ''' % (language_or_voice
,))
91 print call_festival ('''
92 (let ((voices (voice.list))
93 (print-voice (lambda (v) (format t "voice_%s\n" v))))
94 (mapcar print-voice voices)
95 (mapcar (lambda (v) (if (not (member v voices)) (print-voice v)))
96 (mapcar car Voice_descriptions)))
99 def list_languages ():
100 print call_festival ('''
101 (let ((languages '()))
102 (let ((voices (voice.list))
103 (print-language (lambda (v)
104 (let ((language (cadr (assoc 'language (cadr (voice.description v))))))
105 (if (and language (not (member language languages)))
107 (set! languages (cons language languages))
108 (print language)))))))
109 (mapcar print-language voices)
110 (mapcar (lambda (v) (if (not (member v voices)) (print-language v)))
111 (mapcar car Voice_descriptions))))
114 def process_xml_file (file_name
, voice
, speedup
, play_program
):
117 coding
= (VOICE_CODINGS
.get (voice
) or 'iso-8859-1')
118 _
, xml_temp_file
= tempfile
.mkstemp ('.xml')
120 # recode the XML file
121 recodep
= (coding
!= 'utf-8')
123 decode
= codecs
.getdecoder ('utf-8')
124 encode
= codecs
.getencoder (coding
)
125 input = open (file_name
)
126 output
= open (xml_temp_file
, 'w')
132 data
= encode (decode (data
)[0])[0]
136 wav_file
= file_name
[:-3] + 'wav'
138 _
, wav_temp_file
= tempfile
.mkstemp ('.wav')
140 wav_temp_file
= wav_file
142 print "text2wave -eval '(%s)' -mode singing '%s' -o '%s'" % (voice
, xml_temp_file
, wav_temp_file
,)
143 result
= os
.system ("text2wave -eval '(%s)' -mode singing '%s' -o '%s'" %
144 (voice
, xml_temp_file
, wav_temp_file
,))
146 sys
.stdout
.write ("Festival processing failed.\n")
149 result
= os
.system ("sox '%s' '%s' speed '%f'" % (wav_temp_file
, wav_file
, speedup
,))
151 sys
.stdout
.write ("Festival processing failed.\n")
156 os
.delete (wav_temp_file
)
159 sys
.stdout
.write ("%s created.\n" % (wav_file
,))
162 os
.system ("%s '%s' >/dev/null" % (play_program
, wav_file
,))
165 os
.delete (xml_temp_file
)
169 def process_ly_file (file_name
, voice
):
170 result
= os
.system ("lilypond '%s'" % (file_name
,))
174 for f
in os
.listdir (os
.path
.dirname (file_name
) or '.'):
175 if (f
[-4:] == '.xml' and
176 (not xml_file
or os
.stat
.st_mtime (f
) > os
.stat
.st_mtime (xml_file
))):
179 process_xml_file (xml_file
, voice
, None, None)
181 sys
.stderr
.write ("No XML file found\n")
184 options
, args
= process_options (sys
.argv
[1:])
185 if options
.list_voices
:
187 elif options
.list_languages
:
195 language_or_voice
= args
[1]
196 voice
= select_voice (language_or_voice
)
199 if file_name
[-3:] == '.ly':
202 process_ly_file (file_name
, voice
)
208 speedup
= float (args
[2])
213 process_xml_file (file_name
, voice
, speedup
, options
.play_program
)
215 if __name__
== '__main__':