eSpeak NG is an open source speech synthesizer that supports more than hundred languages and accents.

compiledata.cpp 45KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138
  1. /***************************************************************************
  2. * Copyright (C) 2005, 2006 by Jonathan Duddington *
  3. * jonsd@users.sourceforge.net *
  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 2 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 to the *
  17. * Free Software Foundation, Inc., *
  18. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
  19. ***************************************************************************/
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <ctype.h>
  23. #include <stdlib.h>
  24. #include "wx/wx.h"
  25. #include "wx/wfstream.h"
  26. #include "wx/dir.h"
  27. #include "wx/filename.h"
  28. #include "speak_lib.h"
  29. #include "main.h"
  30. #include "speech.h"
  31. #include "voice.h"
  32. #include "spect.h"
  33. #include "options.h"
  34. #include "phoneme.h"
  35. #include "synthesize.h"
  36. #define tNUMBER 1
  37. #define tSTRING 2
  38. #define tPHONEMEMNEM 3
  39. #define tKEYWORD 4
  40. #define tSIGNEDNUMBER 5
  41. #define tPHONEMESTART 1
  42. #define tEND 2
  43. #define tSPECT 3
  44. #define tWAVE 4
  45. #define tSTARTTYPE 5
  46. #define tENDTYPE 6
  47. #define tBEFORE 7
  48. #define tAFTER 8
  49. #define tTONESPEC 9
  50. #define tLENGTHMOD 11
  51. #define tLENGTH 12
  52. #define tLONGLENGTH 13
  53. #define tOLDNAME 14
  54. #define tREDUCETO 15
  55. #define tFIXEDCODE 16
  56. #define tBEFOREVOWEL 17
  57. #define tBEFOREVOWELPAUSE 18
  58. #define tBEFORENOTVOWEL 19
  59. #define tLINKOUT 20
  60. #define tSWITCHVOICING 21
  61. #define tVOWELIN 22
  62. #define tVOWELOUT 23
  63. #define tAPPENDPH 24
  64. #define tPHONEMENUMBER 29
  65. #define tPHONEMETABLE 30
  66. #define tINCLUDE 31
  67. extern void Write4Bytes(FILE *f, int value);
  68. extern void MakeVowelLists(void);
  69. extern int CompileDictionary(const char *dsource, const char *dict_name, FILE *log, char *fname);
  70. extern char voice_name[];
  71. static int markers_used[8];
  72. #define N_USED_BY 12
  73. typedef struct {
  74. void *link;
  75. int value;
  76. short n_uses;
  77. short n_used_by;
  78. unsigned char used_by[N_USED_BY];
  79. char string[1];
  80. } REF_HASH_TAB;
  81. class Compile
  82. {//==========
  83. public:
  84. Compile();
  85. void CPhonemeTab(const char *path);
  86. private:
  87. int CPhoneme();
  88. void StartPhonemeTable(const char *name);
  89. void EndPhonemeTable();
  90. void CPhonemeFiles(char *path_source);
  91. int NextItem(int type);
  92. void UngetItem();
  93. void Error(const char *msg);
  94. void Error(const char *msg, const char *msg2);
  95. void Summary(FILE *f);
  96. void SummarySpect(FILE *f,unsigned int value);
  97. void SummarySpectList(FILE *f,int ref,const char *label);
  98. const char *PhonemeMnem(int phcode);
  99. void WritePhonemeTable();
  100. void Report();
  101. int LookupPhoneme(const char *string, int declare);
  102. void ReservePhCode(const char *string, int value);
  103. const char *GetKeyMnem(int value);
  104. int LoadSpect(const char *path, int control);
  105. int LoadWavefile(FILE *f, const char *fname);
  106. int LoadEnvelope(FILE *f, const char *fname);
  107. int LoadDataFile(const char *path, int control);
  108. int AddSpect(int phcode, int *list, int control);
  109. void AddSpectList(int *list, int control);
  110. void AddEnvelope(int *list);
  111. void VowelTransition(int which, unsigned int *trans);
  112. FILE *f_in;
  113. FILE *f_phdata;
  114. FILE *f_phcontents;
  115. FILE *f_errors;
  116. FILE *f_phindex;
  117. FILE *f_phtab;
  118. int f_in_displ;
  119. int f_in_linenum;
  120. PHONEME_TAB *ph;
  121. int linenum;
  122. int item_type;
  123. char current_fname[80];
  124. char item_string[256];
  125. char phoneme_tab_name[N_PHONEME_TAB_NAME];
  126. unsigned int vowel_in[2];
  127. unsigned int vowel_out[2];
  128. int n_phcodes_list[N_PHONEME_TABS];
  129. PHONEME_TAB_LIST phoneme_tab_list2[N_PHONEME_TABS];
  130. PHONEME_TAB *phoneme_tab2;
  131. int n_phoneme_tabs;
  132. int n_phcodes;
  133. int count_references;
  134. int duplicate_references;
  135. int count_frames;
  136. int error_count;
  137. REF_HASH_TAB *ref_hash_tab[256];
  138. typedef struct {
  139. FILE *file;
  140. int linenum;
  141. char fname[80];
  142. } STACK;
  143. #define N_STACK 12
  144. int stack_ix;
  145. STACK stack[N_STACK];
  146. };
  147. char path_source[80];
  148. Compile *compile;
  149. typedef struct {
  150. const char *mnem;
  151. int data;
  152. } keywtab_t;
  153. static keywtab_t keywords[] = {
  154. {"vowel", 0x1000000+phVOWEL},
  155. {"liquid", 0x1000000+phLIQUID},
  156. {"pause", 0x1000000+phPAUSE},
  157. {"stress", 0x1000000+phSTRESS},
  158. {"virtual", 0x1000000+phVIRTUAL},
  159. {"fricative", 0x1000000+phFRICATIVE},
  160. {"vstop", 0x1000000+phVSTOP},
  161. {"vfricative",0x1000000+phVFRICATIVE},
  162. // type of consonant
  163. {"stop", 0x1000000+phSTOP},
  164. {"frc", 0x1000000+phFRICATIVE},
  165. {"nasal", 0x1000000+phNASAL},
  166. {"flp", 0x1000000+phVSTOP},
  167. {"afr", 0x1000000+phSTOP}, // treat as stop
  168. {"apr", 0x1000000+phFRICATIVE}, // [h] voiceless approximant
  169. // keywords
  170. {"phonemenumber", 29},
  171. {"phonemetable", 30},
  172. {"include", 31},
  173. {"phoneme", 1},
  174. {"endphoneme", 2},
  175. {"formants", 3},
  176. {"wave", 4},
  177. {"starttype",5},
  178. {"endtype", 6},
  179. {"before", 7},
  180. {"after", 8},
  181. {"tone", 9},
  182. {"lengthmod",11},
  183. {"length", 12},
  184. {"longlength", 13},
  185. {"reduceto", 15},
  186. {"beforevowel", 17},
  187. {"beforevowelpause", 18},
  188. {"beforenotvowel",19},
  189. {"linkout",20},
  190. {"switchvoicing",21},
  191. {"vowelin",22},
  192. {"vowelout",23},
  193. {"appendph",24},
  194. // flags
  195. {"wavef", 0x2000000+phWAVE},
  196. {"unstressed", 0x2000000+phUNSTRESSED},
  197. {"fortis", 0x2000000+phFORTIS},
  198. {"sibilant", 0x2000000+phSIBILANT},
  199. {"nolink", 0x2000000+phNOLINK},
  200. {"trill", 0x2000000+phTRILL},
  201. {"vowel2", 0x2000000+phVOWEL2},
  202. {"palatal", 0x2000000+phPALATAL},
  203. {"long", 0x2000000+phLONG},
  204. // voiced / unvoiced
  205. {"vcd", 0x2000000+phVOICED},
  206. {"vls", 0x2000000+phFORTIS},
  207. // place of articulation
  208. {"blb", 0x2010000},
  209. {"lbd", 0x2020000},
  210. {"dnt", 0x2030000},
  211. {"alv", 0x2040000},
  212. {"rfx", 0x2050000},
  213. {"pla", 0x2060000},
  214. {"pal", 0x2070000},
  215. {"vel", 0x2080000},
  216. {"lbv", 0x2090000},
  217. {"uvl", 0x20a0000},
  218. {"phr", 0x20b0000},
  219. {"glt", 0x20c0000},
  220. // vowel transition attributes
  221. {"len=", 0x3000001},
  222. {"rms=", 0x3000002},
  223. {"f1=", 0x3000003},
  224. {"f2=", 0x3000004},
  225. {"f3=", 0x3000005},
  226. {"brk", 0x3000006},
  227. {"rate", 0x3000007},
  228. {"glstop", 0x3000008},
  229. {"lenadd", 0x3000009},
  230. {"f4", 0x300000a},
  231. {"paus", 0x300000b},
  232. {"colr=",0x300000c},
  233. {"", -1}
  234. };
  235. extern void strncpy0(char *to,const char *from, int size);
  236. static unsigned int StringToWord(const char *string)
  237. {//=================================================
  238. // Pack 4 characters into a word
  239. int ix;
  240. unsigned char c;
  241. unsigned int word;
  242. word = 0;
  243. for(ix=0; ix<4; ix++)
  244. {
  245. if(string[ix]==0) break;
  246. c = string[ix];
  247. word |= (c << (ix*8));
  248. }
  249. return(word);
  250. }
  251. int Read4Bytes(FILE *f)
  252. {//====================
  253. // Read 4 bytes (least significant first) into a word
  254. int ix;
  255. unsigned char c;
  256. int acc=0;
  257. for(ix=0; ix<4; ix++)
  258. {
  259. c = fgetc(f) & 0xff;
  260. acc += (c << (ix*8));
  261. }
  262. return(acc);
  263. }
  264. static FILE *fopen_log(FILE *f_log, const char *fname,const char *access)
  265. {//==================================================
  266. // performs fopen, but produces error message to f_log if it fails
  267. FILE *f;
  268. if((f = fopen(fname,access)) == NULL)
  269. {
  270. if(f_log != NULL)
  271. fprintf(f_log,"Can't access (%s) file '%s'\n",access,fname);
  272. }
  273. return(f);
  274. }
  275. int Hash8(const char *string)
  276. //===========================
  277. /* Generate a hash code from the specified string */
  278. {
  279. int c;
  280. int chars=0;
  281. int hash=0;
  282. while((c = *string++) != 0)
  283. {
  284. c = tolower(c) - 'a';
  285. hash = hash * 8 + c;
  286. hash = (hash & 0x1ff) ^ (hash >> 8); /* exclusive or */
  287. chars++;
  288. }
  289. return((hash+chars) & 0xff);
  290. } // end of Hash8
  291. Compile::Compile()
  292. {//===============
  293. linenum = 0;
  294. }
  295. void Compile::Error(const char *msg)
  296. {//=================================
  297. fprintf(f_errors,"%3d: %s\n",linenum,msg);
  298. error_count++;
  299. }
  300. void Compile::Error(const char *msg, const char *msg2)
  301. {//=================================
  302. fprintf(f_errors,"%3d: %s: '%s'\n",linenum,msg,msg2);
  303. error_count++;
  304. }
  305. const char *Compile::PhonemeMnem(int phcode)
  306. {//==========================================
  307. if((phcode >= 0) && (phcode < N_PHONEME_TAB))
  308. return(WordToString(phoneme_tab2[phcode].mnemonic));
  309. else
  310. return("???");
  311. }
  312. const char *Compile::GetKeyMnem(int value)
  313. {//=======================================
  314. keywtab_t *p;
  315. p = keywords;
  316. while(p->data != -1)
  317. {
  318. if(p->data == value)
  319. return(p->mnem);
  320. p++;
  321. }
  322. return("???");
  323. }
  324. void Compile::ReservePhCode(const char *string, int value)
  325. {//=======================================================
  326. // Use a fixed number for this phoneme. Must be used before any
  327. // phonemes are declared
  328. unsigned int word;
  329. word = StringToWord(string);
  330. phoneme_tab2[value].mnemonic = word;
  331. phoneme_tab2[value].code = value;
  332. if(n_phcodes <= value)
  333. n_phcodes = value+1;
  334. }
  335. int Compile::LookupPhoneme(const char *string, int control)
  336. {//========================================================
  337. // control = 0 explicit declaration
  338. // control = 1 declare phoneme if not found
  339. // control = 2 start looking after control & stress phonemes
  340. int ix;
  341. int start;
  342. int use;
  343. unsigned int word;
  344. if(strcmp(string,"NULL")==0)
  345. return(1);
  346. ix = strlen(string);
  347. if((ix==0) || (ix> 4))
  348. {
  349. Error("Bad phoneme",string);
  350. }
  351. word = StringToWord(string);
  352. // don't use phoneme number 0, reserved for string terminator
  353. start = 1;
  354. if(control==2)
  355. start = 8; // don't look for control and stress phonemes (allows these characters to be
  356. // used for other purposes)
  357. use = 0;
  358. for(ix=start; ix<n_phcodes; ix++)
  359. {
  360. if(phoneme_tab2[ix].mnemonic == word)
  361. return(ix);
  362. if((use==0) && (phoneme_tab2[ix].mnemonic == 0))
  363. {
  364. use = ix;
  365. }
  366. }
  367. // if(control != 1) return(-1); // not found
  368. if(use == 0)
  369. {
  370. if(n_phcodes >= N_PHONEME_TAB-1)
  371. return(-1); // phoneme table is full
  372. use = n_phcodes++;
  373. }
  374. // add this phoneme to the phoneme table
  375. phoneme_tab2[use].mnemonic = word;
  376. phoneme_tab2[use].type = phINVALID;
  377. phoneme_tab2[use].spect = linenum; // for error report if the phoneme remains undeclared
  378. return(use);
  379. } // end of Compile::LookupPhoneme
  380. int Compile::NextItem(int type)
  381. {//============================
  382. int acc;
  383. unsigned char c=0;
  384. unsigned char c2;
  385. int ix;
  386. int sign;
  387. int keyword;
  388. keywtab_t *p;
  389. f_in_displ = ftell(f_in);
  390. f_in_linenum = linenum;
  391. while(!feof(f_in))
  392. {
  393. c = fgetc(f_in);
  394. if(c=='/')
  395. {
  396. if((c2 = fgetc(f_in))=='/')
  397. {
  398. // comment, ignore to end of line
  399. while(!feof(f_in) && ((c=fgetc(f_in)) != '\n'));
  400. }
  401. else
  402. {
  403. ungetc(c2,f_in);
  404. }
  405. }
  406. if(c == '\n') linenum++;
  407. if(!isspace(c))
  408. {
  409. break;
  410. }
  411. }
  412. if(feof(f_in))
  413. return(tEND);
  414. if((type == tNUMBER) || (type == tSIGNEDNUMBER))
  415. {
  416. acc = 0;
  417. sign = 1;
  418. if((c == '-') && (type == tSIGNEDNUMBER))
  419. {
  420. sign = -1;
  421. c = fgetc(f_in);
  422. }
  423. if(!isdigit(c))
  424. Error("Expected number");
  425. while(isdigit(c))
  426. {
  427. acc *= 10;
  428. acc += (c - '0');
  429. c = fgetc(f_in);
  430. }
  431. ungetc(c,f_in);
  432. return(acc * sign);
  433. }
  434. ix = 0;
  435. while(!feof(f_in) && !isspace(c))
  436. {
  437. item_string[ix++] = c;
  438. c = fgetc(f_in);
  439. if(item_string[ix-1] == '=')
  440. break;
  441. }
  442. ungetc(c,f_in);
  443. item_string[ix] = 0;
  444. if(feof(f_in)) return(-1);
  445. keyword = -1;
  446. if(type == tSTRING)
  447. {
  448. return(0);
  449. }
  450. if(type == tKEYWORD)
  451. {
  452. p = keywords;
  453. while(p->data != -1)
  454. {
  455. if(strcmp(item_string,p->mnem)==0)
  456. {
  457. item_type = p->data >> 24;
  458. return(p->data & 0xffffff);
  459. }
  460. p++;
  461. }
  462. item_type = -1;
  463. return(-1); // keyword not found
  464. }
  465. if(type == tPHONEMEMNEM)
  466. {
  467. return(LookupPhoneme(item_string,2));
  468. }
  469. return(-1);
  470. } // end of Compile::NextItem
  471. void Compile::UngetItem()
  472. {//======================
  473. fseek(f_in,f_in_displ,SEEK_SET);
  474. linenum = f_in_linenum;
  475. } // end of Compile::UngetItem
  476. int Compile::LoadSpect(const char *path, int control)
  477. {//=================================================
  478. SpectSeq *spectseq;
  479. int peak;
  480. int displ;
  481. int frame;
  482. int ix;
  483. int x;
  484. int rms;
  485. float total;
  486. float pkheight;
  487. SpectFrame *fr;
  488. wxString path_sep = _T("/");
  489. SPECT_SEQ seq_out;
  490. // create SpectSeq and import data
  491. spectseq = new SpectSeq;
  492. if(spectseq == NULL)
  493. {
  494. Error("Failed to create SpectSeq");
  495. return(0);
  496. }
  497. wxString filename = path_phsource + path_sep + wxString(path,wxConvLocal);
  498. wxFileInputStream stream(filename);
  499. if(stream.Ok() == FALSE)
  500. {
  501. Error("Failed to open",path);
  502. return(0);
  503. }
  504. spectseq->Load(stream);
  505. displ = ftell(f_phdata);
  506. seq_out.n_frames=0;
  507. seq_out.flags=0;
  508. seq_out.length=0;
  509. total = 0;
  510. for(frame=0; frame < spectseq->numframes; frame++)
  511. {
  512. #ifdef deleted
  513. for(ix=0; ix<8; ix++)
  514. {
  515. // find which files have certain markers set
  516. if(spectseq->frames[frame]->markers & (1<<ix))
  517. {
  518. markers_used[ix]++;
  519. if((ix==3) || (ix==4))
  520. {
  521. fprintf(f_errors,"Marker %d: %s\n",ix,path);
  522. }
  523. }
  524. }
  525. #endif
  526. if(spectseq->frames[frame]->keyframe)
  527. {
  528. seq_out.n_frames++;
  529. if(frame > 0)
  530. total += spectseq->frames[frame-1]->length;
  531. }
  532. }
  533. seq_out.length = int(total);
  534. if((control & 1) && (spectseq->numframes > 2))
  535. {
  536. // set a marker flag for the second frame of a vowel
  537. spectseq->frames[1]->markers |= FRFLAG_VOWEL_CENTRE;
  538. }
  539. ix = 0;
  540. for(frame=0; frame < spectseq->numframes; frame++)
  541. {
  542. fr = spectseq->frames[frame];
  543. if(fr->keyframe)
  544. {
  545. x = int(fr->length + 0.5); // round to nearest mS
  546. if(x > 255) x = 255;
  547. seq_out.frame[ix].length = x;
  548. seq_out.frame[ix].frflags = fr->markers;
  549. rms = int(fr->GetRms(spectseq->amplitude));
  550. if(rms > 255) rms = 255;
  551. seq_out.frame[ix].rms = rms;
  552. if(ix == (seq_out.n_frames-1))
  553. seq_out.frame[ix].length = 0; // give last frame zero length
  554. // write: peak data
  555. count_frames++;
  556. for(peak=0; peak<N_PEAKS; peak++)
  557. {
  558. seq_out.frame[ix].ffreq[peak] = fr->peaks[peak].pkfreq;
  559. pkheight = spectseq->amplitude * fr->amp_adjust * fr->peaks[peak].pkheight;
  560. pkheight = pkheight/640000;
  561. if(pkheight > 255) pkheight = 255;
  562. seq_out.frame[ix].fheight[peak] = int(pkheight);
  563. if(peak < 6)
  564. {
  565. x = fr->peaks[peak].pkwidth/4;
  566. if(x > 255) x = 255;
  567. seq_out.frame[ix].fwidth[peak] = x;
  568. x = fr->peaks[peak].pkright/4;
  569. if(x > 255) x = 255;
  570. seq_out.frame[ix].fright[peak] = x;
  571. }
  572. }
  573. #ifdef LOG_DETAIL
  574. fprintf(f_errors,"Frame %d %3dmS rms=%3d flags=%2d pk=%4d %4d %4d",ix,seq_out.frame[ix].length,
  575. seq_out.frame[ix].rms,seq_out.frame[ix].flags,
  576. seq_out.frame[ix].peaks[1].pkfreq,seq_out.frame[ix].peaks[2].pkfreq,seq_out.frame[ix].peaks[3].pkfreq);
  577. if(fr->markers != 0)
  578. {
  579. fprintf(f_errors," [%x]",fr->markers);
  580. }
  581. fputc('\n',f_errors);
  582. #endif
  583. ix++;
  584. }
  585. }
  586. ix = (char *)(&seq_out.frame[seq_out.n_frames]) - (char *)(&seq_out);
  587. ix = (ix+3) & 0xfffc; // round up to multiple of 4 bytes
  588. fwrite(&seq_out,ix,1,f_phdata);
  589. delete spectseq;
  590. return(displ);
  591. } // end of Compile::LoadSpect
  592. int Compile::LoadWavefile(FILE *f, const char *fname)
  593. {//==================================================
  594. int displ;
  595. unsigned char c1;
  596. unsigned char c3;
  597. int c2;
  598. int sample;
  599. int sample2;
  600. float x;
  601. int max = 0;
  602. int length;
  603. int sr1, sr2;
  604. int resample_wav = 0;
  605. char fname_temp[100];
  606. int scale_factor=0;
  607. char command[200];
  608. fseek(f,24,SEEK_SET);
  609. sr1 = Read4Bytes(f);
  610. sr2 = Read4Bytes(f);
  611. fseek(f,40,SEEK_SET);
  612. if((sr1 != samplerate) || (sr2 != sr1*2))
  613. {
  614. #ifdef PLATFORM_WINDOWS
  615. if(sr1 != samplerate)
  616. {
  617. fprintf(f_errors,"Wrong samplerate %d, wants %d\n",sr1,samplerate);
  618. Error("Wrong samplerate: ",fname);
  619. }
  620. if(sr2 != sr1*2)
  621. {
  622. Error("Not mono: ",fname);
  623. }
  624. #else
  625. sprintf(fname_temp,"%s.wav",tmpnam(NULL));
  626. sprintf(command,"sox \"%s%s.wav\" -r %d -c 1 -w %s polyphase\n",path_source,fname,samplerate,fname_temp);
  627. if(system(command) < 0)
  628. {
  629. Error("Failed to resample: ",command);
  630. return(0);
  631. }
  632. f = fopen(fname_temp,"rb");
  633. if(f == NULL)
  634. {
  635. Error("Can't read temp file: ",fname_temp);
  636. return(0);
  637. }
  638. resample_wav = 1;
  639. fseek(f,40,SEEK_SET); // skip past the WAV header, up to before "data length"
  640. #endif
  641. }
  642. displ = ftell(f_phdata);
  643. // data contains: 4 bytes of length (n_samples * 2), followed by 2-byte samples (lsb byte first)
  644. length = Read4Bytes(f);
  645. while(!feof(f))
  646. {
  647. c1 = fgetc(f);
  648. c3 = fgetc(f);
  649. if(feof(f)) break;
  650. c2 = c3 << 24;
  651. c2 = c2 >> 16; // sign extend
  652. sample = (c1 & 0xff) + c2;;
  653. if(sample > max)
  654. max = sample;
  655. else
  656. if(sample < -max)
  657. max = -sample;
  658. }
  659. if(max > 1)
  660. {
  661. scale_factor = (max / 127) + 1;
  662. }
  663. else
  664. scale_factor = 0;
  665. //fprintf(f_errors," sample len=%d max=%4x shift=%d\n",length,max,scale_factor);
  666. #define MIN_FACTOR 6
  667. if(scale_factor > MIN_FACTOR)
  668. {
  669. length = length/2 + (scale_factor << 16);
  670. }
  671. Write4Bytes(f_phdata,length);
  672. // fwrite(&length,4,1,f_phdata);
  673. fseek(f,44,SEEK_SET);
  674. while(!feof(f))
  675. {
  676. c1 = fgetc(f);
  677. c3 = fgetc(f);
  678. c2 = c3 << 24;
  679. c2 = c2 >> 16; // sign extend
  680. sample = (c1 & 0xff) + c2;
  681. if(feof(f)) break;
  682. if(scale_factor <= MIN_FACTOR)
  683. {
  684. fputc(sample & 0xff,f_phdata);
  685. fputc(sample >> 8,f_phdata);
  686. }
  687. else
  688. {
  689. x = (float(sample) / scale_factor) + 0.5;
  690. sample2= int(x);
  691. if(sample2 > 127)
  692. sample2 = 127;
  693. if(sample2 < -128)
  694. sample2 = -128;
  695. fputc(sample2,f_phdata);
  696. }
  697. }
  698. length = ftell(f_phdata);
  699. while((length & 3) != 0)
  700. {
  701. // pad to a multiple of 4 bytes
  702. fputc(0,f_phdata);
  703. length++;
  704. }
  705. if(resample_wav != 0)
  706. {
  707. fclose(f);
  708. remove(fname_temp);
  709. }
  710. return(displ | 0x800000); // set bit 23 to indicate a wave file rather than a spectrum
  711. } // end of Compile::LoadWavefile
  712. int Compile::LoadEnvelope(FILE *f, const char *fname)
  713. {//==================================================
  714. int displ;
  715. char buf[128];
  716. displ = ftell(f_phdata);
  717. fseek(f,12,SEEK_SET);
  718. fread(buf,1,128,f);
  719. fwrite(buf,1,128,f_phdata);
  720. return(displ);
  721. }
  722. int Compile::LoadDataFile(const char *path, int control)
  723. {//====================================================
  724. // load spectrum sequence or sample data from a file.
  725. // return index into spect or sample data area. bit 23=1 if a sample
  726. FILE *f;
  727. int id;
  728. int ix;
  729. int hash;
  730. int type_code=' ';
  731. REF_HASH_TAB *p, *p2;
  732. char buf[256];
  733. count_references++;
  734. hash = Hash8(path);
  735. p = ref_hash_tab[hash];
  736. while(p != NULL)
  737. {
  738. if(strcmp(path,p->string)==0)
  739. {
  740. int found = 0;
  741. duplicate_references++;
  742. p->n_uses++;
  743. // add the current phoneme table to a list of users of this data file, if not already listed
  744. for(ix=0; (ix < p->n_used_by) && (ix < N_USED_BY); ix++)
  745. {
  746. if(p->used_by[ix] == n_phoneme_tabs)
  747. {
  748. found = 1;
  749. break;
  750. }
  751. }
  752. if(found == 0)
  753. {
  754. if(ix < N_USED_BY)
  755. {
  756. p->used_by[ix] = n_phoneme_tabs;
  757. }
  758. p->n_used_by++;
  759. }
  760. return(p->value); // already loaded this data
  761. }
  762. p = (REF_HASH_TAB *)p->link;
  763. }
  764. sprintf(buf,"%s%s",path_source,path);
  765. if(strcmp(path,"NULL")==0)
  766. return(0);
  767. if(strcmp(path,"DFT")==0)
  768. return(1);
  769. if((f = fopen(buf,"rb")) == NULL)
  770. {
  771. sprintf(buf,"%s%s.wav",path_source,path);
  772. if((f = fopen(buf,"rb")) == NULL)
  773. {
  774. Error("Can't read file",path);
  775. return(0);
  776. }
  777. }
  778. id = Read4Bytes(f);
  779. // fread(&id,1,4,f);
  780. rewind(f);
  781. if(id == 0x43455053)
  782. {
  783. ix = LoadSpect(path, control);
  784. type_code = 'S';
  785. }
  786. else
  787. if(id == 0x46464952)
  788. {
  789. ix = LoadWavefile(f,path);
  790. type_code = 'W';
  791. }
  792. else
  793. if(id == 0x43544950)
  794. {
  795. ix = LoadEnvelope(f,path);
  796. type_code = 'E';
  797. }
  798. else
  799. {
  800. Error("File not SPEC or RIFF",path);
  801. ix = -1;
  802. }
  803. fclose(f);
  804. if(ix > 0)
  805. {
  806. // fprintf(f_phcontents,"%c %-15s %4d 0x%.5x %s\n",type_code,current_fname,linenum,ix & 0x7fffff,path);
  807. fprintf(f_phcontents,"%c 0x%.5x %s\n",type_code,ix & 0x7fffff,path);
  808. }
  809. // add this item to the hash table
  810. p = ref_hash_tab[hash];
  811. p2 = (REF_HASH_TAB *)malloc(sizeof(REF_HASH_TAB)+strlen(path)+1);
  812. p2->value = ix;
  813. p2->n_uses = 1;
  814. p2->n_used_by = 1;
  815. strcpy(p2->string,path);
  816. p2->used_by[0] = n_phoneme_tabs;
  817. p2->link = (char *)p;
  818. ref_hash_tab[hash] = p2;
  819. return(ix);
  820. } // end of Compile::LoadDataFile
  821. void Compile::AddEnvelope(int *list)
  822. {//================================
  823. NextItem(tSTRING);
  824. *list++ = LoadDataFile(item_string,0);
  825. list[0] = 0;
  826. }
  827. int Compile::AddSpect(int phcode, int *list, int control)
  828. {//=====================================================
  829. int sign;
  830. int sign2;
  831. int all_digits;
  832. char *p;
  833. char *p2;
  834. int spect;
  835. int value;
  836. int count = 0;
  837. int v_in = vowel_in[0];
  838. int v_out = vowel_out[0];
  839. sign = 0;
  840. all_digits=0;
  841. p = p2 = item_string;
  842. for(;;)
  843. {
  844. if((*p == 0) || (*p == '+') || (((*p == '-') || (*p == '%')) && isdigit(p[1])))
  845. {
  846. sign2 = *p;
  847. *p = 0;
  848. if(all_digits)
  849. {
  850. value = atoi(p2);
  851. if(sign == '+')
  852. *list++ = 1 + (value<<8);
  853. else
  854. if(sign == '-')
  855. *list++ = 2 + (value<<8);
  856. else
  857. {
  858. value = (value * 32)/100; // convert from % to 1/32s
  859. *list++ = 3 + (value<<8);
  860. }
  861. count++;
  862. }
  863. else
  864. {
  865. spect = LoadDataFile(p2, control);
  866. if(spect != -1)
  867. {
  868. *list++ = phcode + (spect<<8);
  869. count++;
  870. // vowel formant transitions specified ?
  871. if(v_in != 0)
  872. {
  873. *list++ = 0x4 + (vowel_in[0] << 8);
  874. *list++ = vowel_in[1];
  875. v_in = 0;
  876. count+=2;
  877. }
  878. if(v_out != 0)
  879. {
  880. *list++ = 0x5 + (vowel_out[0] << 8);
  881. *list++ = vowel_out[1];
  882. v_out = 0;
  883. count+=2;
  884. }
  885. }
  886. phcode = 0;
  887. }
  888. if((sign = sign2)==0) break;
  889. p2 = p+1;
  890. all_digits=1;
  891. }
  892. else
  893. {
  894. if(!isdigit(*p))
  895. all_digits=0;
  896. }
  897. p++;
  898. }
  899. *list = 0;
  900. return(count);
  901. } // end of AddSpect
  902. void Compile::AddSpectList(int *list, int control)
  903. {//==============================================
  904. int phcode;
  905. int key;
  906. while(*list != 0) list++; // find end of the list
  907. for(;;)
  908. {
  909. key = NextItem(tKEYWORD);
  910. UngetItem();
  911. if(key != -1)
  912. break;
  913. phcode = NextItem(tPHONEMEMNEM);
  914. if(phcode == -1)
  915. Error("Undeclared phoneme",item_string);
  916. if(NextItem(tSTRING) == -1)
  917. break;
  918. list += AddSpect(phcode, list, control);
  919. }
  920. *list++ = 0;
  921. } // end of Compile::AddSpectList
  922. static int Range(int value, int divide, int min, int max)
  923. {//======================================================
  924. if(value < 0)
  925. value -= divide/2;
  926. else
  927. value += divide/2;
  928. value = value / divide;
  929. if(value > max)
  930. value = max;
  931. if(value < min)
  932. value = min;
  933. return(value - min);
  934. }
  935. void Compile::VowelTransition(int which, unsigned int *trans)
  936. {//==========================================================
  937. // Compile a vowel transition
  938. int key;
  939. int len=0;
  940. int rms=0;
  941. int f1=0;
  942. int f2=0;
  943. int f2_min=0;
  944. int f2_max=0;
  945. int f3_adj=0;
  946. int f3_amp=0;
  947. int flags=0;
  948. int vcolour=0;
  949. int x;
  950. if(which==1)
  951. {
  952. len = 50 / 2; // defaults for transition into vowel
  953. rms = 25 / 2;
  954. }
  955. else
  956. if(which==2)
  957. {
  958. len = 36 / 2; // defaults for transition out of vowel
  959. rms = 16 / 2;
  960. }
  961. for(;;)
  962. {
  963. key = NextItem(tKEYWORD);
  964. if(item_type != 3)
  965. {
  966. UngetItem();
  967. break;
  968. }
  969. switch(key & 0xf)
  970. {
  971. case 1:
  972. len = Range(NextItem(tNUMBER), 2, 0, 63) & 0x3f;
  973. flags |= 1;
  974. break;
  975. case 2:
  976. rms = Range(NextItem(tNUMBER), 2, 0, 63) & 0x3f;
  977. flags |= 1;
  978. break;
  979. case 3:
  980. f1 = NextItem(tNUMBER);
  981. break;
  982. case 4:
  983. f2 = Range(NextItem(tNUMBER), 50, 0, 63) & 0x3f;
  984. f2_min = Range(NextItem(tSIGNEDNUMBER), 50, -15, 15) & 0x1f;
  985. f2_max = Range(NextItem(tSIGNEDNUMBER), 50, -15, 15) & 0x1f;
  986. if(f2_min > f2_max)
  987. {
  988. x = f2_min;
  989. f2_min = f2_max;
  990. f2_max = x;
  991. }
  992. break;
  993. case 5:
  994. f3_adj = Range(NextItem(tSIGNEDNUMBER), 50, -15, 15) & 0x1f;
  995. f3_amp = Range(NextItem(tNUMBER), 8, 0, 15) & 0x1f;
  996. break;
  997. case 6:
  998. flags |= 2; // break
  999. break;
  1000. case 7:
  1001. flags |= 4; // rate
  1002. break;
  1003. case 8:
  1004. flags |= 8; // glstop
  1005. break;
  1006. case 9:
  1007. flags |= 16; // lenadd
  1008. break;
  1009. case 10:
  1010. flags |= 32; // f4
  1011. break;
  1012. case 11:
  1013. flags |= 64; // paus
  1014. break;
  1015. case 12:
  1016. vcolour = NextItem(tNUMBER);
  1017. break;
  1018. }
  1019. }
  1020. trans[0] = len + (rms << 6) + (flags << 12) + 0x80000000;
  1021. trans[1] = f2 + (f2_min << 6) + (f2_max << 11) + (f3_adj << 16) + (f3_amp << 21) + (f1 << 26) + (vcolour << 29);
  1022. } // end of VowelTransition
  1023. int Compile::CPhoneme()
  1024. {//====================
  1025. int phcode;
  1026. int phindex;
  1027. int value;
  1028. int item;
  1029. int finish=0;
  1030. int *intp;
  1031. int before_tab[N_PHONEME_TAB];
  1032. int after_tab[N_PHONEME_TAB];
  1033. int default_spect[N_PHONEME_TAB];
  1034. NextItem(tSTRING);
  1035. phindex = LookupPhoneme(item_string,1); // declare phoneme if not already there
  1036. if(phindex == -1) return(0);
  1037. ph = &phoneme_tab2[phindex];
  1038. memset(ph,0,sizeof(PHONEME_TAB));
  1039. ph->mnemonic = StringToWord(item_string);
  1040. ph->type = 0xff; // undefined
  1041. ph->code = phindex;
  1042. before_tab[0] = 0;
  1043. after_tab[0] = 0;
  1044. default_spect[0] = 0;
  1045. vowel_in[0] = 0;
  1046. vowel_in[1] = 0;
  1047. vowel_out[0] = 0;
  1048. vowel_out[1] = 0;
  1049. while(!finish)
  1050. {
  1051. item = NextItem(tKEYWORD);
  1052. if(item_type==2)
  1053. ph->phflags |= item & 0xffffff;
  1054. else
  1055. if(item_type==1)
  1056. {
  1057. if(ph->type != 0xff)
  1058. {
  1059. Error("Phoneme type already set",item_string);
  1060. }
  1061. ph->type = item & 0xff;
  1062. }
  1063. else
  1064. switch(item)
  1065. {
  1066. case tLENGTHMOD:
  1067. ph->length_mod = NextItem(tNUMBER);
  1068. break;
  1069. case tLENGTH:
  1070. ph->std_length = NextItem(tNUMBER);
  1071. break;
  1072. case tWAVE:
  1073. ph->phflags |= phWAVE; // drop through to tSPECT
  1074. case tSPECT:
  1075. if(NextItem(tSTRING) == -1)
  1076. Error("Syntax error",item_string);
  1077. else
  1078. {
  1079. if(ph->type == phVOWEL)
  1080. AddSpect(phonPAUSE,default_spect,1);
  1081. else
  1082. AddSpect(phonPAUSE,default_spect,0);
  1083. }
  1084. break;
  1085. case tSTARTTYPE:
  1086. phcode = NextItem(tPHONEMEMNEM);
  1087. if(phcode == -1)
  1088. phcode = LookupPhoneme(item_string,1);
  1089. ph->start_type = phcode;
  1090. break;
  1091. case tENDTYPE:
  1092. phcode = NextItem(tPHONEMEMNEM);
  1093. if(phcode == -1)
  1094. phcode = LookupPhoneme(item_string,1);
  1095. ph->end_type = phcode;
  1096. break;
  1097. case tTONESPEC:
  1098. ph->start_type = NextItem(tNUMBER); // tone's min pitch (range 0-50)
  1099. ph->end_type = NextItem(tNUMBER); // tone's max pitch (range 0-50)
  1100. if(ph->start_type > ph->end_type)
  1101. {
  1102. // ensure pitch1 < pitch2
  1103. value = ph->start_type;
  1104. ph->start_type = ph->end_type;
  1105. ph->end_type = value;
  1106. }
  1107. AddEnvelope(default_spect); // envelope for pitch change, rise or fall or fall/rise, etc
  1108. AddEnvelope(after_tab); // envelope for amplitude change
  1109. break;
  1110. case tREDUCETO:
  1111. phcode = NextItem(tPHONEMEMNEM);
  1112. if(phcode == -1)
  1113. phcode = LookupPhoneme(item_string,1);
  1114. ph->reduce_to = phcode;
  1115. value = NextItem(tNUMBER);
  1116. ph->phflags = (ph->phflags & 0xfffffff) + (value << 28);
  1117. break;
  1118. case tBEFORENOTVOWEL:
  1119. ph->phflags |= phBEFORENOTVOWEL; // and drop through to tBEFOREVOWEL
  1120. case tBEFOREVOWELPAUSE:
  1121. ph->phflags |= phBEFOREVOWELPAUSE;
  1122. case tBEFOREVOWEL:
  1123. if((phcode = NextItem(tPHONEMEMNEM)) == -1)
  1124. phcode = LookupPhoneme(item_string,1);
  1125. ph->alternative_ph = phcode;
  1126. break;
  1127. case tSWITCHVOICING:
  1128. ph->phflags |= phSWITCHVOICING;
  1129. if((phcode = NextItem(tPHONEMEMNEM)) == -1)
  1130. phcode = LookupPhoneme(item_string,1);
  1131. ph->alternative_ph = phcode;
  1132. break;
  1133. case tAPPENDPH:
  1134. ph->phflags |= phAPPENDPH; // drop through to tLINKOUT
  1135. case tLINKOUT:
  1136. phcode = NextItem(tPHONEMEMNEM);
  1137. if(phcode == -1)
  1138. phcode = LookupPhoneme(item_string,1);
  1139. ph->link_out = phcode;
  1140. break;
  1141. case tBEFORE:
  1142. AddSpectList(before_tab,0);
  1143. break;
  1144. case tAFTER:
  1145. AddSpectList(after_tab,0);
  1146. break;
  1147. case tEND:
  1148. finish = 1;
  1149. break;
  1150. case tVOWELIN:
  1151. VowelTransition(1,vowel_in);
  1152. break;
  1153. case tVOWELOUT:
  1154. VowelTransition(2,vowel_out);
  1155. break;
  1156. default:
  1157. Error("Syntax error",item_string);
  1158. break;
  1159. }
  1160. }
  1161. // write out indices
  1162. if(before_tab[0] != 0)
  1163. {
  1164. ph->before = ftell(f_phindex) / 4;
  1165. intp = before_tab;
  1166. for(;;)
  1167. {
  1168. fwrite(intp,4,1,f_phindex);
  1169. if(*intp++ == 0) break;
  1170. }
  1171. }
  1172. if(after_tab[0] != 0)
  1173. {
  1174. ph->after = ftell(f_phindex) / 4;
  1175. intp = after_tab;
  1176. for(;;)
  1177. {
  1178. fwrite(intp,4,1,f_phindex);
  1179. if(*intp++ == 0) break;
  1180. }
  1181. }
  1182. if(default_spect[0] != 0)
  1183. {
  1184. ph->spect = ftell(f_phindex) / 4;
  1185. intp = default_spect;
  1186. for(;;)
  1187. {
  1188. fwrite(intp,4,1,f_phindex);
  1189. if(*intp++ == 0) break;
  1190. }
  1191. }
  1192. if(ph->phflags & phVOICED)
  1193. {
  1194. if(ph->type == phSTOP)
  1195. ph->type = phVSTOP;
  1196. else
  1197. if(ph->type == phFRICATIVE)
  1198. ph->type = phVFRICATIVE;
  1199. }
  1200. ph->std_length |= 0x8000; // 'locally declared' indicator
  1201. return(phindex);
  1202. } // end of Compile::CPhoneme
  1203. void Compile::WritePhonemeTable()
  1204. {//==============================
  1205. int ix;
  1206. int j;
  1207. int n;
  1208. int value;
  1209. int count;
  1210. PHONEME_TAB *p;
  1211. value = n_phoneme_tabs;
  1212. fputc(value,f_phtab);
  1213. fputc(0,f_phtab);
  1214. fputc(0,f_phtab);
  1215. fputc(0,f_phtab);
  1216. for(ix=0; ix<n_phoneme_tabs; ix++)
  1217. {
  1218. p = phoneme_tab_list2[ix].phoneme_tab_ptr;
  1219. n = n_phcodes_list[ix];
  1220. p[n].mnemonic = 0; // terminate the phoneme table
  1221. // count number of locally declared phonemes
  1222. count=0;
  1223. for(j=0; j<n; j++)
  1224. {
  1225. if(ix==0)
  1226. p[j].std_length |= 0x8000; // write all phonemes in the base phoneme table
  1227. if(p[j].std_length & 0x8000)
  1228. count++;
  1229. }
  1230. phoneme_tab_list2[ix].n_phonemes = count+1;
  1231. fputc(count+1,f_phtab);
  1232. fputc(phoneme_tab_list2[ix].includes,f_phtab);
  1233. fputc(0,f_phtab);
  1234. fputc(0,f_phtab);
  1235. fwrite(phoneme_tab_list2[ix].name,1,N_PHONEME_TAB_NAME,f_phtab);
  1236. for(j=0; j<n; j++)
  1237. {
  1238. if(p[j].std_length & 0x8000)
  1239. {
  1240. // this bit is set temporarily to incidate a local phoneme, declared in
  1241. // in the current phoneme file
  1242. p[j].std_length &= 0x7fff;
  1243. fwrite(&p[j],sizeof(PHONEME_TAB),1,f_phtab);
  1244. }
  1245. }
  1246. fwrite(&p[n],sizeof(PHONEME_TAB),1,f_phtab); // include the extra list-terminator phoneme entry
  1247. free(p);
  1248. }
  1249. }
  1250. void Compile::EndPhonemeTable()
  1251. {//============================
  1252. int ix;
  1253. if(n_phoneme_tabs == 0)
  1254. return;
  1255. // check that all referenced phonemes have been declared
  1256. for(ix=0; ix<n_phcodes; ix++)
  1257. {
  1258. if(phoneme_tab2[ix].type == phINVALID)
  1259. {
  1260. fprintf(f_errors,"%3d: Phoneme [%s] not declared, referenced at line %d\n",linenum,
  1261. WordToString(phoneme_tab2[ix].mnemonic),int(phoneme_tab2[ix].spect));
  1262. error_count++;
  1263. }
  1264. }
  1265. n_phcodes_list[n_phoneme_tabs-1] = n_phcodes;
  1266. }
  1267. void Compile::StartPhonemeTable(const char *name)
  1268. {//==============================================
  1269. int ix;
  1270. int j;
  1271. PHONEME_TAB *p;
  1272. fprintf(f_errors,"______________________________\nPhoneme Table: '%s'\n",name);
  1273. if(n_phoneme_tabs >= N_PHONEME_TABS-1)
  1274. {
  1275. Error("Too many phonemetables");
  1276. return;
  1277. }
  1278. p = (PHONEME_TAB *)calloc(sizeof(PHONEME_TAB),N_PHONEME_TAB);
  1279. if(p == NULL)
  1280. {
  1281. Error("Out of memory");
  1282. return;
  1283. }
  1284. if(gui_flag)
  1285. progress->Update(n_phoneme_tabs);
  1286. phoneme_tab_list2[n_phoneme_tabs].phoneme_tab_ptr = phoneme_tab2 = p;
  1287. memset(phoneme_tab_list2[n_phoneme_tabs].name, 0, N_PHONEME_TAB_NAME);
  1288. strncpy0(phoneme_tab_list2[n_phoneme_tabs].name, name, N_PHONEME_TAB_NAME);
  1289. n_phcodes = 1;
  1290. phoneme_tab_list2[n_phoneme_tabs].includes = 0;
  1291. if(n_phoneme_tabs > 0)
  1292. {
  1293. NextItem(tSTRING); // name of base phoneme table
  1294. for(ix=0; ix<n_phoneme_tabs; ix++)
  1295. {
  1296. if(strcmp(item_string,phoneme_tab_list2[ix].name)==0)
  1297. {
  1298. phoneme_tab_list2[n_phoneme_tabs].includes = ix+1;
  1299. // initialise the new phoneme table with the contents of this one
  1300. memcpy(phoneme_tab2,phoneme_tab_list2[ix].phoneme_tab_ptr,sizeof(PHONEME_TAB)*N_PHONEME_TAB);
  1301. n_phcodes = n_phcodes_list[ix];
  1302. // clear "local phoneme" bit"
  1303. for(j=0; j<n_phcodes; j++)
  1304. phoneme_tab2[j].std_length = phoneme_tab2[j].std_length & 0x7fff;
  1305. break;
  1306. }
  1307. }
  1308. if(ix == n_phoneme_tabs)
  1309. {
  1310. Error("Can't find base phonemetable '%s'",item_string);
  1311. }
  1312. }
  1313. n_phoneme_tabs++;
  1314. } // end of StartPhonemeTable
  1315. void Compile::CPhonemeFiles(char *path_source)
  1316. {//===========================================
  1317. int phcode;
  1318. int item;
  1319. FILE *f;
  1320. char buf[120];
  1321. linenum = 1;
  1322. n_phcodes = 1; // don't use phoneme code=0, it's used as string terminator
  1323. count_references = 0;
  1324. duplicate_references = 0;
  1325. count_frames = 0;
  1326. for(;;)
  1327. {
  1328. if(feof(f_in))
  1329. {
  1330. // end of file, go back to previous from, from which this was included
  1331. if(stack_ix == 0)
  1332. break; // end of top level, finished
  1333. fclose(f_in);
  1334. f_in = stack[--stack_ix].file;
  1335. strcpy(current_fname,stack[stack_ix].fname);
  1336. linenum = stack[stack_ix].linenum;
  1337. fprintf(f_errors,"\n\n");
  1338. }
  1339. item = NextItem(tKEYWORD);
  1340. if(item == tINCLUDE)
  1341. {
  1342. NextItem(tSTRING);
  1343. sprintf(buf,"%s%s",path_source,item_string);
  1344. if((stack_ix < N_STACK) && (f = fopen_log(f_errors,buf,"rb")) != NULL)
  1345. {
  1346. fprintf(f_errors,"include %s\n",item_string);
  1347. stack[stack_ix].linenum = linenum;
  1348. strcpy(stack[stack_ix].fname,current_fname);
  1349. stack[stack_ix++].file = f_in;
  1350. f_in = f;
  1351. strncpy0(current_fname,item_string,sizeof(current_fname));
  1352. linenum = 1;
  1353. }
  1354. }
  1355. else
  1356. if(item == tPHONEMETABLE)
  1357. {
  1358. EndPhonemeTable();
  1359. NextItem(tSTRING); // name of the new phoneme table
  1360. StartPhonemeTable(item_string);
  1361. }
  1362. else
  1363. if(item == tPHONEMENUMBER)
  1364. {
  1365. // reserve a specified number for this phoneme
  1366. phcode = NextItem(tNUMBER);
  1367. NextItem(tSTRING);
  1368. ReservePhCode(item_string,phcode);
  1369. }
  1370. else
  1371. if(item == tPHONEMESTART)
  1372. {
  1373. if(n_phoneme_tabs == 0)
  1374. {
  1375. Error("phonemetable is missing");
  1376. return;
  1377. }
  1378. phcode = CPhoneme();
  1379. }
  1380. else
  1381. if(!feof(f_in))
  1382. Error("Keyword 'phoneme' expected");
  1383. }
  1384. phoneme_tab2[n_phcodes+1].mnemonic = 0; // terminator
  1385. } // end of CPhonemeFiles
  1386. //#define MAKE_ENVELOPES
  1387. #ifdef MAKE_ENVELOPES
  1388. #define ENV_LEN 128
  1389. void MakeEnvelope(unsigned char *env, float *points_x, float *points_y)
  1390. {//====================================================================
  1391. int ix = -1;
  1392. int x,y;
  1393. for(x=0; x<ENV_LEN; x++)
  1394. {
  1395. if(x > points_x[ix+4])
  1396. ix++;
  1397. y = (int)polint(&points_x[ix],&points_y[ix],4,x);
  1398. if(y < 0) y = 0;
  1399. if(y > 255) y = 255;
  1400. env[x] = y;
  1401. }
  1402. }
  1403. static float env1_x[]={0,0x30,0x40,ENV_LEN};
  1404. static float env1_y[]={0,200,255,255};
  1405. static float env2_x[]={0,0x28,0x48,0x60,ENV_LEN};
  1406. static float env2_y[]={255,0xe0,0xc0,0x60,0};
  1407. static float env3_x[]={0,0x18,0x30,ENV_LEN};
  1408. static float env3_y[]={0,0x28,0x50,255};
  1409. static float env4_x[]={0,0x20,0x40,0x60,ENV_LEN};
  1410. static float env4_y[]={255,0x70,0,0x70,255};
  1411. static float env5_x[]={0,0x20,0x40,0x58,0x70,ENV_LEN};
  1412. static float env5_y[]={0,0x28,0x50,0xa0,0xf0,255};
  1413. static float env6_x[]={0,0x20,0x38,0x60,ENV_LEN};
  1414. static float env6_y[]={255,0xe8,0xd0,0x68,0};
  1415. static float env7_x[]={0,0x20,0x40,0x60,ENV_LEN}; // 214
  1416. static float env7_y[]={85,42,0,128,255};
  1417. static float env8_x[]={0,0x20,0x40,0x60,ENV_LEN}; // 211
  1418. static float env8_y[]={255,130,20,10,0};
  1419. static float env9_x[]={0,0x20,0x40,0x60,ENV_LEN}; // 51 fall
  1420. static float env9_y[]={255,210,140,70,0};
  1421. static float enva3_x[]={0,44,64,84,ENV_LEN}; // amp env for broken tone
  1422. static float enva3_y[]={255,255,160,255,255};
  1423. static float enva6_x[]={0,44,64,80,ENV_LEN}; // amp env for drop tone
  1424. static float enva6_y[]={255,255,255,250,50};
  1425. unsigned char env_test[ENV_LEN];
  1426. void MakeEnvFile(char *fname, float *x, float *y, int source)
  1427. {//==========================================================
  1428. static char hdr[12] = {'P','I','T','C','H','E','N','V',80,0,120,0};
  1429. FILE *f;
  1430. int ix;
  1431. MakeEnvelope(env_test,x,y);
  1432. f = fopen(fname,"wb");
  1433. if(source)
  1434. {
  1435. for(ix=0; ix<128; ix++)
  1436. {
  1437. fprintf(f,"0x%.2x,",env_test[ix]);
  1438. if((ix & 7) == 7)
  1439. fprintf(f,"\n");
  1440. }
  1441. }
  1442. else
  1443. {
  1444. fwrite(hdr,12,1,f);
  1445. fwrite(env_test,128,1,f);
  1446. }
  1447. fclose(f);
  1448. }
  1449. void make_envs()
  1450. {//=============
  1451. MakeEnvFile("p_level",env1_x,env1_y,0);
  1452. MakeEnvFile("p_rise",env5_x,env5_y,0);
  1453. MakeEnvFile("p_fall",env9_x,env9_y,0);
  1454. MakeEnvFile("p_214",env7_x,env7_y,0);
  1455. MakeEnvFile("p_211",env8_x,env8_y,0);
  1456. MakeEnvFile("vi_2",env2_x,env2_y,0);
  1457. MakeEnvFile("vi_5",env3_x,env3_y,0);
  1458. MakeEnvFile("p_fallrise",env4_x,env4_y,0);
  1459. MakeEnvFile("vi_6",env6_x,env6_y,0);
  1460. MakeEnvFile("vi_5amp",enva3_x,enva3_y,0);
  1461. MakeEnvFile("vi_6amp",enva6_x,enva6_y,0);
  1462. }
  1463. #endif
  1464. static int ref_sorter(char **a, char **b)
  1465. {//======================================
  1466. REF_HASH_TAB *p1 = (REF_HASH_TAB *)(*a);
  1467. REF_HASH_TAB *p2 = (REF_HASH_TAB *)(*b);
  1468. return(strcoll(p1->string,p2->string));
  1469. } /* end of strcmp2 */
  1470. void Compile::Report(void)
  1471. {//=======================
  1472. int ix;
  1473. int hash;
  1474. int n;
  1475. REF_HASH_TAB *p;
  1476. REF_HASH_TAB **list;
  1477. FILE *f_report;
  1478. char fname[80];
  1479. // make a list of all the references and sort it
  1480. list = (REF_HASH_TAB **)malloc(count_references * sizeof(REF_HASH_TAB *));
  1481. if(list == NULL)
  1482. return;
  1483. sprintf(fname,"%scompile_report",path_source);
  1484. f_report = fopen(fname,"w");
  1485. if(f_report == NULL)
  1486. {
  1487. free(list);
  1488. return;
  1489. }
  1490. fprintf(f_report,"%d phoneme tables\n",n_phoneme_tabs);
  1491. fprintf(f_report," new total\n");
  1492. for(ix=0; ix<n_phoneme_tabs; ix++)
  1493. {
  1494. fprintf(f_report,"%8s %3d %4d\n",phoneme_tab_list2[ix].name, phoneme_tab_list2[ix].n_phonemes, n_phcodes_list[ix]+1);
  1495. }
  1496. fputc('\n',f_report);
  1497. ix = 0;
  1498. for(hash=0; (hash < 256) && (ix < count_references); hash++)
  1499. {
  1500. p = ref_hash_tab[hash];
  1501. while(p != NULL)
  1502. {
  1503. list[ix++] = p;
  1504. p = (REF_HASH_TAB *)(p->link);
  1505. }
  1506. }
  1507. n = ix;
  1508. qsort((void *)list,n,sizeof(REF_HASH_TAB *),(int (*)(const void *,const void *))ref_sorter);
  1509. for(ix=0; ix<n; ix++)
  1510. {
  1511. int j, ph_tab_num;
  1512. fprintf(f_report,"%3d %s",list[ix]->n_uses, list[ix]->string);
  1513. for(j = strlen(list[ix]->string); j < 14; j++)
  1514. {
  1515. fputc(' ',f_report); // pad filename with spaces
  1516. }
  1517. for(j=0; (j < list[ix]->n_used_by) && (j<N_USED_BY); j++)
  1518. {
  1519. ph_tab_num = list[ix]->used_by[j];
  1520. fprintf(f_report," %s",phoneme_tab_list2[ph_tab_num-1].name);
  1521. }
  1522. if(j < list[ix]->n_used_by)
  1523. {
  1524. fprintf(f_report," ++");
  1525. }
  1526. fputc('\n',f_report);
  1527. }
  1528. free(list);
  1529. fclose(f_report);
  1530. }
  1531. wxString CompileAllDictionaries()
  1532. {//==============================
  1533. wxString filename;
  1534. wxFileName fname;
  1535. wxString dictstr;
  1536. wxString report = _T("");
  1537. int err;
  1538. int errors = 0;
  1539. int dict_count = 0;
  1540. FILE *log;
  1541. char dictname[80];
  1542. char fname_log[80];
  1543. char save_voice_name[80];
  1544. if(!wxDirExists(path_dictsource))
  1545. {
  1546. if(gui_flag)
  1547. {
  1548. wxString dirname = wxDirSelector(_T("Directory of dictionary files"),path_phsource);
  1549. if(!dirname.IsEmpty())
  1550. {
  1551. path_dictsource = dirname;
  1552. strncpy0(path_dsource,path_dictsource.mb_str(wxConvLocal),sizeof(path_dsource)-1);
  1553. strcat(path_dsource,"/");
  1554. }
  1555. }
  1556. else
  1557. {
  1558. fprintf(stderr,"Can't find dictionary files: %s\n",path_dsource);
  1559. }
  1560. }
  1561. wxDir dir(path_dictsource);
  1562. if(!dir.IsOpened())
  1563. {
  1564. return(_T(" No dictionaries"));
  1565. }
  1566. strcpy(save_voice_name,voice_name);
  1567. sprintf(fname_log,"%s%s",path_dsource,"dict_log");
  1568. log = fopen(fname_log,"w");
  1569. bool cont = dir.GetFirst(&filename, _T("*_rules"), wxDIR_FILES);
  1570. while ( cont )
  1571. {
  1572. fname = wxFileName(filename);
  1573. dictstr = fname.GetName().BeforeLast('_');
  1574. strcpy(dictname,dictstr.mb_str(wxConvLocal));
  1575. dict_count++;
  1576. LoadVoice(dictname,0);
  1577. if((err = CompileDictionary(path_dsource, dictname,log,NULL)) > 0)
  1578. {
  1579. report = report + dictstr + wxString::Format(_T(" %d, "),err);
  1580. errors += err;
  1581. }
  1582. cont = dir.GetNext(&filename);
  1583. }
  1584. if(log != NULL)
  1585. fclose(log);
  1586. LoadVoice(save_voice_name,1);
  1587. if(errors == 0)
  1588. return(wxString::Format(_T(" Compiled %d dictionaries"),dict_count));
  1589. else
  1590. {
  1591. return(_T(" Dictionary errors: ") + report);
  1592. }
  1593. } // end of CompileAllDictionaries
  1594. void Compile::CPhonemeTab(const char *source)
  1595. {//========================================
  1596. char fname[130];
  1597. wxString report;
  1598. wxString report_dict;
  1599. #ifdef MAKE_ENVELOPES
  1600. make_envs();
  1601. #endif
  1602. error_count = 0;
  1603. memset(markers_used,0,sizeof(markers_used));
  1604. f_errors = stderr;
  1605. strncpy0(current_fname,source,sizeof(current_fname));
  1606. sprintf(fname,"%s%s",path_source,source);
  1607. f_in = fopen_log(f_errors,fname,"rb");
  1608. if(f_in == NULL)
  1609. {
  1610. if(gui_flag)
  1611. {
  1612. wxString dir = wxDirSelector(_T("Directory for 'phonemes' file"),path_phsource);
  1613. if(!dir.IsEmpty())
  1614. {
  1615. path_phsource = dir;
  1616. strncpy0(path_source,path_phsource.mb_str(wxConvLocal),sizeof(path_source)-1);
  1617. strcat(path_source,"/");
  1618. }
  1619. }
  1620. sprintf(fname,"%s%s",path_source,source);
  1621. f_in = fopen_log(f_errors,fname,"rb");
  1622. if(f_in == NULL)
  1623. {
  1624. wxLogError(_T("Can't read master phonemes file:\n") + wxString(fname,wxConvLocal));
  1625. return;
  1626. }
  1627. }
  1628. progress_max = 0;
  1629. while(fgets(fname,sizeof(fname),f_in) != NULL)
  1630. {
  1631. // count the number of phoneme tables declared in the master phonemes file
  1632. if(memcmp(fname,"phonemetable",12)==0)
  1633. progress_max++;
  1634. }
  1635. rewind(f_in);
  1636. sprintf(fname,"%s%s",path_source,"error_log");
  1637. if((f_errors = fopen_log(f_errors,fname,"w")) == NULL)
  1638. f_errors = stderr;
  1639. sprintf(fname,"%s/%s",path_home,"phondata_manifest");
  1640. if((f_phcontents = fopen_log(f_phcontents,fname,"w")) == NULL)
  1641. f_phcontents = stderr;
  1642. fprintf (f_phcontents,
  1643. "# This file lists the type of data that has been compiled into the\n"
  1644. "# phondata file\n"
  1645. "#\n"
  1646. "# The first character of a line indicates the type of data:\n"
  1647. "# S - A SPECT_SEQ structure\n"
  1648. "# W - A wavefile segment\n"
  1649. "# E - An envelope\n"
  1650. "#\n"
  1651. "# Address is the displacement within phondata of this item\n"
  1652. "#\n"
  1653. "# Address Data file\n"
  1654. "# ------- ---------\n");
  1655. sprintf(fname,"%s/%s",path_home,"phondata");
  1656. f_phdata = fopen_log(f_errors,fname,"wb");
  1657. sprintf(fname,"%s/%s",path_home,"phonindex");
  1658. f_phindex = fopen_log(f_errors,fname,"wb");
  1659. sprintf(fname,"%s/%s",path_home,"phontab");
  1660. f_phtab = fopen_log(f_errors,fname,"wb");
  1661. if(f_phdata==NULL || f_phindex==NULL || f_phtab==NULL)
  1662. {
  1663. return;
  1664. }
  1665. if(gui_flag)
  1666. {
  1667. progress = new wxProgressDialog(_T("Phonemes"),_T(""),progress_max);
  1668. }
  1669. else
  1670. {
  1671. fprintf(stderr,"Compiling phoneme data: %s\n",path_source);
  1672. }
  1673. // write a word so that further data doesn't start at displ=0
  1674. fwrite(&version_phdata,4,1,f_phdata);
  1675. fwrite(&version_phdata,4,1,f_phindex);
  1676. memset(ref_hash_tab,0,sizeof(ref_hash_tab));
  1677. n_phoneme_tabs = 0;
  1678. stack_ix = 0;
  1679. StartPhonemeTable("base");
  1680. CPhonemeFiles(path_source);
  1681. EndPhonemeTable();
  1682. WritePhonemeTable();
  1683. fprintf(f_errors,"Refs %d, Reused %d\n",count_references,duplicate_references);
  1684. fclose(f_in);
  1685. fclose(f_phdata);
  1686. fclose(f_errors);
  1687. fclose(f_phindex);
  1688. fclose(f_phtab);
  1689. fclose(f_phcontents);
  1690. LoadPhData();
  1691. LoadVoice(voice_name,0);
  1692. Report();
  1693. report_dict = CompileAllDictionaries();
  1694. if(gui_flag)
  1695. {
  1696. delete progress;
  1697. }
  1698. report.Printf(_T("Compiled phonemes: %d errors."),error_count);
  1699. if(error_count > 0)
  1700. {
  1701. report += _T(" See file: 'error_log'.");
  1702. wxLogError(report);
  1703. }
  1704. wxLogStatus(report + report_dict);
  1705. if(gui_flag == 0)
  1706. {
  1707. strcpy(fname,(report+report_dict).mb_str(wxConvLocal));
  1708. fprintf(stderr,"%s\n",fname);
  1709. }
  1710. } // end of Compile::CPhonemeTab
  1711. void CompileMbrola()
  1712. {//=================
  1713. char *p;
  1714. FILE *f_in;
  1715. FILE *f_out;
  1716. int percent;
  1717. int n;
  1718. int count = 0;
  1719. int control;
  1720. char phoneme[40];
  1721. char phoneme2[40];
  1722. char name1[40];
  1723. char name2[40];
  1724. char mbrola_voice[40];
  1725. char buf[150];
  1726. int mbrola_ctrl = 20; // volume in 1/16 ths
  1727. MBROLA_TAB data[N_PHONEME_TAB];
  1728. wxString filepath = wxFileSelector(_T("Read Mbrola phonemes file"),path_phsource+_T("/mbrola"),_T(""),_T(""),_T("*"),wxOPEN);
  1729. strcpy(buf,filepath.mb_str(wxConvLocal));
  1730. if((f_in = fopen(buf,"r")) == NULL)
  1731. {
  1732. wxLogError(_T("Can't read: ")+filepath);
  1733. return;
  1734. }
  1735. while(fgets(buf,sizeof(phoneme),f_in) != NULL)
  1736. {
  1737. buf[sizeof(phoneme)-1] = 0;
  1738. if((p = strstr(buf,"//")) != NULL)
  1739. *p = 0; // truncate line at comment
  1740. if(memcmp(buf,"volume",6)==0)
  1741. {
  1742. mbrola_ctrl = atoi(&buf[6]);
  1743. continue;
  1744. }
  1745. n = sscanf(buf,"%d %s %s %d %s %s",&control,phoneme,phoneme2,&percent,name1,name2);
  1746. if(n >= 5)
  1747. {
  1748. data[count].name = StringToWord(phoneme);
  1749. if(strcmp(phoneme2,"NULL")==0)
  1750. data[count].next_phoneme = 0;
  1751. else
  1752. if(strcmp(phoneme2,"VWL")==0)
  1753. data[count].next_phoneme = 2;
  1754. else
  1755. data[count].next_phoneme = StringToWord(phoneme2);
  1756. data[count].mbr_name = 0;
  1757. data[count].mbr_name2 = 0;
  1758. data[count].percent = percent;
  1759. data[count].control = control;
  1760. if(strcmp(name1,"NULL")!=0)
  1761. data[count].mbr_name = StringToWord(name1);
  1762. if(n == 6)
  1763. data[count].mbr_name2 = StringToWord(name2);
  1764. count++;
  1765. }
  1766. }
  1767. fclose(f_in);
  1768. wxFileName filename = wxFileName(filepath);
  1769. strcpy(mbrola_voice,filename.GetName().mb_str(wxConvLocal));
  1770. sprintf(buf,"%s/mbrola_ph/%s_phtrans",path_home,mbrola_voice);
  1771. if((f_out = fopen(buf,"w")) == NULL)
  1772. {
  1773. wxLogError(_T("Can't write to: ")+wxString(buf,wxConvLocal));
  1774. return;
  1775. }
  1776. data[count].name = 0; // list terminator
  1777. fwrite(&mbrola_ctrl,4,1,f_out);
  1778. fwrite(data,sizeof(MBROLA_TAB),count+1,f_out);
  1779. fclose(f_out);
  1780. wxLogStatus(_T("Mbrola translation file: %d phonemes"),count);
  1781. } // end of CompileMbrola
  1782. void CompileInit(void)
  1783. {
  1784. compile = new Compile;
  1785. compile->CPhonemeTab("phonemes");
  1786. delete compile;
  1787. }