3 # Copyright (C) 2006, 2007 Brailcom, o.p.s.
5 # Author: Milan Zamazal <pdm@brailcom.org>
9 # This program 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 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful, but
15 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
36 FESTIVAL_COMMAND = 'festival --pipe'
37 VOICE_CODINGS = {'voice_czech_ph': 'iso-8859-2'}
39 _USAGE = """lilysong [ -p PLAY-PROGRAM ] FILE.xml [ LANGUAGE-CODE-OR-VOICE [ SPEEDUP ] ]
40 ./lilysong FILE.ly [ LANGUAGE-CODE-OR-VOICE ]
41 ./lilysong --list-voices
42 ./lilysong --list-languages
46 print 'usage:', _USAGE
49 def process_options (args):
50 parser = optparse.OptionParser (usage=_USAGE, version="@TOPLEVEL_VERSION@")
51 parser.add_option ('', '--list-voices', action='store_true', dest='list_voices',
52 help="list available Festival voices")
53 parser.add_option ('', '--list-languages', action='store_true', dest='list_languages',
54 help="list available Festival languages")
55 parser.add_option ('-p', '--play-program', metavar='PROGRAM',
56 action='store', type='string', dest='play_program',
57 help="use PROGRAM to play song immediately")
58 options, args = parser.parse_args (args)
61 def call_festival (scheme_code):
62 in_, out = popen2.popen2 (FESTIVAL_COMMAND)
63 out.write (scheme_code)
67 process_output = in_.read ()
68 if not process_output:
70 answer = answer + process_output
73 def select_voice (language_or_voice):
74 if language_or_voice[:6] == 'voice_':
75 voice = language_or_voice
77 voice = call_festival ('''
78 (let ((candidates '()))
80 (if (eq (cadr (assoc 'language (cadr (voice.description v)))) '%s)
81 (set! candidates (cons v candidates))))
82 (append (voice.list) (mapcar car Voice_descriptions)))
84 (format t "voice_%%s" (car candidates))
86 ''' % (language_or_voice,))
92 print call_festival ('''
93 (let ((voices (voice.list))
94 (print-voice (lambda (v) (format t "voice_%s\n" v))))
95 (mapcar print-voice voices)
96 (mapcar (lambda (v) (if (not (member v voices)) (print-voice v)))
97 (mapcar car Voice_descriptions)))
100 def list_languages ():
101 print call_festival ('''
102 (let ((languages '()))
103 (let ((voices (voice.list))
104 (print-language (lambda (v)
105 (let ((language (cadr (assoc 'language (cadr (voice.description v))))))
106 (if (and language (not (member language languages)))
108 (set! languages (cons language languages))
109 (print language)))))))
110 (mapcar print-language voices)
111 (mapcar (lambda (v) (if (not (member v voices)) (print-language v)))
112 (mapcar car Voice_descriptions))))
115 def process_xml_file (file_name, voice, speedup, play_program):
118 coding = (VOICE_CODINGS.get (voice) or 'iso-8859-1')
119 _, xml_temp_file = tempfile.mkstemp ('.xml')
121 # recode the XML file
122 recodep = (coding != 'utf-8')
124 decode = codecs.getdecoder ('utf-8')
125 encode = codecs.getencoder (coding)
126 input = open (file_name)
127 output = open (xml_temp_file, 'w')
133 data = encode (decode (data)[0])[0]
137 wav_file = file_name[:-3] + 'wav'
139 _, wav_temp_file = tempfile.mkstemp ('.wav')
141 wav_temp_file = wav_file
143 print "text2wave -eval '(%s)' -mode singing '%s' -o '%s'" % (voice, xml_temp_file, wav_temp_file,)
144 result = os.system ("text2wave -eval '(%s)' -mode singing '%s' -o '%s'" %
145 (voice, xml_temp_file, wav_temp_file,))
147 sys.stdout.write ("Festival processing failed.\n")
150 result = os.system ("sox '%s' '%s' speed '%f'" % (wav_temp_file, wav_file, speedup,))
152 sys.stdout.write ("Festival processing failed.\n")
157 os.delete (wav_temp_file)
160 sys.stdout.write ("%s created.\n" % (wav_file,))
163 os.system ("%s '%s' >/dev/null" % (play_program, wav_file,))
166 os.delete (xml_temp_file)
170 def process_ly_file (file_name, voice):
171 result = os.system ("lilypond '%s'" % (file_name,))
175 for f in os.listdir (os.path.dirname (file_name) or '.'):
176 if (f[-4:] == '.xml' and
177 (not xml_file or os.stat.st_mtime (f) > os.stat.st_mtime (xml_file))):
180 process_xml_file (xml_file, voice, None, None)
182 sys.stderr.write ("No XML file found\n")
185 options, args = process_options (sys.argv[1:])
186 if options.list_voices:
188 elif options.list_languages:
196 language_or_voice = args[1]
197 voice = select_voice (language_or_voice)
200 if file_name[-3:] == '.ly':
203 process_ly_file (file_name, voice)
209 speedup = float (args[2])
214 process_xml_file (file_name, voice, speedup, options.play_program)
216 if __name__ == '__main__':