--- /dev/null
+#!@TARGET_PYTHON@
+
+# Copyright (C) 2006, 2007 Brailcom, o.p.s.
+#
+# Author: Milan Zamazal <pdm@brailcom.org>
+#
+# COPYRIGHT NOTICE
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+
+
+import codecs
+import optparse
+import os
+import popen2
+import sys
+import tempfile
+
+"""
+@relocate-preamble@
+"""
+
+
+FESTIVAL_COMMAND = 'festival --pipe'
+VOICE_CODINGS = {'voice_czech_ph': 'iso-8859-2'}
+
+_USAGE = """lilysong [ -p PLAY-PROGRAM ] FILE.xml [ LANGUAGE-CODE-OR-VOICE [ SPEEDUP ] ]
+./lilysong FILE.ly [ LANGUAGE-CODE-OR-VOICE ]
+./lilysong --list-voices
+./lilysong --list-languages
+"""
+
+def usage ():
+ print 'usage:', _USAGE
+ sys.exit (2)
+
+def process_options (args):
+ parser = optparse.OptionParser (usage=_USAGE, version="@TOPLEVEL_VERSION@")
+ parser.add_option ('', '--list-voices', action='store_true', dest='list_voices',
+ help="list available Festival voices")
+ parser.add_option ('', '--list-languages', action='store_true', dest='list_languages',
+ help="list available Festival languages")
+ parser.add_option ('-p', '--play-program', metavar='PROGRAM',
+ action='store', type='string', dest='play_program',
+ help="use PROGRAM to play song immediately")
+ options, args = parser.parse_args (args)
+ return options, args
+
+def call_festival (scheme_code):
+ in_, out = popen2.popen2 (FESTIVAL_COMMAND)
+ out.write (scheme_code)
+ out.close ()
+ answer = ''
+ while True:
+ process_output = in_.read ()
+ if not process_output:
+ break
+ answer = answer + process_output
+ return answer
+
+def select_voice (language_or_voice):
+ if language_or_voice[:6] == 'voice_':
+ voice = language_or_voice
+ else:
+ voice = call_festival ('''
+(let ((candidates '()))
+ (mapcar (lambda (v)
+ (if (eq (cadr (assoc 'language (cadr (voice.description v)))) '%s)
+ (set! candidates (cons v candidates))))
+ (append (voice.list) (mapcar car Voice_descriptions)))
+ (if candidates
+ (format t "voice_%%s" (car candidates))
+ (format t "nil")))
+''' % (language_or_voice,))
+ if voice == 'nil':
+ voice = None
+ return voice
+
+def list_voices ():
+ print call_festival ('''
+(let ((voices (voice.list))
+ (print-voice (lambda (v) (format t "voice_%s\n" v))))
+ (mapcar print-voice voices)
+ (mapcar (lambda (v) (if (not (member v voices)) (print-voice v)))
+ (mapcar car Voice_descriptions)))
+''')
+
+def list_languages ():
+ print call_festival ('''
+(let ((languages '()))
+ (let ((voices (voice.list))
+ (print-language (lambda (v)
+ (let ((language (cadr (assoc 'language (cadr (voice.description v))))))
+ (if (and language (not (member language languages)))
+ (begin
+ (set! languages (cons language languages))
+ (print language)))))))
+ (mapcar print-language voices)
+ (mapcar (lambda (v) (if (not (member v voices)) (print-language v)))
+ (mapcar car Voice_descriptions))))
+''')
+
+def process_xml_file (file_name, voice, speedup, play_program):
+ if speedup == 1:
+ speedup = None
+ coding = (VOICE_CODINGS.get (voice) or 'iso-8859-1')
+ _, xml_temp_file = tempfile.mkstemp ('.xml')
+ try:
+ # recode the XML file
+ recodep = (coding != 'utf-8')
+ if recodep:
+ decode = codecs.getdecoder ('utf-8')
+ encode = codecs.getencoder (coding)
+ input = open (file_name)
+ output = open (xml_temp_file, 'w')
+ while True:
+ data = input.read ()
+ if not data:
+ break
+ if recodep:
+ data = encode (decode (data)[0])[0]
+ output.write (data)
+ output.close ()
+ # synthesize
+ wav_file = file_name[:-3] + 'wav'
+ if speedup:
+ _, wav_temp_file = tempfile.mkstemp ('.wav')
+ else:
+ wav_temp_file = wav_file
+ try:
+ print "text2wave -eval '(%s)' -mode singing '%s' -o '%s'" % (voice, xml_temp_file, wav_temp_file,)
+ result = os.system ("text2wave -eval '(%s)' -mode singing '%s' -o '%s'" %
+ (voice, xml_temp_file, wav_temp_file,))
+ if result:
+ sys.stdout.write ("Festival processing failed.\n")
+ return
+ if speedup:
+ result = os.system ("sox '%s' '%s' speed '%f'" % (wav_temp_file, wav_file, speedup,))
+ if result:
+ sys.stdout.write ("Festival processing failed.\n")
+ return
+ finally:
+ if speedup:
+ try:
+ os.delete (wav_temp_file)
+ except:
+ pass
+ sys.stdout.write ("%s created.\n" % (wav_file,))
+ # play
+ if play_program:
+ os.system ("%s '%s' >/dev/null" % (play_program, wav_file,))
+ finally:
+ try:
+ os.delete (xml_temp_file)
+ except:
+ pass
+
+def process_ly_file (file_name, voice):
+ result = os.system ("lilypond '%s'" % (file_name,))
+ if result:
+ return
+ xml_file = None
+ for f in os.listdir (os.path.dirname (file_name) or '.'):
+ if (f[-4:] == '.xml' and
+ (not xml_file or os.stat.st_mtime (f) > os.stat.st_mtime (xml_file))):
+ xml_file = f
+ if xml_file:
+ process_xml_file (xml_file, voice, None, None)
+ else:
+ sys.stderr.write ("No XML file found\n")
+
+def go ():
+ options, args = process_options (sys.argv[1:])
+ if options.list_voices:
+ list_voices ()
+ elif options.list_languages:
+ list_languages ()
+ else:
+ arglen = len (args)
+ if arglen < 1:
+ usage ()
+ file_name = args[0]
+ if arglen > 1:
+ language_or_voice = args[1]
+ voice = select_voice (language_or_voice)
+ else:
+ voice = None
+ if file_name[-3:] == '.ly':
+ if arglen > 2:
+ usage ()
+ process_ly_file (file_name, voice)
+ else:
+ if arglen > 3:
+ usage ()
+ elif arglen == 3:
+ try:
+ speedup = float (args[2])
+ except ValueError:
+ usage ()
+ else:
+ speedup = None
+ process_xml_file (file_name, voice, speedup, options.play_program)
+
+if __name__ == '__main__':
+ go ()