#!@TARGET_PYTHON@ # Copyright (c) 2006--2012 Brailcom, o.p.s. # # Author: Milan Zamazal # # This file is part of LilyPond, the GNU music typesetter. # # LilyPond 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 3 of the License, or # (at your option) any later version. # # LilyPond 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 LilyPond. If not, see . 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 ()