eSpeak NG is an open source speech synthesizer that supports more than hundred languages and accents.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

speak_riscos 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437
  1. /***************************************************************************
  2. * Copyright (C) 2005 to 2013 by Jonathan Duddington *
  3. * email: [email protected] *
  4. * *
  5. * This program is free software; you can redistribute it and/or modify *
  6. * it under the terms of the GNU General Public License as published by *
  7. * the Free Software Foundation; either version 3 of the License, or *
  8. * (at your option) any later version. *
  9. * *
  10. * This program is distributed in the hope that it will be useful, *
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  13. * GNU General Public License for more details. *
  14. * *
  15. * You should have received a copy of the GNU General Public License *
  16. * along with this program; if not, write see: *
  17. * <http://www.gnu.org/licenses/>. *
  18. ***************************************************************************/
  19. #define USE_MODULE
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <ctype.h>
  24. #include <locale.h>
  25. #include "kernel.h"
  26. #include <math.h>
  27. #include "speech.h"
  28. #include "speak_lib.h"
  29. #include "phoneme.h"
  30. #include "synthesize.h"
  31. #include "voice.h"
  32. #include "translate.h"
  33. #define os_X 0x20000
  34. // interface to the assembler section
  35. extern "C" {
  36. extern void DMA_Handler(void);
  37. // used from the cmhgfile
  38. extern _kernel_oserror *user_init(char *cmd_fail, int podule_base, void *pw);
  39. extern _kernel_oserror *swi_handler(int swi_no, int *r, void *pw);
  40. extern _kernel_oserror *command_handler(char *arg_string, int argc, int cmd_no, void *pw);
  41. extern int callback_handler(_kernel_swi_regs *r, void *pw);
  42. extern int callback_entry(_kernel_swi_regs *r, void *pw);
  43. extern int sound_handler(_kernel_swi_regs *r, void *pw);
  44. extern int sound_entry(_kernel_swi_regs *r, void *pw);
  45. }
  46. extern int Generate(PHONEME_LIST *phoneme_list, int *n_ph, int resume);
  47. extern void RiscosOpenSound();
  48. extern int WcmdqUsed();
  49. extern void FreePhData();
  50. extern void FreeDictionary();
  51. extern void Write4Bytes(FILE *f, int value);
  52. extern int wcmdq_head;
  53. extern int wcmdq_tail;
  54. extern int current_source_index;
  55. FILE *f_text;
  56. FILE *f_wave = NULL;
  57. int (* uri_callback)(int, const char *, const char *) = NULL;
  58. int (* phoneme_callback)(const char *) = NULL;
  59. int amp = 8; // default
  60. char path_home[N_PATH_HOME] = "";
  61. char wavefile[120];
  62. char textbuffile[L_tmpnam];
  63. int sample_rate_index; // current value
  64. char current_voice_name[40] = {0};
  65. int n_voice_files = 0;
  66. int n_voice_variant_files = 0;
  67. // output sound buffer, 2 bytes per sample
  68. static unsigned short SoundBuf[4096];
  69. static void *module_data;
  70. static int callback_inhibit = 0;
  71. static int more_text=0;
  72. #define N_VOICE_NAMES 60
  73. #define N_VOICE_VARIANT_NAMES 30
  74. static char *voice_names[N_VOICE_NAMES];
  75. static char *voice_variant_names[N_VOICE_VARIANT_NAMES];
  76. #define N_STATIC_BUF 8000
  77. static char static_buf[N_STATIC_BUF];
  78. static _kernel_oserror errblk;
  79. USHORT voice_pcnt[N_PEAKS+1][3];
  80. static const char *help_text =
  81. "\nspeak [options] [\"<words>\"]\n\n"
  82. "-f <text file> Text file to speak\n"
  83. //"--stdin Read text input from stdin instead of a file\n\n"
  84. "If neither -f nor --stdin, <words> are spoken, or if none then text is\n"
  85. "spoken from stdin, each line separately.\n\n"
  86. "-a <integer>\n"
  87. "\t Amplitude, 0 to 200, default is 100\n"
  88. "-g <integer>\n"
  89. "\t Word gap. Pause between words, units of 10mS at the default speed\n"
  90. "-l <integer>\n"
  91. "\t Line length. If not zero (which is the default), consider\n"
  92. "\t lines less than this length as end-of-clause\n"
  93. "-p <integer>\n"
  94. "\t Pitch adjustment, 0 to 99, default is 50\n"
  95. "-s <integer>\n"
  96. "\t Speed in words per minute 80 to 390, default is 170\n"
  97. "-v <voice name>\n"
  98. "\t Use voice file of this name from espeak-data/voices\n"
  99. "-w <wave file name>\n"
  100. "\t Write output to this WAV file, rather than speaking it directly\n"
  101. "-b\t Input text is 8-bit encoding\n"
  102. "-m\t Interpret SSML markup, and ignore other < > tags\n"
  103. "-q\t Quiet, don't produce any speech (may be useful with -x)\n"
  104. "-x\t Write phoneme mnemonics to stdout\n"
  105. "-X\t Write phonemes mnemonics and translation trace to stdout\n"
  106. //"--stdout Write speech output to stdout\n"
  107. "--compile=<voice name>\n"
  108. "\t Compile the pronunciation rules and dictionary in the current\n"
  109. "\t directory. =<voice name> is optional and specifies which language\n"
  110. "--punct=\"<characters>\"\n"
  111. "\t Speak the names of punctuation characters during speaking. If\n"
  112. "\t =<characters> is omitted, all punctuation is spoken.\n"
  113. "--voices=<language>\n"
  114. "\t List the available voices for the specified language.\n"
  115. "\t If <language> is omitted, then list all voices.\n"
  116. "-k <integer>\n"
  117. "\t Indicate capital letters with: 1=sound, 2=the word \"capitals\",\n"
  118. "\t higher values = a pitch increase (try -k20).\n";
  119. int GetFileLength(const char *filename)
  120. {//====================================
  121. int length=0;
  122. int type;
  123. _kernel_swi_regs regs;
  124. _kernel_oserror *error;
  125. regs.r[0] = 5;
  126. regs.r[1] = (int)filename;
  127. regs.r[2] = 0;
  128. regs.r[3] = 0;
  129. regs.r[4] = 0;
  130. regs.r[5] = 0;
  131. error = _kernel_swi(0x20008,&regs,&regs);
  132. if(error)
  133. return(0);
  134. type = regs.r[0];
  135. length = regs.r[4];
  136. if(type==2)
  137. return(-2); // a directory
  138. if((type!=1) && (type!=3))
  139. return(0); /* not a file */
  140. return(length);
  141. } /* end of GetFileLength */
  142. void MarkerEvent(int type, unsigned int char_position, int value, int value2, unsigned char *out_ptr)
  143. {
  144. }
  145. void ReadVoiceNames2(char *directory)
  146. {//==================================
  147. int len;
  148. int path_len;
  149. int *type;
  150. char *p;
  151. _kernel_swi_regs regs;
  152. _kernel_oserror *error;
  153. char buf[80];
  154. char directory2[sizeof(path_home)+100];
  155. regs.r[0] = 10;
  156. regs.r[1] = (int)directory;
  157. regs.r[2] = (int)buf;
  158. regs.r[3] = 1;
  159. regs.r[4] = 0;
  160. regs.r[5] = sizeof(buf);
  161. regs.r[6] = 0;
  162. path_len = strlen(directory);
  163. voice_variant_names[0] = "(none)";
  164. n_voice_variant_files = 1;
  165. while(regs.r[3] > 0)
  166. {
  167. error = _kernel_swi(0x0c+os_X,&regs,&regs); /* OS_GBPB 10, read directory entries */
  168. if((error != NULL) || (regs.r[3] == 0))
  169. {
  170. break;
  171. }
  172. type = (int *)(&buf[16]);
  173. len = strlen(&buf[20]);
  174. if(*type == 2)
  175. {
  176. // a sub-directory
  177. sprintf(directory2,"%s.%s",directory,&buf[20]);
  178. ReadVoiceNames2(directory2);
  179. }
  180. else
  181. {
  182. p = (char *)malloc(len+1);
  183. strcpy(p,&buf[20]);
  184. if(strcmp(&directory[path_len-3],".!v")==0)
  185. {
  186. if(n_voice_variant_files >= (N_VOICE_VARIANT_NAMES-1))
  187. continue;
  188. voice_variant_names[n_voice_variant_files++] = p;
  189. }
  190. else
  191. if(strcmp(p, "default") != 0)
  192. {
  193. if(n_voice_files >= (N_VOICE_NAMES-1))
  194. continue;
  195. voice_names[n_voice_files++] = p;
  196. }
  197. }
  198. }
  199. }
  200. void ReadVoiceNames()
  201. {//===================
  202. char directory[sizeof(path_home)+20];
  203. n_voice_files = 0;
  204. n_voice_variant_files = 0;
  205. sprintf(directory,"%s.voices.default", path_home);
  206. if(GetFileLength(directory) > 0)
  207. {
  208. // put the 'default' voice at the start of the list
  209. voice_names[n_voice_files++] = "default";
  210. }
  211. sprintf(directory,"%s.voices",path_home);
  212. ReadVoiceNames2(directory);
  213. voice_names[n_voice_files] = NULL;
  214. voice_variant_names[n_voice_variant_files] = NULL;
  215. }
  216. #ifdef USE_MODULE
  217. char *Alloc(int size)
  218. /*******************/
  219. { // version of malloc() for use in RISC_OS module
  220. _kernel_swi_regs regs;
  221. regs.r[0] = 6;
  222. regs.r[3] = size;
  223. _kernel_swi(0x1e, &regs, &regs); /* OS_Module 6 claim memory */
  224. return(char *)regs.r[2];
  225. } /* end of module_malloc */
  226. void Free(void *ptr)
  227. /*******************/
  228. { // version of free() for use in RISC_OS module
  229. _kernel_swi_regs regs;
  230. if(ptr == NULL)
  231. return;
  232. regs.r[0] = 7;
  233. regs.r[2] = (int)(ptr);
  234. _kernel_swi(0x1e, &regs, &regs); /* OS_Module 7 free memory */
  235. } /* end of Free */
  236. #else
  237. char *Alloc(int size)
  238. {//==================
  239. char *p;
  240. if((p = (char *)malloc(size)) == NULL)
  241. fprintf(stderr,"Can't allocate memory\n");
  242. return(p);
  243. }
  244. void Free(void **ptr)
  245. {//=================
  246. if(ptr != NULL)
  247. {
  248. free(ptr);
  249. }
  250. }
  251. #endif
  252. static int OpenWaveFile(const char *path, int rate)
  253. //=================================================
  254. {
  255. // Set the length of 0x7fffffff for --stdout
  256. // This will be changed to the correct length for -w (write to file)
  257. static unsigned char wave_hdr[44] = {
  258. 'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',
  259. 0x10,0,0,0,1,0,1,0, 9,0x3d,0,0,0x12,0x7a,0,0,
  260. 2,0,0x10,0,'d','a','t','a', 0xff,0xff,0xff,0x7f};
  261. if(path == NULL)
  262. return(2);
  263. if(strcmp(path,"stdout")==0)
  264. f_wave = stdout;
  265. else
  266. f_wave = fopen(path,"wb");
  267. if(f_wave != NULL)
  268. {
  269. fwrite(wave_hdr,1,24,f_wave);
  270. Write4Bytes(f_wave,rate);
  271. Write4Bytes(f_wave,rate * 2);
  272. fwrite(&wave_hdr[32],1,12,f_wave);
  273. return(0);
  274. }
  275. return(1);
  276. } // end of OpenWaveFile
  277. static void CloseWaveFile(int rate)
  278. //=================================
  279. {
  280. unsigned int pos;
  281. if((f_wave == NULL) || (f_wave == stdout))
  282. return;
  283. fflush(f_wave);
  284. pos = ftell(f_wave);
  285. fseek(f_wave,4,SEEK_SET);
  286. Write4Bytes(f_wave,pos - 8);
  287. fseek(f_wave,40,SEEK_SET);
  288. Write4Bytes(f_wave,pos - 44);
  289. fclose(f_wave);
  290. f_wave = NULL;
  291. } // end of CloseWaveFile
  292. void MarkerEvent(int type, unsigned int char_position, int value, unsigned char *out_ptr)
  293. {//======================================================================================
  294. // Do nothing in the command-line version.
  295. } // end of MarkerEvent
  296. static int WavegenFile(void)
  297. {//=========================
  298. int finished;
  299. unsigned char wav_outbuf[1024];
  300. out_ptr = out_start = wav_outbuf;
  301. out_end = wav_outbuf + sizeof(wav_outbuf);
  302. finished = WavegenFill(0);
  303. if(f_wave != NULL)
  304. {
  305. fwrite(wav_outbuf, 1, out_ptr - wav_outbuf, f_wave);
  306. }
  307. return(finished);
  308. } // end of WavegenFile
  309. void FillSoundBuf(int size)
  310. {//========================
  311. // Fill the buffer with output sound
  312. // size is number of samples*4
  313. size = size;
  314. if(size > sizeof(SoundBuf))
  315. size = sizeof(SoundBuf);
  316. out_ptr = (unsigned char *)(&SoundBuf[0]);
  317. out_end = (unsigned char *)(&SoundBuf[size]);
  318. WavegenFill(1);
  319. }
  320. int initialise(void)
  321. {//=================
  322. char buf[N_PATH_HOME];
  323. _kernel_swi_regs regs;
  324. _kernel_oserror *error;
  325. buf[0] = 0;
  326. regs.r[0] = (int)"eSpeak$Dir";
  327. regs.r[1] = (int)buf;
  328. regs.r[2] = sizeof(buf);
  329. regs.r[3] = 0;
  330. regs.r[4] = 0;
  331. regs.r[5] = 0;
  332. error = _kernel_swi(0x20023,&regs,&regs); // OS_ReadVarVal
  333. buf[regs.r[2]] = 0;
  334. sprintf(path_home,"%s.espeak-data",buf);
  335. if(GetFileLength(path_home) != -2)
  336. {
  337. // not found, try the 10 character version of the directory name
  338. sprintf(path_home,"%s.espeak-dat",buf);
  339. }
  340. if(GetFileLength(path_home) != -2)
  341. {
  342. // still can't find data directory
  343. sprintf(errblk.errmess,"Speak: Can't find data directory: '%s'\n",path_home);
  344. return(-1);
  345. }
  346. WavegenInit(22050,0);
  347. LoadPhData(NULL);
  348. SetVoiceStack(NULL, "");
  349. SynthesizeInit();
  350. return(0);
  351. }
  352. void speak_text_string(char *data, int terminator, int len, int wait, int voice_num)
  353. /**********************************************************************************/
  354. /* 'wait' indictes wait until speaking is finished before returning */
  355. {
  356. int c;
  357. int ix;
  358. char *vname;
  359. static static_length=0;
  360. static int user_token=0; /* increment for each call of translate() */
  361. _kernel_swi_regs regs;
  362. regs.r[0] = (int)callback_entry;
  363. regs.r[1] = (int)module_data;
  364. _kernel_swi(0x5f, &regs, &regs);
  365. option_endpause = 1;
  366. vname = voice_names[voice_num];
  367. if((voice_num >= 0) && (strcmp(vname, current_voice_name) != 0) && (voice_num < N_VOICE_NAMES))
  368. {
  369. SetVoiceByName(voice_names[voice_num]);
  370. WavegenSetVoice(voice);
  371. }
  372. strcmp(current_voice_name, vname);
  373. /* don't count CR as terminator if length is specified */
  374. if(len > 0) terminator = 0;
  375. ix = 0;
  376. if(more_text == 0)
  377. static_length = 0;
  378. else
  379. {
  380. strcat(&static_buf[static_length]," \n ");
  381. static_length+=3;
  382. }
  383. if(terminator == 0)
  384. {
  385. while(((c = data[ix++]) != 0) && (static_length < N_STATIC_BUF-4))
  386. {
  387. static_buf[static_length++] = c;
  388. if(ix == len)
  389. break;
  390. }
  391. }
  392. else
  393. {
  394. while(((c = data[ix++]) != '\r') && (c != '\n') && (c != 0) && (static_length < N_STATIC_BUF-3))
  395. {
  396. static_buf[static_length++] = c;
  397. if(ix == len)
  398. break;
  399. }
  400. }
  401. static_buf[static_length] = 0;
  402. if(option_waveout==0)
  403. {
  404. if(more_text == 0)
  405. {
  406. InitText(0);
  407. RiscosOpenSound();
  408. more_text = SpeakNextClause(NULL,(void *)static_buf,0);
  409. }
  410. while(wait)
  411. {
  412. if((more_text==0) && (wcmdq_head == wcmdq_tail))
  413. break;
  414. //we need to block to allow the callback handler to run
  415. regs.r[0] = 129; // wait for key press
  416. regs.r[1] = 10;
  417. regs.r[2] = 0;
  418. _kernel_swi(0x06, &regs, &regs); // OS_Byte
  419. }
  420. }
  421. else
  422. {
  423. more_text = 0;
  424. SpeakNextClause(NULL,(void *)static_buf,0);
  425. for(;;)
  426. {
  427. if(WavegenFile() != 0)
  428. break; // finished, wavegen command queue is empty
  429. if(Generate(phoneme_list,&n_phoneme_list,1)==0)
  430. SpeakNextClause(NULL,NULL,1);
  431. }
  432. CloseWaveFile(samplerate);
  433. }
  434. } /* end of speak_text_string */
  435. void speak_file(char *fname)
  436. {//=========================
  437. FILE *f_in;
  438. char buf[120];
  439. f_in = fopen(fname,"r");
  440. if(f_in == NULL)
  441. {
  442. fprintf(stderr,"Can't read file: '%s'",fname);
  443. return;
  444. }
  445. more_text = 1;
  446. if(option_waveout == 0)
  447. {
  448. RiscosOpenSound();
  449. SpeakNextClause(f_in,NULL,0);
  450. }
  451. else
  452. {
  453. more_text = 0;
  454. SpeakNextClause(f_in,NULL,0);
  455. for(;;)
  456. {
  457. if(WavegenFile() != 0)
  458. break; // finished, wavegen command queue is empty
  459. if(Generate(phoneme_list,&n_phoneme_list,1)==0)
  460. SpeakNextClause(NULL,NULL,1);
  461. }
  462. CloseWaveFile(samplerate);
  463. }
  464. }
  465. void set_say_options(int reg2, int reg3)
  466. /**************************************/
  467. /* Sets options from information in 'say' SWI */
  468. /* R3 bits 0-7 stress indicator character
  469. bit 8 inhibit unpronouncable check */
  470. {
  471. option_linelength = 0;
  472. option_phonemes = 0;
  473. option_waveout = 0;
  474. option_multibyte = 0; // auto
  475. option_capitals = 0;
  476. if(reg2 >= 0)
  477. {
  478. // not using SWI_SPEAK+14 to set voice and punctiation option
  479. option_punctuation = 0;
  480. option_punctlist[0] = 0;
  481. }
  482. else
  483. {
  484. SetParameter(espeakPUNCTUATION,option_punctuation,0);
  485. }
  486. } /* end of set_say_options */
  487. void jsd_swi_functions(int *r)
  488. /****************************/
  489. {
  490. int use_ipa;
  491. espeak_VOICE voice_select;
  492. switch(r[0])
  493. {
  494. case 0: /* major version */
  495. r[0] = 4;
  496. r[1] = 347;
  497. break;
  498. case 1: /* register user */
  499. break;
  500. case 2: /* deregister user */
  501. break;
  502. case 3:
  503. // translate into phonemes
  504. use_ipa = 0;
  505. if((r[2] >= 1) && (r[2] <= 5))
  506. use_ipa = r[2];
  507. TranslateClause(translator,NULL,(char *)r[1],NULL,NULL);
  508. GetTranslatedPhonemeString(translator->phon_out, sizeof(translator->phon_out), use_ipa);
  509. r[0] = (int)translator->phon_out;
  510. break;
  511. case 4:
  512. // r[0] = reload_word_dict(NULL);
  513. break;
  514. case 5: /* get table of voice names */
  515. r[0] = (int)voice_names;
  516. r[1] = (int)voice_variant_names;
  517. break;
  518. case 6: /* update voice data, r1 = voice_number */
  519. if(r[1] < N_VOICE_NAMES)
  520. {
  521. SetVoiceByName(voice_names[r[1]]);
  522. strcmp(current_voice_name, voice_names[r[1]]);
  523. WavegenSetVoice(voice);
  524. }
  525. break;
  526. case 7: /* load voice data */
  527. // init_voice((char *)r[1]);
  528. break;
  529. case 8:
  530. // list voices, r[1] contains optional language name (or "variant")
  531. voice_select.languages = (char *)r[1];
  532. voice_select.age = 0;
  533. voice_select.gender = 0;
  534. voice_select.name = NULL;
  535. r[0] = (int)espeak_ListVoices(&voice_select);
  536. break;
  537. default:
  538. r[0] = 0;
  539. r[1] = 0;
  540. break;
  541. }
  542. } /* end of jsd_swi_functions */
  543. _kernel_oserror *swi_handler(int swi_no, int *r, void *pw)
  544. /*********************************************************/
  545. {
  546. int value;
  547. int value2;
  548. int q_length;
  549. char *p;
  550. int ix;
  551. value = r[0];
  552. switch(swi_no)
  553. {
  554. case 0: // ready ?
  555. // returns the index into the source text of the currently speaking word
  556. ix = current_source_index & 0x7ff;
  557. if(ix > 0)
  558. r[1] = ix-1;
  559. else
  560. r[1] = ix; /* source index */
  561. r[2] = 0; /* source tag */
  562. r[3] = 0; /* for future expansion */
  563. r[4] = 0;
  564. r[5] = 0;
  565. if((WcmdqUsed() < 5) && (more_text == 0))
  566. {
  567. r[0] = -1; /* ready, or nearly */
  568. }
  569. else
  570. {
  571. r[0] = 0;
  572. }
  573. break;
  574. case 1: /* restore old sound channel. DO NOTHING */
  575. break;
  576. case 2: /* miscellaneous functions */
  577. jsd_swi_functions(r);
  578. break;
  579. case 3: /* speak text */
  580. // _kernel_irqs_on();
  581. set_say_options(r[2],r[3]);
  582. speak_text_string((char *)r[0],'\r',r[1],0,r[2]);
  583. break;
  584. case 4: /* speak text and wait */
  585. // _kernel_irqs_on(); /* enable interrupts */
  586. set_say_options(r[2],r[3]);
  587. speak_text_string((char *)r[0],'\r',r[1],1,r[2]);
  588. break;
  589. case 5: /* stop speaking */
  590. SpeakNextClause(NULL,NULL,2);
  591. more_text = 0;
  592. break;
  593. case 7: /* pitch */
  594. value = (value * 50)/128;
  595. SetParameter(espeakPITCH, value, 0);
  596. break;
  597. case 8: // speed, convert to range 80 to 400, mid-value=180
  598. if(value < 128)
  599. value2 = 80 + (value*100)/128; // linear range for 0-127 -> 80-179
  600. else
  601. value2 = 80 + pow((float)value/128.0, 1.75)*100;
  602. SetParameter(espeakRATE,value2,0);
  603. break;
  604. case 9: /* word_gap */
  605. if(value >= 128)
  606. value = value - 128;
  607. if(value > 1)
  608. value = (value-1) * 5;
  609. SetParameter(espeakWORDGAP,value,0);
  610. break;
  611. case 10: /* pitch_range */
  612. value = (value * 50)/128;
  613. SetParameter(espeakRANGE, value, 0);
  614. break;
  615. case 12: /* reset */
  616. // not implemented
  617. break;
  618. case 13: /* volume */
  619. // convert to range 6-255 to 10-400, mid-value=100
  620. if(value < 6)
  621. value2 = value + 4;
  622. else
  623. if(value < 128)
  624. {
  625. value2 = pow((float)(value+40)/168.0, 1.75)*100;
  626. }
  627. else
  628. value2 = pow((float)value/128.0, 2.0)*100;
  629. SetParameter(espeakVOLUME,value2,0);
  630. WavegenSetVoice(voice);
  631. break;
  632. case 14: // set voice by name
  633. p = (char *)r[0];
  634. if(strcmp(p, current_voice_name) != 0)
  635. {
  636. SetVoiceByName(p);
  637. WavegenSetVoice(voice);
  638. strcpy(current_voice_name, p);
  639. }
  640. option_punctuation = 0;
  641. if(r[1] != 0)
  642. {
  643. p = (char *)r[1];
  644. if(p[0] == 0)
  645. option_punctuation = 1;
  646. else
  647. {
  648. for(ix=0; ix < N_PUNCTLIST; ix++)
  649. {
  650. if((option_punctlist[ix] = p[ix]) == 0)
  651. break;
  652. }
  653. option_punctlist[N_PUNCTLIST-1] = 0;
  654. option_punctuation = 2;
  655. }
  656. }
  657. break;
  658. }
  659. return(NULL);
  660. } /* end of swi_handler */
  661. void PitchAdjust(int pitch_adjustment)
  662. {//===================================
  663. int ix, factor;
  664. extern unsigned char pitch_adjust_tab[MAX_PITCH_VALUE+1];
  665. voice->pitch_base = (voice->pitch_base * pitch_adjust_tab[pitch_adjustment])/128;
  666. // adjust formants to give better results for a different voice pitch
  667. factor = 256 + (25 * (pitch_adjustment - 50))/50;
  668. for(ix=0; ix<=5; ix++)
  669. {
  670. voice->freq[ix] = (voice->freq2[ix] * factor)/256;
  671. }
  672. } // end of PitchAdjustment
  673. void DisplayVoices(FILE *f_out, char *language)
  674. {//============================================
  675. int ix;
  676. const char *p;
  677. int len;
  678. int count;
  679. int scores = 0;
  680. const espeak_VOICE *v;
  681. const char *lang_name;
  682. char age_buf[12];
  683. const espeak_VOICE **voices;
  684. espeak_VOICE voice_select;
  685. static char genders[4] = {' ','M','F',' '};
  686. if(language[0] == '=')
  687. {
  688. // display only voices for the specified language, in order of priority
  689. voice_select.languages = &language[1];
  690. voice_select.age = 0;
  691. voice_select.gender = 0;
  692. voice_select.name = NULL;
  693. voices = espeak_ListVoices(&voice_select);
  694. scores = 1;
  695. }
  696. else
  697. {
  698. voices = espeak_ListVoices(NULL);
  699. }
  700. fprintf(f_out,"Pty Language Age/Gender VoiceName File Other Langs\n");
  701. for(ix=0; (v = voices[ix]) != NULL; ix++)
  702. {
  703. count = 0;
  704. p = v->languages;
  705. while(*p != 0)
  706. {
  707. len = strlen(p+1);
  708. lang_name = p+1;
  709. if(v->age == 0)
  710. strcpy(age_buf," ");
  711. else
  712. sprintf(age_buf,"%3d",v->age);
  713. if(count==0)
  714. {
  715. fprintf(f_out,"%2d %-12s%s%c %-17s %-11s ",
  716. p[0],lang_name,age_buf,genders[v->gender],v->name,v->identifier);
  717. }
  718. else
  719. {
  720. fprintf(f_out,"(%s %d)",lang_name,p[0]);
  721. }
  722. count++;
  723. p += len+2;
  724. }
  725. // if(scores)
  726. // fprintf(f_out,"%3d ",v->score);
  727. fputc('\n',f_out);
  728. }
  729. } // end of DisplayVoices
  730. char *param_string(char **argp)
  731. {//============================
  732. char *p;
  733. int ix=0;
  734. static char buf[80];
  735. p = *argp;
  736. while(*p == ' ') p++;
  737. while(!isspace(*p))
  738. buf[ix++] = *p++;
  739. buf[ix]=0;
  740. *argp = p;
  741. return(buf);
  742. }
  743. int param_number(char **argp)
  744. {//==========================
  745. int value;
  746. char *p;
  747. p = *argp;
  748. while(*p == ' ') p++;
  749. value = atoi(p);
  750. while(!isspace(*p)) p++;
  751. *argp = p;
  752. return(value);
  753. }
  754. void PrintVersion()
  755. {//================
  756. char buf[120];
  757. printf("\nspeak text-to-speech: %s Data at: %s\n", version_string, path_home);
  758. }
  759. void command_line(char *arg_string, int wait)
  760. {//==========================================
  761. int option_index = 0;
  762. int c;
  763. int value;
  764. int speed;
  765. int amp;
  766. int wordgap;
  767. int speaking = 0;
  768. int flag_stdin = 0;
  769. int flag_compile = 0;
  770. int error;
  771. int pitch_adjustment = 50;
  772. char filename[80];
  773. char voicename[40];
  774. char command[80];
  775. char *p;
  776. int ix;
  777. int quiet;
  778. voicename[0] = 0;
  779. wavefile[0] = 0;
  780. filename[0] = 0;
  781. option_linelength = 0;
  782. option_phonemes = 0;
  783. option_waveout = 0;
  784. quiet = 0;
  785. option_multibyte = 0; // auto
  786. option_capitals = 0;
  787. option_punctuation = 0;
  788. option_punctlist[0] = 0;
  789. f_trans = stdout;
  790. p = arg_string;
  791. for(;;)
  792. {
  793. while(*p==' ') p++; // skip spaces
  794. if(*p == '\r') break; // end of line
  795. if(*p == '-')
  796. {
  797. // a command line argument
  798. p++;
  799. switch(*p++)
  800. {
  801. case 'b':
  802. option_multibyte = espeakCHARS_8BIT;
  803. break;
  804. case 'h':
  805. PrintVersion();
  806. printf("\n%s",help_text);
  807. return;
  808. case 'k':
  809. option_capitals = param_number(&p);
  810. SetParameter(espeakCAPITALS,option_capitals,0);
  811. break;
  812. case 'x':
  813. option_phonemes = 1;
  814. break;
  815. case 'X':
  816. option_phonemes = 2;
  817. break;
  818. case 'm':
  819. option_ssml = 1;
  820. break;
  821. case 'p':
  822. pitch_adjustment = param_number(&p);
  823. break;
  824. case 'q':
  825. quiet = 1;
  826. break;
  827. case 'f':
  828. strncpy0(filename,param_string(&p),sizeof(filename));
  829. break;
  830. case 'l':
  831. option_linelength = param_number(&p);
  832. break;
  833. case 'a':
  834. amp = param_number(&p);
  835. SetParameter(espeakVOLUME,amp,0);
  836. break;
  837. case 's':
  838. speed = param_number(&p);
  839. SetParameter(espeakRATE,speed,0);
  840. break;
  841. case 'g':
  842. wordgap = param_number(&p);
  843. SetParameter(espeakWORDGAP,wordgap,0);
  844. break;
  845. case 'v':
  846. strncpy0(voicename,param_string(&p),sizeof(voicename));
  847. break;
  848. case 'w':
  849. option_waveout=1;
  850. strncpy0(wavefile,param_string(&p),sizeof(wavefile));
  851. break;
  852. case '-':
  853. strncpy0(command,param_string(&p),sizeof(command));
  854. if(memcmp(command,"compile=",8)==0)
  855. {
  856. CompileDictionary(NULL,&command[8],NULL,NULL,0);
  857. return;
  858. }
  859. else
  860. if(memcmp(command,"voices",6)==0)
  861. {
  862. DisplayVoices(stdout,&command[6]);
  863. return;
  864. }
  865. else
  866. if(strcmp(command,"help")==0)
  867. {
  868. PrintVersion();
  869. printf("\n%s",help_text);
  870. return;
  871. }
  872. else
  873. if(memcmp(command,"punct",5)==0)
  874. {
  875. option_punctuation = 1;
  876. if((command[5]=='=') && (command[6]=='"'))
  877. {
  878. ix = 0;
  879. while((ix < N_PUNCTLIST) && ((option_punctlist[ix] = command[ix+7]) != 0)) ix++;
  880. option_punctlist[N_PUNCTLIST-1] = 0;
  881. option_punctuation = 2;
  882. }
  883. SetParameter(espeakPUNCTUATION,option_punctuation,0);
  884. }
  885. else
  886. if(memcmp(command,"version",7)==0)
  887. {
  888. PrintVersion();
  889. return;
  890. }
  891. else
  892. if(memcmp(command,"ipa",3)==0)
  893. {
  894. option_phonemes = 3;
  895. }
  896. else
  897. {
  898. printf("Command not recognised\n");
  899. }
  900. break;
  901. default:
  902. printf("Command not recognised\n");
  903. break;
  904. }
  905. }
  906. else
  907. {
  908. break;
  909. }
  910. }
  911. if((option_phonemes > 1) && !option_waveout)
  912. quiet = 1; // can't call sprintf() during callback
  913. SetVoiceByName(voicename);
  914. if((filename[0]==0) && (p[0]=='\r'))
  915. {
  916. // nothing to speak
  917. if(quiet)
  918. {
  919. SpeakNextClause(NULL,NULL,2); // stop speaking
  920. more_text = 0;
  921. }
  922. }
  923. if(option_waveout || quiet)
  924. {
  925. // write speech to a WAV file
  926. if(quiet)
  927. {
  928. OpenWaveFile(NULL,samplerate);
  929. option_waveout = 2;
  930. }
  931. else
  932. {
  933. if(OpenWaveFile(wavefile,samplerate) != 0)
  934. {
  935. fprintf(stderr,"Can't write to output file '%s'\n'",wavefile);
  936. return;
  937. }
  938. }
  939. }
  940. if(pitch_adjustment != 50)
  941. {
  942. PitchAdjust(pitch_adjustment);
  943. }
  944. WavegenSetVoice(voice);
  945. if(filename[0]==0)
  946. speak_text_string(p,'\r',0,wait,-1);
  947. else
  948. speak_file(filename);
  949. }
  950. _kernel_oserror *command_handler(char *arg_string, int argc, int cmd_no, void *pw)
  951. /********************************************************************************/
  952. {
  953. switch(cmd_no)
  954. {
  955. case 0: /* Say <string> */
  956. command_line(arg_string,0); // for compatibility with speak V2
  957. break;
  958. case 1: /* Sayw <string */
  959. command_line(arg_string,1);
  960. break;
  961. case 2: /* speak [options] [<string>] */
  962. command_line(arg_string,0);
  963. break;
  964. }
  965. return(NULL);
  966. } /* end of cmd_handler */
  967. // sound handler data
  968. int current_sound_handler=0;
  969. int prev_sound_handler=0;
  970. int prev_sound_data=0;
  971. int prev_sound_rate=13;
  972. int sound_handler_changed=0;
  973. void RiscosCloseSound()
  974. {//====================
  975. _kernel_swi_regs regs;
  976. if((sound_handler_changed) && (prev_sound_handler != (int)DMA_Handler))
  977. {
  978. // check whether current handler is ours
  979. regs.r[0]=0;
  980. _kernel_swi(0x40145,&regs,&regs);
  981. if(regs.r[1] == (int)DMA_Handler)
  982. {
  983. regs.r[0]=1;
  984. regs.r[1]=prev_sound_handler;
  985. regs.r[2]=prev_sound_data;
  986. _kernel_swi(0x40145,&regs,&regs); // Sound LinearHandler 1
  987. // reset to the previous sample rate
  988. regs.r[0]=3;
  989. regs.r[1]=prev_sound_rate;
  990. _kernel_swi(0x40146,&regs,&regs); // Sound_SampleRate 3
  991. current_sound_handler = prev_sound_handler;
  992. sound_handler_changed = 0;
  993. }
  994. }
  995. } // end of RiscosCloseSound
  996. void RiscosOpenSound()
  997. {//===================
  998. _kernel_swi_regs regs;
  999. if(current_sound_handler != (int)DMA_Handler)
  1000. {
  1001. // register the sound handler
  1002. regs.r[0]=1;
  1003. regs.r[1]=(int)DMA_Handler;
  1004. regs.r[2]=(int)module_data;
  1005. _kernel_swi(0x40145,&regs,&regs); // Sound_LinearHandler 1
  1006. prev_sound_handler = regs.r[1];
  1007. prev_sound_data = regs.r[2];
  1008. // set the sample rate
  1009. regs.r[0]=3;
  1010. regs.r[1]=sample_rate_index;
  1011. regs.r[2]=0;
  1012. _kernel_swi(0x40146,&regs,&regs); // Sound_SampleRate
  1013. prev_sound_rate = regs.r[1];
  1014. current_sound_handler = (int)DMA_Handler;
  1015. sound_handler_changed = 1;
  1016. }
  1017. } // end of RiscosOpenSound
  1018. int callback_handler(_kernel_swi_regs *r, void *pw)
  1019. /*************************************************/
  1020. {
  1021. if(Generate(phoneme_list,&n_phoneme_list,1)==0)
  1022. {
  1023. more_text = SpeakNextClause(NULL,NULL,1);
  1024. }
  1025. if((WcmdqUsed() == 0) && (more_text == 0))
  1026. {
  1027. RiscosCloseSound();
  1028. }
  1029. callback_inhibit = 0;
  1030. return(1);
  1031. } /* end of callback_handler */
  1032. int sound_handler(_kernel_swi_regs *r, void *pw)
  1033. /**********************************************/
  1034. {
  1035. int n_queue;
  1036. int size;
  1037. int *dma_buf;
  1038. int x;
  1039. int ix;
  1040. module_data = (int *)pw;
  1041. dma_buf = (int *)r->r[1];
  1042. size = (r->r[2] - r->r[1])/4;
  1043. FillSoundBuf(size);
  1044. for(ix=0; ix<size; ix++)
  1045. {
  1046. x = SoundBuf[ix];
  1047. dma_buf[ix] = x + (x << 16);
  1048. }
  1049. n_queue = WcmdqUsed();
  1050. r->r[0] = 0;
  1051. if(callback_inhibit == 0)
  1052. {
  1053. // set a callback either:
  1054. // - queue is low and there is more text to be processed
  1055. // - queue is empty and no more text, so callback handler will remove the sound handler
  1056. if(((n_queue < 20) && (more_text != 0)) ||
  1057. ((n_queue==0) && (more_text == 0)))
  1058. {
  1059. callback_inhibit = 1;
  1060. r->r[0] = 1;
  1061. r->r[1] = (int)pw;
  1062. }
  1063. }
  1064. return(1);
  1065. }
  1066. int InitSound16(int sample_rate)
  1067. /******************************/
  1068. /* Find sample rate index */
  1069. {
  1070. int current_rate_index; // current value
  1071. int sound_mode;
  1072. int sound_config;
  1073. int srate;
  1074. int n_srix;
  1075. int ix;
  1076. _kernel_swi_regs regs;
  1077. _kernel_oserror *error;
  1078. sound_mode = 0;
  1079. regs.r[0] = 0;
  1080. error = _kernel_swi(0x40144+os_X,&regs,&regs);
  1081. sound_mode = regs.r[0];
  1082. sound_config = regs.r[1];
  1083. if((error == NULL) && (sound_mode == 1))
  1084. {
  1085. /* 16 bit sound, find sample rate index */
  1086. regs.r[0] = 0;
  1087. regs.r[1] = 0;
  1088. _kernel_swi(0x40146,&regs,&regs);
  1089. n_srix = regs.r[1];
  1090. regs.r[0] = 1;
  1091. regs.r[1] = 0;
  1092. _kernel_swi(0x40146,&regs,&regs);
  1093. current_rate_index = regs.r[1]; // current sample rate index
  1094. srate = regs.r[2];
  1095. for(ix=1; ix<=n_srix; ix++)
  1096. {
  1097. regs.r[0] = 2;
  1098. regs.r[1] = ix;
  1099. _kernel_swi(0x40146,&regs,&regs);
  1100. srate = regs.r[2];
  1101. if(srate >= (sample_rate*1024))
  1102. {
  1103. return(ix);
  1104. }
  1105. }
  1106. }
  1107. return(14); // this was the index for 22050
  1108. } // end of InitSound16
  1109. void RemoveCallback()
  1110. /*******************/
  1111. {
  1112. _kernel_swi_regs regs;
  1113. regs.r[0] = (int)callback_entry;
  1114. regs.r[1] = (int)module_data;
  1115. _kernel_swi(0x5f, &regs, &regs);
  1116. }
  1117. void terminate_module(void)
  1118. /*************************/
  1119. {
  1120. RiscosCloseSound();
  1121. RemoveCallback();
  1122. DeleteTranslator(translator);
  1123. FreePhData();
  1124. } /* end of terminate_module */
  1125. void kill_module(void)
  1126. /********************/
  1127. {
  1128. _kernel_swi_regs regs;
  1129. regs.r[0]=4;
  1130. regs.r[1]=(int)"Speak";
  1131. _kernel_swi(0x1e,&regs,&regs); /* RMKill */
  1132. }
  1133. _kernel_oserror *user_init(char *cmd_fail, int podule_base, void *pw)
  1134. /*******************************************************************/
  1135. {
  1136. _kernel_swi_regs regs;
  1137. _kernel_oserror *error;
  1138. int param;
  1139. // It seems that the wctype functions don't work until the locale has been set
  1140. // to something other than the default "C". Then, not only Latin1 but also the
  1141. // other characters give the correct results with iswalpha() etc.
  1142. static char *locale = "ISO8859-1";
  1143. setlocale(LC_CTYPE,locale);
  1144. module_data = pw;
  1145. sample_rate_index = InitSound16(22050);
  1146. if(initialise() < 0)
  1147. {
  1148. // exit module, errblk.errmess is set by initialise()
  1149. errblk.errnum = 0x101;
  1150. return(&errblk);
  1151. }
  1152. ReadVoiceNames();
  1153. SetVoiceByName("default");
  1154. for(param=0; param<N_SPEECH_PARAM; param++)
  1155. param_stack[0].parameter[param] = param_defaults[param];
  1156. SetParameter(espeakRATE,175,0);
  1157. SetParameter(espeakVOLUME,70,0);
  1158. WavegenSetVoice(voice);
  1159. atexit(terminate_module);
  1160. return(NULL);
  1161. } /* end of user_init */