| /* |
| * BRLTTY - A background process providing access to the console screen (when in |
| * text mode) for a blind person using a refreshable braille display. |
| * |
| * Copyright (C) 1995-2023 by The BRLTTY Developers. |
| * |
| * BRLTTY comes with ABSOLUTELY NO WARRANTY. |
| * |
| * This is free software, placed under the terms of the |
| * GNU Lesser General Public License, as published by the Free Software |
| * Foundation; either version 2.1 of the License, or (at your option) any |
| * later version. Please see the file LICENSE-LGPL for details. |
| * |
| * Web Page: http://brltty.app/ |
| * |
| * This software is maintained by Dave Mielke <dave@mielke.cc>. |
| */ |
| |
| #include "prologue.h" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "log.h" |
| #include "parse.h" |
| |
| typedef enum { |
| PARM_PATH, |
| PARM_PUNCTLIST, |
| PARM_VOICE, |
| PARM_MAXRATE |
| } DriverParameter; |
| #define SPKPARMS "path", "punctlist", "voice", "maxrate" |
| |
| #include "spk_driver.h" |
| |
| #include <espeak-ng/speak_lib.h> |
| |
| static int maxrate = espeakRATE_MAXIMUM; |
| |
| static void |
| spk_say(SpeechSynthesizer *spk, const unsigned char *buffer, size_t length, size_t count, const unsigned char *attributes) |
| { |
| int result; |
| |
| /* add 1 to the length in order to pass along the trailing zero */ |
| result = espeak_Synth(buffer, length+1, 0, POS_CHARACTER, 0, |
| espeakCHARS_UTF8, NULL, (void *)spk); |
| if (result != EE_OK) |
| logMessage(LOG_ERR, "eSpeak-NG: Synth() returned error %d", result); |
| } |
| |
| static void |
| spk_mute(SpeechSynthesizer *spk) |
| { |
| espeak_Cancel(); |
| } |
| |
| static int SynthCallback(short *audio, int numsamples, espeak_EVENT *events) |
| { |
| SpeechSynthesizer *spk = events->user_data; |
| |
| while (events->type != espeakEVENT_LIST_TERMINATED) { |
| if (events->type == espeakEVENT_WORD) |
| tellSpeechLocation(spk, events->text_position - 1); |
| if (events->type == espeakEVENT_MSG_TERMINATED) |
| tellSpeechFinished(spk); |
| events++; |
| } |
| return 0; |
| } |
| |
| static void |
| spk_drain(SpeechSynthesizer *spk) |
| { |
| espeak_Synchronize(); |
| } |
| |
| static void |
| spk_setVolume(SpeechSynthesizer *spk, unsigned char setting) |
| { |
| int volume = getIntegerSpeechVolume(setting, 50); |
| espeak_SetParameter(espeakVOLUME, volume, 0); |
| } |
| |
| static void |
| spk_setRate(SpeechSynthesizer *spk, unsigned char setting) |
| { |
| int h_range = (maxrate - espeakRATE_MINIMUM)/2; |
| int rate = getIntegerSpeechRate(setting, h_range) + espeakRATE_MINIMUM; |
| espeak_SetParameter(espeakRATE, rate, 0); |
| } |
| |
| static void |
| spk_setPitch(SpeechSynthesizer *spk, unsigned char setting) |
| { |
| int pitch = getIntegerSpeechPitch(setting, 50); |
| espeak_SetParameter(espeakPITCH, pitch, 0); |
| } |
| |
| static void |
| spk_setPunctuation(SpeechSynthesizer *spk, SpeechPunctuation setting) |
| { |
| espeak_PUNCT_TYPE punct; |
| if (setting <= SPK_PUNCTUATION_NONE) |
| punct = espeakPUNCT_NONE; |
| else if (setting >= SPK_PUNCTUATION_ALL) |
| punct = espeakPUNCT_ALL; |
| else |
| punct = espeakPUNCT_SOME; |
| espeak_SetParameter(espeakPUNCTUATION, punct, 0); |
| } |
| |
| static int spk_construct(SpeechSynthesizer *spk, char **parameters) |
| { |
| const char *data_path, *voicename, *punctlist; |
| int result; |
| |
| spk->setVolume = spk_setVolume; |
| spk->setRate = spk_setRate; |
| spk->setPitch = spk_setPitch; |
| spk->setPunctuation = spk_setPunctuation; |
| spk->drain = spk_drain; |
| |
| logMessage(LOG_INFO, "eSpeak-NG version %s", espeak_Info(NULL)); |
| |
| data_path = parameters[PARM_PATH]; |
| if (data_path && !*data_path) |
| data_path = NULL; |
| result = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, data_path, 0); |
| if (result < 0) { |
| logMessage(LOG_ERR, "eSpeak-NG: initialization failed"); |
| return 0; |
| } |
| |
| voicename = parameters[PARM_VOICE]; |
| if(!voicename || !*voicename) |
| voicename = "en"; |
| result = espeak_SetVoiceByName(voicename); |
| if (result != EE_OK) { |
| espeak_VOICE voice_select; |
| memset(&voice_select, 0, sizeof(voice_select)); |
| voice_select.languages = voicename; |
| result = espeak_SetVoiceByProperties(&voice_select); |
| } |
| if (result != EE_OK) { |
| logMessage(LOG_ERR, "eSpeak-NG: unable to load voice '%s'", voicename); |
| return 0; |
| } |
| |
| punctlist = parameters[PARM_PUNCTLIST]; |
| if (punctlist && *punctlist) { |
| wchar_t w_punctlist[strlen(punctlist) + 1]; |
| int i = 0; |
| while ((w_punctlist[i] = punctlist[i]) != 0) i++; |
| espeak_SetPunctuationList(w_punctlist); |
| } |
| |
| if (parameters[PARM_MAXRATE]) { |
| int val = atoi(parameters[PARM_MAXRATE]); |
| if (val > espeakRATE_MINIMUM) maxrate = val; |
| } |
| |
| espeak_SetSynthCallback(SynthCallback); |
| |
| return 1; |
| } |
| |
| static void spk_destruct(SpeechSynthesizer *spk) |
| { |
| espeak_Cancel(); |
| espeak_Terminate(); |
| } |