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.

espeakedit.cpp 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. /***************************************************************************
  2. * Copyright (C) 2005 to 2007 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, see: *
  17. * <http://www.gnu.org/licenses/>. *
  18. ***************************************************************************/
  19. #include "wx/wx.h"
  20. #include "wx/wfstream.h"
  21. #include "wx/notebook.h"
  22. #include "wx/image.h"
  23. #include "wx/filename.h"
  24. #include "wx/numdlg.h"
  25. #include "wx/mdi.h"
  26. #include "wx/laywin.h"
  27. #include "wx/sashwin.h"
  28. #include <locale.h>
  29. #include "speak_lib.h"
  30. #include "main.h"
  31. #include "speech.h"
  32. #include "options.h"
  33. #include "phoneme.h"
  34. #include "synthesize.h"
  35. #include "voice.h"
  36. #include "spect.h"
  37. #include "translate.h"
  38. #include "prosodydisplay.h"
  39. static char *about_string = "espeakedit: %s\nAuthor: Jonathan Duddington (c) 2007\n\n"
  40. "Licensed under GNU General Public License version 3\n"
  41. "http://espeak.sourceforge.net/";
  42. const char *path_data = "/home/jsd1/speechdata/phsource";
  43. extern void TestTest(int control);
  44. extern void CompareLexicon(int);
  45. extern void ConvertToUtf8();
  46. extern void init_z();
  47. extern void CompileInit(void);
  48. extern void CompileMbrola();
  49. extern void InitSpectrumDisplay();
  50. extern void InitProsodyDisplay();
  51. extern void InitWaveDisplay();
  52. extern void VowelChart(int control, char *fname);
  53. extern void MakeVowelLists(void);
  54. extern void MakeWordFreqList();
  55. extern const char *dict_name;
  56. extern char voice_name[];
  57. MyFrame *myframe = NULL;
  58. SpectDisplay *currentcanvas = NULL;
  59. ChildFrProsody *prosodyframe = NULL;
  60. ProsodyDisplay *prosodycanvas = NULL;
  61. wxNotebook *notebook = NULL;
  62. wxProgressDialog *progress;
  63. int progress_max;
  64. int gui_flag = 0;
  65. wxList my_children;
  66. wxFont FONT_SMALL(8,wxSWISS,wxNORMAL,wxNORMAL);
  67. wxFont FONT_MEDIUM(9,wxSWISS,wxNORMAL,wxNORMAL);
  68. wxFont FONT_NORMAL(10,wxSWISS,wxNORMAL,wxNORMAL);
  69. IMPLEMENT_APP(MyApp)
  70. wxString AppName = _T("espeakedit");
  71. int MyApp::OnExit()
  72. {//================
  73. ConfigSave(1);
  74. return(0);
  75. }
  76. static const char *help_text =
  77. "\n\nespeakedit\n"
  78. "\tRun with GUI\n"
  79. "espeakedit --compile\n"
  80. "\tCompile phoneme data in espeak-data/phsource\n"
  81. "\tand dictionary data in espeak-data/dictsource\n";
  82. // Initialise this in OnInit, not statically
  83. bool MyApp::OnInit(void)
  84. {//=====================
  85. int j;
  86. wxChar *p;
  87. char param[80];
  88. if(argc > 1)
  89. {
  90. extern void VoiceReset(int control);
  91. p = argv[1];
  92. j = 0;
  93. while((param[j] = p[j]) != 0) j++;
  94. if((strcmp(param,"--help")==0) || (strcmp(param,"-h")==0))
  95. {
  96. printf(about_string,espeak_Info(NULL));
  97. printf(help_text);
  98. exit(0);
  99. }
  100. ConfigInit();
  101. VoiceReset(0);
  102. WavegenSetVoice(voice);
  103. WavegenInitSound();
  104. if(strcmp(param,"--compile")==0)
  105. {
  106. CompileInit();
  107. exit(0);
  108. }
  109. }
  110. gui_flag = 1;
  111. // It seems that the wctype functions don't work until the locale has been set
  112. // to something other than the default "C". Then, not only Latin1 but also the
  113. // other characters give the correct results with iswalpha() etc.
  114. if(setlocale(LC_CTYPE,"en_US.UTF-8") == NULL)
  115. {
  116. if(setlocale(LC_CTYPE,"UTF-8") == NULL)
  117. setlocale(LC_CTYPE,"");
  118. }
  119. // Create the main frame window
  120. myframe = new MyFrame(NULL, -1, AppName, wxPoint(0, 0), wxSize(1024, 768),
  121. wxDEFAULT_FRAME_STYLE |
  122. wxNO_FULL_REPAINT_ON_RESIZE |
  123. wxHSCROLL | wxVSCROLL);
  124. // Make a menubar
  125. myframe->SetMenuBar(MakeMenu(0));
  126. myframe->CreateStatusBar();
  127. myframe->Show(TRUE);
  128. SetTopWindow(myframe);
  129. wxInitAllImageHandlers();
  130. // wxImage::AddHandler(wxPNGHandler);
  131. return TRUE;
  132. }
  133. BEGIN_EVENT_TABLE(MyFrame, wxMDIParentFrame)
  134. EVT_CHAR(MyFrame::OnKey)
  135. EVT_MENU(MENU_ABOUT, MyFrame::OnAbout)
  136. EVT_MENU(MENU_SPECTRUM, MyFrame::OnNewWindow)
  137. EVT_MENU(MENU_SPECTRUM2, MyFrame::OnNewWindow)
  138. EVT_MENU(MENU_PROSODY, MyFrame::OnProsody)
  139. EVT_MENU(MENU_PARAMS, MyFrame::OnOptions)
  140. EVT_MENU(MENU_PATH0, MyFrame::OnOptions)
  141. EVT_MENU(MENU_PATH1, MyFrame::OnOptions)
  142. EVT_MENU(MENU_PATH2, MyFrame::OnOptions)
  143. EVT_MENU(MENU_PATH3, MyFrame::OnOptions)
  144. EVT_MENU(MENU_PATH4, MyFrame::OnOptions)
  145. EVT_MENU(MENU_COMPILE_PH, MyFrame::OnTools)
  146. EVT_MENU(MENU_COMPILE_DICT, MyFrame::OnTools)
  147. EVT_MENU(MENU_COMPILE_MBROLA, MyFrame::OnTools)
  148. EVT_MENU(MENU_CLOSE_ALL, MyFrame::OnQuit)
  149. EVT_MENU(MENU_QUIT, MyFrame::OnQuit)
  150. EVT_MENU(MENU_SPEAK_TRANSLATE, MyFrame::OnSpeak)
  151. EVT_MENU(MENU_SPEAK_RULES, MyFrame::OnSpeak)
  152. EVT_MENU(MENU_SPEAK_TEXT, MyFrame::OnSpeak)
  153. EVT_MENU(MENU_SPEAK_FILE, MyFrame::OnSpeak)
  154. EVT_MENU(MENU_SPEAK_STOP, MyFrame::OnSpeak)
  155. EVT_MENU(MENU_SPEAK_PAUSE, MyFrame::OnSpeak)
  156. EVT_MENU(MENU_SPEAK_VOICE, MyFrame::OnSpeak)
  157. EVT_MENU(MENU_LOAD_WAV, MyFrame::OnTools)
  158. EVT_MENU(MENU_VOWELCHART1, MyFrame::OnTools)
  159. EVT_MENU(MENU_VOWELCHART2, MyFrame::OnTools)
  160. EVT_MENU(MENU_VOWELCHART3, MyFrame::OnTools)
  161. EVT_MENU(MENU_LEXICON_RU, MyFrame::OnTools)
  162. EVT_MENU(MENU_LEXICON_DE, MyFrame::OnTools)
  163. EVT_MENU(MENU_TO_UTF8, MyFrame::OnTools)
  164. EVT_MENU(MENU_COUNT_WORDS, MyFrame::OnTools)
  165. EVT_MENU(MENU_TEST, MyFrame::OnTools)
  166. EVT_MENU(MENU_TEST2, MyFrame::OnTools)
  167. EVT_TIMER(1, MyFrame::OnTimer)
  168. EVT_SIZE(MyFrame::OnSize)
  169. EVT_SASH_DRAGGED_RANGE(ID_WINDOW_TOP, ID_WINDOW_BOTTOM, MyFrame::OnSashDrag)
  170. END_EVENT_TABLE()
  171. MyFrame::MyFrame(wxWindow *parent, const wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size,
  172. const long style):
  173. wxMDIParentFrame(parent, id, title, pos, size, style)
  174. {//===================================================================================================================
  175. // Main Frame constructor
  176. int error_flag = 0;
  177. int result;
  178. // Create some dummy layout windows
  179. wxSashLayoutWindow *win;
  180. // Another window to the left of the client window
  181. win = new wxSashLayoutWindow(this, ID_WINDOW_LEFT2,
  182. wxDefaultPosition, wxSize(200, 30),
  183. wxNO_BORDER | wxSW_3D | wxCLIP_CHILDREN);
  184. win->SetDefaultSize(wxSize(208, 1000));
  185. win->SetOrientation(wxLAYOUT_VERTICAL);
  186. win->SetAlignment(wxLAYOUT_LEFT);
  187. // win->SetBackgroundColour(wxColour(0, 255, 255));
  188. win->SetSashVisible(wxSASH_RIGHT, TRUE);
  189. m_leftWindow2 = win;
  190. notebook = new wxNotebook(m_leftWindow2,-1);
  191. // notebook->AddPage(voicedlg,_T("Voice"),FALSE);
  192. formantdlg = new FormantDlg(notebook);
  193. notebook->AddPage(formantdlg,_T(" Spect"),FALSE);
  194. voicedlg = new VoiceDlg(notebook);
  195. transldlg = new TranslDlg(notebook);
  196. notebook->AddPage(transldlg,_T("Text"),TRUE);
  197. ConfigInit();
  198. WavegenInitSound();
  199. if((result = LoadPhData()) != 1)
  200. {
  201. if(result == -1)
  202. wxLogError(_T("Failed to load phoneme data,\nneeds espeak-data/phontab,phondata,phonindex\nPath = ")+wxString(path_home,wxConvLocal));
  203. else
  204. wxLogError(_T("Wrong version of espeak-data: 0x%x (expects 0x%x)"),result,version_phdata);
  205. error_flag = 1;
  206. }
  207. f_trans = stdout;
  208. option_ssml = 1;
  209. // if(LoadVoice(voice_name,0) == NULL)
  210. if(SetVoiceByName(voice_name) != EE_OK)
  211. {
  212. if(error_flag==0)
  213. wxLogError(_T("Failed to load voice data"));
  214. strcpy(dictionary_name,"en");
  215. }
  216. else
  217. {
  218. SetVoiceTitle(voice_name);
  219. }
  220. WavegenSetVoice(voice);
  221. SetParameter(espeakRATE,option_speed,0);
  222. SetSpeed(3);
  223. SynthesizeInit();
  224. InitSpectrumDisplay();
  225. InitProsodyDisplay();
  226. // InitWaveDisplay();
  227. espeak_ListVoices(NULL);
  228. m_timer.SetOwner(this,1);
  229. m_timer.Start(500); /* 0.5 timer */
  230. } // end of MyFrame::MyFrame
  231. void MyFrame::SetVoiceTitle(char *voice_name)
  232. {//==========================================
  233. SetTitle(AppName + _T(" - ") + wxString(voice_name,wxConvLocal) + _T(" voice"));
  234. }
  235. void MyFrame::OnKey(wxKeyEvent& event)
  236. {
  237. int key;
  238. key = event.GetKeyCode();
  239. if((currentcanvas != NULL) && (currentcanvas != FindFocus()))
  240. {
  241. if((key == WXK_F1) || (key == WXK_F2))
  242. {
  243. currentcanvas->OnKey(event);
  244. currentcanvas->SetFocus();
  245. return;
  246. }
  247. }
  248. event.Skip();
  249. }
  250. void MyFrame::OnTimer(wxTimerEvent &event)
  251. //****************************************
  252. {
  253. SynthOnTimer();
  254. }
  255. void MyFrame::OnQuit(wxCommandEvent& event)
  256. {
  257. switch(event.GetId())
  258. {
  259. case MENU_QUIT:
  260. Close(TRUE);
  261. break;
  262. case MENU_CLOSE_ALL:
  263. break;
  264. }
  265. }
  266. void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
  267. {//===================================================
  268. char buf[300];
  269. sprintf(buf,about_string,espeak_Info(NULL));
  270. (void)wxMessageBox(wxString(buf,wxConvLocal), _T("About eSpeak Editor"));
  271. }
  272. void OnOptions2(int event_id)
  273. {//==========================
  274. wxString string;
  275. int value;
  276. switch(event_id)
  277. {
  278. case MENU_PARAMS:
  279. value = wxGetNumberFromUser(_T(""),_T(""),_T("Speed"),option_speed,80,320);
  280. if(value > 0)
  281. {
  282. option_speed = value;
  283. SetParameter(espeakRATE,option_speed,0);
  284. SetSpeed(3);
  285. }
  286. break;
  287. case MENU_PATH0:
  288. string = wxFileSelector(_T("Master phonemes file"),wxFileName(path_phfile).GetPath(),
  289. _T(""),_T(""),_T("*"),wxOPEN);
  290. if(!string.IsEmpty())
  291. {
  292. path_phfile = string;
  293. }
  294. break;
  295. case MENU_PATH1:
  296. string = wxDirSelector(_T("Phoneme source directory"),path_phsource);
  297. if(!string.IsEmpty())
  298. {
  299. path_phsource = string;
  300. }
  301. break;
  302. case MENU_PATH2:
  303. string = wxDirSelector(_T("Dictionary source directory"),path_dictsource);
  304. if(!string.IsEmpty())
  305. {
  306. path_dictsource = string;
  307. }
  308. break;
  309. case MENU_PATH3:
  310. string = wxFileSelector(_T("Sound output file"),wxFileName(path_speech).GetPath(),
  311. _T(""),_T("WAV"),_T("*"),wxSAVE);
  312. if(!string.IsEmpty())
  313. {
  314. path_speech = string;
  315. }
  316. break;
  317. case MENU_PATH4:
  318. string = wxFileSelector(_T("Voice file to modify formant peaks"),wxFileName(path_speech).GetPath(),
  319. _T(""),_T(""),_T("*"),wxOPEN);
  320. if(!string.IsEmpty())
  321. {
  322. path_modifiervoice = string;
  323. }
  324. break;
  325. }
  326. ConfigSetPaths();
  327. }
  328. void MyFrame::OnOptions(wxCommandEvent& event)
  329. {//===========================================
  330. OnOptions2(event.GetId());
  331. }
  332. void MyFrame::OnTools(wxCommandEvent& event)
  333. {//=========================================
  334. int err;
  335. FILE *log;
  336. char fname_log[sizeof(path_dsource)+12];
  337. char err_fname[sizeof(path_home)+15];
  338. switch(event.GetId())
  339. {
  340. case MENU_TEST:
  341. TestTest(0);
  342. break;
  343. case MENU_TEST2:
  344. TestTest(2);
  345. break;
  346. case MENU_TO_UTF8:
  347. ConvertToUtf8();
  348. break;
  349. case MENU_COUNT_WORDS:
  350. MakeWordFreqList();
  351. break;
  352. case MENU_LEXICON_RU:
  353. case MENU_LEXICON_DE:
  354. CompareLexicon(event.GetId()); // Compare a lexicon with _rules translation
  355. break;
  356. case MENU_COMPILE_PH:
  357. CompileInit();
  358. break;
  359. case MENU_COMPILE_MBROLA:
  360. CompileMbrola();
  361. break;
  362. case MENU_COMPILE_DICT:
  363. sprintf(fname_log,"%s%s",path_dsource,"dict_log");
  364. log = fopen(fname_log,"w");
  365. if((err = CompileDictionary(path_dsource,dictionary_name,log,err_fname)) < 0)
  366. {
  367. wxLogError(_T("Can't access file:\n")+wxString(err_fname,wxConvLocal));
  368. wxString dir = wxDirSelector(_T("Directory containing dictionary files"),path_dictsource);
  369. if(!dir.IsEmpty())
  370. {
  371. path_dictsource = dir;
  372. strncpy0(path_dsource,path_dictsource.mb_str(wxConvLocal),sizeof(path_dsource)-1);
  373. strcat(path_dsource,"/");
  374. }
  375. break;
  376. }
  377. wxLogStatus(_T("Compiled '")+wxString(dictionary_name,wxConvLocal)+_T("', %d errors"),err);
  378. if(log != NULL)
  379. fclose(log);
  380. break;
  381. case MENU_VOWELCHART1:
  382. MakeVowelLists();
  383. break;
  384. case MENU_VOWELCHART2:
  385. VowelChart(2,NULL);
  386. break;
  387. case MENU_VOWELCHART3:
  388. VowelChart(3,NULL);
  389. break;
  390. case MENU_LOAD_WAV:
  391. // LoadWavFile();
  392. break;
  393. }
  394. }
  395. extern wxMenu *speak_menu;
  396. void MyFrame::OnSpeak(wxCommandEvent& event)
  397. {//=========================================
  398. switch(event.GetId())
  399. {
  400. case MENU_SPEAK_TRANSLATE:
  401. case MENU_SPEAK_RULES:
  402. case MENU_SPEAK_TEXT:
  403. transldlg->OnCommand(event);
  404. break;
  405. case MENU_SPEAK_FILE:
  406. transldlg->SpeakFile();
  407. break;
  408. case MENU_SPEAK_STOP:
  409. SpeakNextClause(NULL,NULL,2);
  410. break;
  411. case MENU_SPEAK_PAUSE:
  412. SpeakNextClause(NULL,NULL,3);
  413. if(SynthStatus() & 2)
  414. speak_menu->SetLabel(MENU_SPEAK_PAUSE,_T("&Resume"));
  415. else
  416. speak_menu->SetLabel(MENU_SPEAK_PAUSE,_T("&Pause"));
  417. break;
  418. case MENU_SPEAK_VOICE:
  419. transldlg->ReadVoice();
  420. // if(voicedlg != NULL)
  421. // voicedlg->WriteParams();
  422. SetVoiceTitle(voice_name);
  423. break;
  424. }
  425. }
  426. void MyFrame::OnSashDrag(wxSashEvent& event)
  427. {
  428. if (event.GetDragStatus() == wxSASH_STATUS_OUT_OF_RANGE)
  429. return;
  430. switch (event.GetId())
  431. {
  432. case ID_WINDOW_TOP:
  433. {
  434. m_topWindow->SetDefaultSize(wxSize(1000, event.GetDragRect().height));
  435. break;
  436. }
  437. case ID_WINDOW_LEFT1:
  438. {
  439. m_leftWindow1->SetDefaultSize(wxSize(event.GetDragRect().width, 1000));
  440. break;
  441. }
  442. case ID_WINDOW_LEFT2:
  443. {
  444. m_leftWindow2->SetDefaultSize(wxSize(event.GetDragRect().width, 1000));
  445. break;
  446. }
  447. case ID_WINDOW_BOTTOM:
  448. {
  449. m_bottomWindow->SetDefaultSize(wxSize(1000, event.GetDragRect().height));
  450. break;
  451. }
  452. }
  453. wxLayoutAlgorithm layout;
  454. layout.LayoutMDIFrame(this);
  455. // Leaves bits of itself behind sometimes
  456. GetClientWindow()->Refresh();
  457. }
  458. void MyFrame::OnSize(wxSizeEvent& WXUNUSED(event))
  459. {
  460. wxLayoutAlgorithm layout;
  461. layout.LayoutMDIFrame(this);
  462. }
  463. // Note that SASHTEST_NEW_WINDOW and SASHTEST_ABOUT commands get passed
  464. // to the parent window for processing, so no need to
  465. // duplicate event handlers here.
  466. BEGIN_EVENT_TABLE(MyChild, wxMDIChildFrame)
  467. EVT_MENU(SPECTSEQ_CLOSE, MyChild::OnQuit)
  468. EVT_MENU(SPECTSEQ_SAVE, MyChild::OnSave)
  469. EVT_MENU(SPECTSEQ_SAVEAS, MyChild::OnSaveAs)
  470. EVT_MENU(SPECTSEQ_SAVESELECT, MyChild::OnSaveSelect)
  471. EVT_MENU(SPECTSEQ_SAVEPITCH, MyChild::OnSavePitchenv)
  472. EVT_ACTIVATE(MyChild::OnActivate)
  473. END_EVENT_TABLE()
  474. MyChild::MyChild(wxMDIParentFrame *parent, const wxString& title, const wxPoint& pos, const wxSize& size,
  475. const long style):
  476. wxMDIChildFrame(parent, -1, title, pos, size, style)
  477. {
  478. canvas = NULL;
  479. my_children.Append(this);
  480. }
  481. MyChild::~MyChild(void)
  482. {
  483. wxWindow *w;
  484. canvas = NULL;
  485. #ifndef PLATFORM_WINDOWS
  486. // bug in wxMDIChildFrame, we need to explicitly remove the ChildFrame from the ClientWindow
  487. w = myframe->GetClientWindow();
  488. w->RemoveChild(this);
  489. #endif
  490. my_children.DeleteObject(this);
  491. }
  492. //extern void CloseCanvas();
  493. void MyChild::OnQuit(wxCommandEvent& WXUNUSED(event))
  494. {
  495. Destroy();
  496. }
  497. void MyChild::OnSave(wxCommandEvent& WXUNUSED(event))
  498. {
  499. canvas->Save(canvas->savepath);
  500. }
  501. void MyChild::OnSaveAs(wxCommandEvent& WXUNUSED(event))
  502. {
  503. canvas->Save();
  504. }
  505. void MyChild::OnSaveSelect(wxCommandEvent& WXUNUSED(event))
  506. {
  507. canvas->Save(_T(""),1);
  508. }
  509. void MyChild::OnSavePitchenv(wxCommandEvent& WXUNUSED(event))
  510. {
  511. canvas->SavePitchenv(canvas->spectseq->pitchenv);
  512. }
  513. void MyChild::OnActivate(wxActivateEvent& event)
  514. {
  515. if(canvas)
  516. canvas->OnActivate(event.GetActive());
  517. }