from __future__ import print_function import time from ctypes import (CFUNCTYPE, POINTER, Structure, Union, c_char_p, c_int, c_long, c_short, c_ubyte, c_uint, c_ulong, c_void_p, c_wchar, cdll) def cfunc(name, dll, result, *args): """build and apply a ctypes prototype complete with parameter flags""" atypes = [] aflags = [] for arg in args: atypes.append(arg[1]) aflags.append((arg[2], arg[0]) + arg[3:]) return CFUNCTYPE(result, *atypes)((name, dll), tuple(aflags)) dll = None def load_library(): global dll paths = [ # macOS paths '/usr/local/lib/libespeak-ng.1.dylib', '/usr/local/lib/libespeak.dylib', # Linux paths 'libespeak-ng.so.1', '/usr/local/lib/libespeak-ng.so.1', 'libespeak.so.1', # Windows paths r'C:\Program Files\eSpeak NG\libespeak-ng.dll', r'C:\Program Files (x86)\eSpeak NG\libespeak-ng.dll' ] for path in paths: try: dll = cdll.LoadLibrary(path) return True except Exception: continue # Try the next path return False try: if not load_library(): raise RuntimeError("This means you probably do not have eSpeak or eSpeak-ng installed!") except Exception as exp: raise # constants and such from speak_lib.h EVENT_LIST_TERMINATED = 0 EVENT_WORD = 1 EVENT_SENTENCE = 2 EVENT_MARK = 3 EVENT_PLAY = 4 EVENT_END = 5 EVENT_MSG_TERMINATED = 6 class numberORname(Union): _fields_ = [ ('number', c_int), ('name', c_char_p) ] class EVENT(Structure): _fields_ = [ ('type', c_int), ('unique_identifier', c_uint), ('text_position', c_int), ('length', c_int), ('audio_position', c_int), ('sample', c_int), ('user_data', c_void_p), ('id', numberORname) ] AUDIO_OUTPUT_PLAYBACK = 0 AUDIO_OUTPUT_RETRIEVAL = 1 AUDIO_OUTPUT_SYNCHRONOUS = 2 AUDIO_OUTPUT_SYNCH_PLAYBACK = 3 EE_OK = 0 EE_INTERNAL_ERROR = -1 EE_BUFFER_FULL = 1 EE_NOT_FOUND = 2 Initialize = cfunc('espeak_Initialize', dll, c_int, ('output', c_int, 1, AUDIO_OUTPUT_PLAYBACK), ('bufflength', c_int, 1, 100), ('path', c_char_p, 1, None), ('option', c_int, 1, 0)) Initialize.__doc__ = """Must be called before any synthesis functions are called. output: the audio data can either be played by eSpeak or passed back by the SynthCallback function. buflength: The length in mS of sound buffers passed to the SynthCallback function. path: The directory which contains the espeak-data directory, or NULL for the default location. options: bit 0: 1=allow espeakEVENT_PHONEME events. Returns: sample rate in Hz, or -1 (EE_INTERNAL_ERROR).""" t_espeak_callback = CFUNCTYPE(c_int, POINTER(c_short), c_int, POINTER(EVENT)) cSetSynthCallback = cfunc('espeak_SetSynthCallback', dll, None, ('SynthCallback', t_espeak_callback, 1)) SynthCallback = None def SetSynthCallback(cb): global SynthCallback SynthCallback = t_espeak_callback(cb) cSetSynthCallback(SynthCallback) SetSynthCallback.__doc__ = """Must be called before any synthesis functions are called. This specifies a function in the calling program which is called when a buffer of speech sound data has been produced. The callback function is of the form: int SynthCallback(short *wav, int numsamples, espeak_EVENT *events); wav: is the speech sound data which has been produced. NULL indicates that the synthesis has been completed. numsamples: is the number of entries in wav. This number may vary, may be less than the value implied by the buflength parameter given in espeak_Initialize, and may sometimes be zero (which does NOT indicate end of synthesis). events: an array of espeak_EVENT items which indicate word and sentence events, and also the occurance if and