espeak_io.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /* espeak_io.cpp
  2. *
  3. // * Copyright (C) 2017 David Weenink
  4. *
  5. * This code 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 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /*
  19. djmw 20171024
  20. */
  21. #include "espeakdata_FileInMemory.h"
  22. #include "espeak_ng.h"
  23. #include "speech.h"
  24. #include "synthesize.h"
  25. #include <errno.h>
  26. extern autoFileInMemoryManager espeak_ng_FileInMemoryManager;
  27. #define ESPEAK_FILEINMEMORYMANAGER espeak_ng_FileInMemoryManager.get()
  28. FILE *espeak_io_fopen (const char * filename, const char * mode) {
  29. return FileInMemoryManager_fopen (ESPEAK_FILEINMEMORYMANAGER, filename, mode);
  30. }
  31. void espeak_io_rewind (FILE *stream) {
  32. FileInMemoryManager_rewind (ESPEAK_FILEINMEMORYMANAGER, stream);
  33. }
  34. int espeak_io_fclose (FILE *stream) {
  35. return FileInMemoryManager_fclose (ESPEAK_FILEINMEMORYMANAGER, stream);
  36. }
  37. int espeak_io_feof (FILE *stream) {
  38. return FileInMemoryManager_feof (ESPEAK_FILEINMEMORYMANAGER, stream);
  39. }
  40. long espeak_io_ftell (FILE *stream) {
  41. return FileInMemoryManager_ftell (ESPEAK_FILEINMEMORYMANAGER, stream);
  42. }
  43. int espeak_io_fseek (FILE *stream, long offset, int origin) {
  44. return FileInMemoryManager_fseek (ESPEAK_FILEINMEMORYMANAGER, stream, offset, origin);
  45. }
  46. char *espeak_io_fgets (char *str, int num, FILE *stream) {
  47. return FileInMemoryManager_fgets (ESPEAK_FILEINMEMORYMANAGER, str, num, stream);
  48. }
  49. size_t espeak_io_fread (void *ptr, size_t size, size_t count, FILE *stream) {
  50. return FileInMemoryManager_fread (ESPEAK_FILEINMEMORYMANAGER, ptr, size, count, stream);
  51. }
  52. int espeak_io_fgetc (FILE *stream) {
  53. return FileInMemoryManager_fgetc (ESPEAK_FILEINMEMORYMANAGER, stream);
  54. }
  55. int espeak_io_fprintf (FILE * stream, ... ) {
  56. va_list arg;
  57. va_start (arg, stream);
  58. char *format = static_cast<char *> (va_arg (arg, void*));
  59. int result = FileInMemoryManager_fprintf (ESPEAK_FILEINMEMORYMANAGER, stream, format, arg);
  60. va_end (arg);
  61. return result;
  62. }
  63. int espeak_io_ungetc (int character, FILE * stream) {
  64. return FileInMemoryManager_ungetc (ESPEAK_FILEINMEMORYMANAGER, character,stream);
  65. }
  66. /* This mimics GetFileLength of espeak-ng */
  67. int FileInMemoryManager_GetFileLength (FileInMemoryManager me, const char *filename) {
  68. integer index = FileInMemorySet_lookUp (my files.get(), Melder_peek8to32(filename));
  69. if (index > 0) {
  70. FileInMemory fim = static_cast<FileInMemory> (my files -> at [index]);
  71. return fim -> d_numberOfBytes;
  72. }
  73. // Directory ??
  74. if (FileInMemorySet_hasDirectory (my files.get(), Melder_peek8to32(filename))) {
  75. return -EISDIR;
  76. }
  77. return -1;
  78. }
  79. /*
  80. espeak_io_GetFileLength: mimics GetFileLength of espeak-ng
  81. Returns the number of bytes in the file.
  82. If the filename is a directory it return -EISDIR
  83. */
  84. int espeak_io_GetFileLength (const char *filename) {
  85. FileInMemorySet me = ESPEAK_FILEINMEMORYMANAGER -> files.get();
  86. integer index = FileInMemorySet_lookUp (me, Melder_peek8to32(filename));
  87. if (index > 0) {
  88. FileInMemory fim = static_cast<FileInMemory> (my at [index]);
  89. return fim -> d_numberOfBytes;
  90. }
  91. // Directory ??
  92. if (FileInMemorySet_hasDirectory (me, Melder_peek8to32(filename))) {
  93. return -EISDIR;
  94. }
  95. return -1;
  96. }
  97. /*
  98. espeak_io_GetVoices: mimics GetVoices of espeak-ng
  99. If is_languange_file == 0 then /voices/ else /lang/
  100. We know our voices are in /voices/ and our languages in /lang/
  101. */
  102. void espeak_io_GetVoices (const char *path, int len_path_voices, int is_language_file) {
  103. (void) path;
  104. /*
  105. if is_languange_file == 0 then /voices/ else /lang/
  106. We know our voices are in /voices/!v/ and our languages in /lang/
  107. */
  108. FileInMemoryManager me = ESPEAK_FILEINMEMORYMANAGER;
  109. conststring32 criterion = is_language_file ? U"/lang/" : U"/voices/";
  110. autoFileInMemorySet fileList = FileInMemorySet_listFiles (my files.get(), kMelder_string :: CONTAINS, criterion);
  111. for (long ifile = 1; ifile <= fileList -> size; ifile ++) {
  112. FileInMemory fim = static_cast<FileInMemory> (fileList -> at [ifile]);
  113. FILE *f_voice = FileInMemoryManager_fopen (me, Melder_peek32to8 (fim -> d_path.get()), "r");
  114. conststring8 fname = Melder_peek32to8 (fim -> d_path.get());
  115. espeak_VOICE *voice_data = ReadVoiceFile (f_voice, fname + len_path_voices, is_language_file);
  116. FileInMemoryManager_fclose (me, f_voice);
  117. if (voice_data) {
  118. voices_list [n_voices_list ++] = voice_data;
  119. } /*else {
  120. Melder_warning (U"Voice data for ", fname, U" could not be gathered.");
  121. }*/
  122. }
  123. }
  124. int get_int32_le (char *ch) {
  125. return (((uint8)ch[0]<<0) | ((uint8)ch[1]<<8) | ((uint8)ch[2]<<16) | ((uint8)ch[3]<<24));
  126. }
  127. short get_int16_le (char *ch) {
  128. return (((uint8)ch[0]<<0) | ((uint8)ch[1]<<8));
  129. }
  130. int get_set_int32_le (char *ch) {
  131. int i32 = (((uint8)ch[0]<<0) | ((uint8)ch[1]<<8) | ((uint8)ch[2]<<16) | ((uint8)ch[3]<<24));
  132. int *p32 = (int *) ch;
  133. *p32 = i32;
  134. return i32;
  135. }
  136. /*
  137. The espeak-ng data files have been written with little endian byte order. To be able to use these files on big endian hardware we have to change these files as if they were written on a big endian machine.
  138. The following routines were modeled after espeak-phonemedata.c by Jonathan Duddington.
  139. A serious bug in his code for the phontab_to_bigendian procedure has been corrected.
  140. A better solution would be:
  141. espeak-ng should read a little endian int32 as 4 unsigned bytes:
  142. int32 i = (ch[0]<<0) | (ch[1]<<8) | (ch[2]<<16) | (ch[3]<<24);
  143. a int16 (short) as 2 unsigned bytes:
  144. int16 i = (ch[0]<<0) | (ch[1]<<8);
  145. Then no conversion of data files would be necessary.
  146. */
  147. #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
  148. #define SWAP_2(i1) { integer i2 = i1 + 1; \
  149. thy d_data [i1] = my d_data [i2]; \
  150. thy d_data [i2] = my d_data [i1]; }
  151. #define SWAP_4(i1) { integer i2 = i1 + 1, i3 = i1 + 2, i4 = i1 + 3; \
  152. thy d_data [i1] = my d_data [i4]; \
  153. thy d_data [i2] = my d_data [i3]; \
  154. thy d_data [i3] = my d_data [i2]; \
  155. thy d_data [i4] = my d_data [i1]; }
  156. #else
  157. #define SWAP_2(i1)
  158. #define SWAP_4(i1)
  159. #endif
  160. static autoFileInMemory phondata_to_bigendian (FileInMemory me, FileInMemory manifest) {
  161. try {
  162. autoFileInMemory thee = Data_copy (me);
  163. FILE *phondataf = fopen (Melder_peek32to8 (my d_path.get()), "r");
  164. FILE *manifestf = fopen (Melder_peek32to8 (manifest -> d_path.get()), "r");
  165. char line [1024];
  166. // copy 4 bytes: version number
  167. // copy 4 bytes: sample rate
  168. while (fgets (line, sizeof (line), manifestf)) {
  169. if (! isupper (line [0])) continue;
  170. unsigned int index;
  171. sscanf(& line [2], "%x", & index);
  172. fseek (phondataf, index, SEEK_SET);
  173. integer i1 = index;
  174. if (line [0] == 'S') { //
  175. /*
  176. typedef struct {
  177. short length;
  178. unsigned char n_frames;
  179. unsigned char sqflags;
  180. frame_t frame[N_SEQ_FRAMES];
  181. } SPECT_SEQ;
  182. */
  183. SWAP_2 (i1)
  184. index += 2; // skip the short length
  185. integer numberOfFrames = my d_data [index]; // unsigned char n_frames
  186. index += 2; // skip the 2 unsigned char's n_frames & sqflags
  187. for (integer n = 1; n <= numberOfFrames; n ++) {
  188. /*
  189. typedef struct { //64 bytes
  190. short frflags;
  191. short ffreq[7];
  192. unsigned char length;
  193. unsigned char rms;
  194. unsigned char fheight[8];
  195. unsigned char fwidth[6]; // width/4 f0-5
  196. unsigned char fright[3]; // width/4 f0-2
  197. unsigned char bw[4]; // Klatt bandwidth BNZ /2, f1,f2,f3
  198. unsigned char klattp[5]; // AV, FNZ, Tilt, Aspr, Skew
  199. unsigned char klattp2[5]; // continuation of klattp[], Avp, Fric, FricBP, Turb
  200. unsigned char klatt_ap[7]; // Klatt parallel amplitude
  201. unsigned char klatt_bp[7]; // Klatt parallel bandwidth /2
  202. unsigned char spare; // pad to multiple of 4 bytes
  203. } frame_t; // with extra Klatt parameters for parallel resonators
  204. typedef struct { // 44 bytes
  205. short frflags;
  206. short ffreq[7];
  207. unsigned char length;
  208. unsigned char rms;
  209. unsigned char fheight[8];
  210. unsigned char fwidth[6]; // width/4 f0-5
  211. unsigned char fright[3]; // width/4 f0-2
  212. unsigned char bw[4]; // Klatt bandwidth BNZ /2, f1,f2,f3
  213. unsigned char klattp[5]; // AV, FNZ, Tilt, Aspr, Skew
  214. } frame_t2;
  215. Both frame_t and frame_t2 start with 8 short's.
  216. */
  217. i1 = index;
  218. for (integer i = 1; i <= 8; i ++) {
  219. SWAP_2 (i1)
  220. i1 += 2;
  221. }
  222. //
  223. #define FRFLAG_KLATT 0x01
  224. index += (thy d_data [i1] & FRFLAG_KLATT) ? sizeof (frame_t) : sizeof (frame_t2); // thy is essential!
  225. }
  226. } else if (line [0] == 'W') { // Wave data
  227. int length = my d_data [i1 + 1] * 256 + my d_data [i1];
  228. index += 4;
  229. index += length; // char wavedata[length]
  230. index += index % 3;
  231. } else if (line [0] == 'E') {
  232. index += 128; // Envelope: skip 128 bytes
  233. } else if (line [0] == 'Q') {
  234. unsigned int length = my d_data [index + 2] << 8 + my d_data [index + 3];
  235. length *= 4;
  236. index += length;
  237. }
  238. Melder_require (index <= my d_numberOfBytes, U"Position ", index, U"is larger than file length (", my d_numberOfBytes, U").");
  239. }
  240. return thee;
  241. } catch (MelderError) {
  242. Melder_throw (U"phondata not converted to bigendian.");
  243. }
  244. }
  245. static autoFileInMemory phontab_to_bigendian (FileInMemory me) {
  246. try {
  247. autoFileInMemory thee = Data_copy (me);
  248. integer numberOfPhonemeTables = my d_data [0];
  249. integer index = 4; // skip first 4 bytes
  250. for (integer itab = 1; itab <= numberOfPhonemeTables; itab ++) {
  251. integer numberOfPhonemes = thy d_data [index];
  252. index += 4; // This is 8 (incorrect) in the original code of espeak.
  253. index += N_PHONEME_TAB_NAME; // skip the name
  254. integer phonemeTableSizes = numberOfPhonemes * sizeof (PHONEME_TAB);
  255. Melder_require (index + phonemeTableSizes <= my d_numberOfBytes, U"Too many tables to process. (table ", itab, U" from ", numberOfPhonemeTables, U").");
  256. for (integer j = 1; j <= numberOfPhonemes; j ++) {
  257. /*
  258. typedef struct { // 16 bytes
  259. unsigned int mnemonic; // 1st char is in the l.s.byte
  260. unsigned int phflags; // bits 16-19 place of articulation
  261. unsigned short program;
  262. unsigned char code; // the phoneme number
  263. unsigned char type; // phVOWEL, phPAUSE, phSTOP etc
  264. unsigned char start_type;
  265. unsigned char end_type;
  266. unsigned char std_length; // for vowels, in mS/2; for phSTRESS, the stress/tone type
  267. unsigned char length_mod; // a length_mod group number, used to access length_mod_tab
  268. } PHONEME_TAB;
  269. */
  270. integer i1 = index;
  271. SWAP_4 (i1)
  272. i1 += 4;
  273. SWAP_4 (i1);
  274. i1 += 4;
  275. SWAP_2 (i1)
  276. index += sizeof (PHONEME_TAB);
  277. }
  278. Melder_require (index <= my d_numberOfBytes, U"Position ", index, U" is larger than file length (", my d_numberOfBytes, U").");
  279. }
  280. return thee;
  281. } catch (MelderError) {
  282. Melder_throw (U"phontab not converted to bigendian.");
  283. }
  284. }
  285. static autoFileInMemory phonindex_to_bigendian (FileInMemory me) {
  286. try {
  287. autoFileInMemory thee = Data_copy (me);
  288. integer numberOfShorts = (my d_numberOfBytes - 4 - 1) / 2;
  289. integer index = 4; // skip first 4 bytes
  290. for (integer i = 0; i < numberOfShorts; i ++) {
  291. SWAP_2 (index)
  292. index += 2;
  293. Melder_require (index <= my d_numberOfBytes, U"Position ", index, U" is larger than file length (", my d_numberOfBytes, U").");
  294. }
  295. return thee;
  296. } catch (MelderError) {
  297. Melder_throw (U"phonindex not converted to bigendian.");
  298. }
  299. }
  300. void espeak_ng_data_to_bigendian () {
  301. FileInMemoryManager me = ESPEAK_FILEINMEMORYMANAGER;
  302. autoMelderString file;
  303. MelderString_append (& file, Melder_peek8to32 (PATH_ESPEAK_DATA), U"/phondata-manifest");
  304. integer index = FileInMemorySet_lookUp (my files.get(), file.string);
  305. Melder_require (index > 0, U"phondata-manifest not present.");
  306. FileInMemory manifest = (FileInMemory) my files -> at [index];
  307. MelderString_empty (& file);
  308. MelderString_append (& file, Melder_peek8to32 (PATH_ESPEAK_DATA), U"/phondata");
  309. index = FileInMemorySet_lookUp (my files.get(), file.string);
  310. Melder_require (index > 0, U"phondata not present.");
  311. FileInMemory phondata = (FileInMemory) my files -> at [index];
  312. autoFileInMemory phondata_new = phondata_to_bigendian (phondata, manifest);
  313. my files -> replaceItem_move (phondata_new.move(), index);
  314. MelderString_empty (& file);
  315. MelderString_append (& file, Melder_peek8to32 (PATH_ESPEAK_DATA), U"/phontab");
  316. index = FileInMemorySet_lookUp (my files.get(), file.string);
  317. Melder_require (index > 0, U"phonindex not present.");
  318. FileInMemory phontab = (FileInMemory) my files -> at [index];
  319. autoFileInMemory phontab_new = phontab_to_bigendian (phontab);
  320. my files -> replaceItem_move (phontab_new.move(), index);
  321. MelderString_empty (& file);
  322. MelderString_append (& file, Melder_peek8to32 (PATH_ESPEAK_DATA), U"/phonindex");
  323. index = FileInMemorySet_lookUp (my files.get(), file.string);
  324. Melder_require (index > 0, U"phonindex not present.");
  325. FileInMemory phonindex = (FileInMemory) my files -> at [index];
  326. autoFileInMemory phonindex_new = phonindex_to_bigendian (phonindex);
  327. my files -> replaceItem_move (phonindex_new.move(), index);
  328. }
  329. /* End of file espeak_io.cpp */