Import([ 'env', 'sourceDir', ]) import os import ctypes from glob import glob class AutoFreeCDLL(ctypes.CDLL): def __del__(self): ctypes.windll.kernel32.FreeLibrary(self._handle) synthDriversDir=sourceDir.Dir('synthDrivers') espeakRepo=Dir("#include/espeak") espeakSrcDir=espeakRepo.Dir('src') espeakIncludeDir=espeakSrcDir.Dir('include') sonicSrcDir=Dir("#include/sonic") class espeak_VOICE(ctypes.Structure): _fields_=[ ('name',ctypes.c_char_p), ('languages',ctypes.c_char_p), ('identifier',ctypes.c_char_p), ('gender',ctypes.c_byte), ('age',ctypes.c_byte), ('variant',ctypes.c_byte), ('xx1',ctypes.c_byte), ('score',ctypes.c_int), ('spare',ctypes.c_void_p), ] env=env.Clone() #Whole-program optimization causes eSpeak to distort and worble with its Klatt4 voice #Therefore specifically force it off env.Append(CCFLAGS='/GL-') env.Append(CCFLAGS='/W0') env.Append(CPPPATH=['#nvdaHelper/espeak',espeakIncludeDir,espeakIncludeDir.Dir('compat'),sonicSrcDir]) def espeak_compilePhonemeData_buildEmitter(target,source,env): phSourceIgnores=['error_log','error_intonation','compile_prog_log','compile_report','envelopes.png'] phSources=env.Flatten([[Dir(topDir).File(f) for f in files if f not in phSourceIgnores] for topDir,subdirs,files in os.walk(source[0].abspath)]) sources=env.Flatten([phSources]) targets=[target[0].File(f) for f in ['intonations','phondata','phondata-manifest','phonindex','phontab']] phSideEffects=[source[0].File(x) for x in phSourceIgnores] env.SideEffect(phSideEffects,targets) return targets,sources def espeak_compilePhonemeData_buildAction(target,source,env): # We want the eSpeak dll to be freed after each dictionary. # This is because it writes to stderr but doesn't flush it. # Unfortunately, there's no way we can flush it or use a different stream # because our eSpeak statically links the CRT. espeak=AutoFreeCDLL(espeakLib[0].abspath) espeak.espeak_ng_InitializePath(espeakRepo.abspath) espeak.espeak_ng_CompileIntonation(None,None) espeak.espeak_ng_CompilePhonemeData(22050,None,None) espeak.espeak_Terminate() env['BUILDERS']['espeak_compilePhonemeData']=Builder(action=env.Action(espeak_compilePhonemeData_buildAction,"Compiling phoneme data"),emitter=espeak_compilePhonemeData_buildEmitter) def espeak_compileDict_buildAction(target,source,env): # We want the eSpeak dll to be freed after each dictionary. # This is because it writes to stderr but doesn't flush it. # Unfortunately, there's no way we can flush it or use a different stream # because our eSpeak statically links the CRT. espeak=AutoFreeCDLL(espeakLib[0].abspath) espeak.espeak_Initialize(0,0,target[0].Dir('..').abspath,0x8000) try: lang=source[0].name.split('_')[0] v=espeak_VOICE(languages=lang+'\x00') if espeak.espeak_SetVoiceByProperties(ctypes.byref(v))!=0: print "espeak_compileDict_action: failed to switch to language %s"%lang return 1 dictPath=os.path.split(source[0].abspath)[0]+'/' if espeak.espeak_ng_CompileDictionary(dictPath,None,0,None)!=0: print "espeak_compileDict_action: failed to compile dictionary for language %s"%lang return finally: espeak.espeak_Terminate() sonicLib=env.StaticLibrary( target='sonic', srcdir=sonicSrcDir.abspath, source='sonic.c', ) espeakLib=env.SharedLibrary( target='espeak', srcdir=espeakSrcDir.Dir('libespeak-ng').abspath, source=[ "speech.c", "readclause.c", "compiledict.c", "dictionary.c", "intonation.c", "setlengths.c", "numbers.c", "synth_mbrola.c", "synthdata.c", "synthesize.c", "translate.c", "tr_languages.c", "voices.c", "wavegen.c", "phonemelist.c", "klatt.c", "compiledata.c", "compilembrola.c", "ieee80.c", "spect.c", "espeak_api.c", "error.c", sonicLib, ], LIBS=['advapi32'], ) phonemeData=env.espeak_compilePhonemeData(espeakRepo.Dir('espeak-data'),espeakRepo.Dir('phsource')) env.Depends(phonemeData,espeakLib) # Move any extra dictionaries into dictsource for compilation env.Install(espeakRepo.Dir('dictsource'),env.Glob(os.path.join(espeakRepo.abspath,'dictsource','extra','*_*'))) #Compile all dictionaries missingDicts=[] #'mt','tn','tt'] dictSourcePath=espeakRepo.Dir('dictsource').abspath for f in env.Glob(os.path.join(dictSourcePath,'*_rules')): lang=f.name.split('_')[0] if lang in missingDicts: continue dictFile=env.Command(espeakRepo.Dir('espeak-data').File(lang+'_dict'),f,espeak_compileDict_buildAction) dictDeps=env.Glob(os.path.join(espeakRepo.abspath,'dictsource',lang+'_*')) dictDeps.remove(f) env.Depends(dictFile,dictDeps) env.Depends(dictFile,[espeakLib,phonemeData]) #Dictionaries can not be compiled in parallel, force SCons not to do this env.SideEffect('_espeak_compileDict',dictFile) env.Install(synthDriversDir,espeakLib) env.RecursiveInstall(synthDriversDir.Dir('espeak-data'),espeakRepo.Dir('espeak-data').abspath)