123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640 |
- /***************************************************************************
- * Copyright (C) 2005 to 2007 by Jonathan Duddington *
- * email: [email protected] *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 3 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write see: *
- * <http://www.gnu.org/licenses/>. *
- ***************************************************************************/
-
-
- #include <math.h>
- #include "wx/wx.h"
- #include <wx/dcmemory.h>
- #include <wx/dc.h>
- #include <wx/bitmap.h>
- #include <wx/dirdlg.h>
- #include "wx/filename.h"
- #include "wx/wfstream.h"
-
- #include "speak_lib.h"
- #include "main.h"
- #include "speech.h"
- #include "phoneme.h"
- #include "synthesize.h"
- #include "voice.h"
- #include "spect.h"
- #include "translate.h"
- #include "options.h"
-
- /* Read a file of vowel symbols and f1,f2 formants, and produce a vowel diagram
- */
- extern wxString path_phsource;
-
- extern char *spects_data;
- extern int n_phoneme_tables;
-
-
- // size of the vowelchart png
- #define WIDTH 1580
- #define HEIGHT 800
-
-
- #define ROUND(x) ((int) ((x) + 0.5))
-
- static int HslValue (double n1, double n2, double hue)
- {//===================================================
- double value;
-
- if (hue > 255)
- hue -= 255;
- else if (hue < 0)
- hue += 255;
-
- if (hue < 42.5)
- value = n1 + (n2 - n1) * (hue / 42.5);
- else if (hue < 127.5)
- value = n2;
- else if (hue < 170)
- value = n1 + (n2 - n1) * ((170 - hue) / 42.5);
- else
- value = n1;
-
- return ROUND (value * 255.0);
- }
-
- /**
- * @hue: Hue channel, returns Red channel
- * @saturation: Saturation channel, returns Green channel
- * @lightness: Lightness channel, returns Blue channel
- *
- * The arguments are pointers to int, with the values pointed to in the
- * following ranges: H [0, 360], L [0, 255], S [0, 255].
- *
- * The function changes the arguments to point to the RGB value
- * corresponding, with the returned values all in the range [0, 255].
- **/
- void HslToRgb (int *hue, int *saturation, int *lightness)
- {//======================================================
- double h, s, l;
-
- h = (*hue * 256)/360;
- s = *saturation;
- l = *lightness;
-
- if (s == 0)
- {
- /* achromatic case */
- *hue = (int)l;
- *lightness = (int)l;
- *saturation = (int)l;
- }
- else
- {
- double m1, m2;
-
- if (l < 128)
- m2 = (l * (255 + s)) / 65025.0;
- else
- m2 = (l + s - (l * s) / 255.0) / 255.0;
-
- m1 = (l / 127.5) - m2;
-
- /* chromatic case */
- *hue = HslValue (m1, m2, h + 85);
- *saturation = HslValue (m1, m2, h);
- *lightness = HslValue (m1, m2, h - 85);
- }
- }
-
-
- static int vowel_posn[N_PHONEME_TAB];
- static int vowel_posn_ix;
-
-
- static double log2a(double x)
- {//========================
- // log2(x) = log(x) / log(2)
- return(log(x) / 0.693147);
- }
-
- static int VowelX(int f2)
- {//======================
- return(WIDTH - int((log2a(f2) - 9.40)*WIDTH/1.9));
- // return(WIDTH - int((log2a(f2) - 9.49)*WIDTH/1.8));
- }
-
- static int VowelY(int f1)
- {//======================
- return(int((log2a(f1) - 7.85)*HEIGHT/2.15));
- }
-
- static int VowelZ(int f3)
- {//======================
- int z;
- // range 2000-3000Hz, log2= 10.96 to 11.55
- z = int((log2a(f3) - 11.05)*256/0.50);
- if(z < 0) z = 0;
- if(z > 255) z = 255;
- return(z);
- }
-
-
- static void DrawVowel(wxDC *dc, wxString name, int f1, int f2, int f3, int g1, int g2)
- {//==================================================================================
- int ix;
- int posn;
- int collisions;
- int x,y,z,x2,y2;
- int r,g,b;
- wxBrush brush;
-
-
- y = VowelY(f1);
- x = VowelX(f2);
- z = VowelZ(f3);
-
- if(y < 0) y = 0;
- if(y > (HEIGHT-4)) y= (HEIGHT-4);
- if(x < 0) x = 0;
- if(x > (WIDTH-12)) x = (WIDTH-12);
-
- r = z;
- g = 255;
- b = 100 + z/2;
- HslToRgb(&r,&g,&b);
- brush.SetColour(r,g,b);
- dc->SetBrush(brush);
- dc->DrawCircle(x,y,4);
-
- // check for a label already at this position
- collisions = 0;
- posn = (x/8)*WIDTH + (y/8);
- for(ix=0; ix<vowel_posn_ix; ix++)
- {
- if(posn == vowel_posn[ix])
- collisions++;
- }
- vowel_posn[vowel_posn_ix++] = posn;
-
- dc->DrawText(name,x+4,y+(collisions*10));
-
- if(g2 != 0xffff)
- {
- y2 = VowelY(g1);
- x2 = VowelX(g2);
- dc->DrawLine(x,y,x2,y2);
- }
- }
-
-
-
- static int VowelChartDir(wxDC *dc, wxBitmap *bitmap)
- {//=================================================
- int ix;
- int nf;
- int count = 0;
- SpectSeq *spectseq;
- SpectFrame *frame1;
- SpectFrame *frame2=NULL;
- wxFileName filename;
-
- wxString dir = wxDirSelector(_T("Directory of vowel files"),path_phsource);
- if(dir.IsEmpty()) return(0);
-
- wxString path = wxFindFirstFile(dir+_T("/*"),wxFILE);
-
- while (!path.empty())
- {
- if((spectseq = new SpectSeq) == NULL) break;
-
- filename = wxFileName(path);
- wxFileInputStream stream(path);
- if(stream.Ok() == FALSE)
- {
- path = wxFindNextFile();
- continue;
- }
- spectseq->Load(stream);
-
- nf = 0;
- frame1 = NULL;
-
- if(spectseq->numframes > 0)
- {
- frame2 = spectseq->frames[0];
- }
- for(ix=0; ix<spectseq->numframes; ix++)
- {
- if(spectseq->frames[ix]->keyframe)
- {
- nf++;
- frame2 = spectseq->frames[ix];
- if(frame2->markers & FRFLAG_VOWEL_CENTRE)
- frame1 = frame2;
- }
- }
- if((nf >= 3) && (frame1 != NULL))
- {
- DrawVowel(dc,wxString(filename.GetName()),
- frame1->peaks[1].pkfreq, frame1->peaks[2].pkfreq, frame1->peaks[3].pkfreq,
- frame2->peaks[1].pkfreq, frame2->peaks[2].pkfreq);
-
- count++;
- }
- delete spectseq;
- path = wxFindNextFile();
- }
- filename.SetPath(dir);
- filename.SetFullName(_T("vowelchart.png"));
- bitmap->SaveFile(filename.GetFullPath(),wxBITMAP_TYPE_PNG);
- return(count);
- }
-
-
- static int VowelChartList(wxDC *dc, wxBitmap *bitmap, char *fname)
- {//===============================================================
- // Plot a graph of vowel formants.
- // y-axis is decreasing f1 (closeness)
- // x-axis is decreasing f2 (backness)
- FILE *f_in;
- int ix;
- int f1,f2,f3,g1,g2;
- int count=0;
- wxFileName filename;
- char name[40];
- char buf[200];
- wxString path;
-
- if(fname != NULL)
- {
- path = wxString(fname,wxConvLocal);
- }
- else
- {
- path = wxFileSelector(_T("Read file of vowel formants"),path_phsource,
- _T(""),_T(""),_T("*"),wxOPEN);
- }
- if(path.IsEmpty())
- {
- return(0);
- }
-
- filename = wxFileName(path);
- strcpy(buf,path.mb_str(wxConvLocal));
- f_in = fopen(buf,"r");
- if(f_in == NULL)
- {
- wxLogError(_T("Can't read file: %s"),buf);
- return(0);
- }
-
-
- while(fgets(buf,sizeof(buf),f_in) != NULL)
- {
- g2 = 0xffff;
- ix = sscanf(buf,"%s %d %d %d %d %d",name,&f1,&f2,&f3,&g1,&g2);
- if(ix >= 3)
- {
- DrawVowel(dc,wxString(name,wxConvLocal),
- f1,f2,f3,g1,g2);
- count++;
- }
- }
-
- filename.SetExt(_T("png"));
- bitmap->SaveFile(filename.GetFullPath(),wxBITMAP_TYPE_PNG);
- return(count);
- }
-
-
- void VowelChart(int control, char *fname)
- {//======================================
- // Plot a graph of vowel formants.
- // y-axis is decreasing f1 (closeness)
- // x-axis is decreasing f2 (backness)
- // control=1 from directory of lists
- // control=2 from single list
- // control=3 from directory of phoneme source data files
-
- int ix;
- int x,y;
- int count;
- wxFileName filename;
-
- wxBitmap bitmap(WIDTH,HEIGHT);
-
- // Create a memory DC
- wxMemoryDC dc;
- dc.SelectObject(bitmap);
- dc.SetBrush(*wxWHITE_BRUSH);
- dc.SetFont(*wxSWISS_FONT);
- dc.Clear();
-
- // draw grid
- dc.SetPen(*wxLIGHT_GREY_PEN);
- for(ix=200; ix<=1000; ix+=50)
- {
- y = VowelY(ix);
- dc.DrawLine(0,y,WIDTH,y);
- if((ix % 100) == 0)
- dc.DrawText(wxString::Format(_T("%d"),ix),1,y);
- }
- for(ix=700; ix<=2400; ix+=100)
- {
- x = VowelX(ix);
- dc.DrawLine(x,0,x,HEIGHT);
- if((ix % 200)==0)
- dc.DrawText(wxString::Format(_T("%d"),ix),x+1,0);
- }
- dc.SetPen(*wxBLACK_PEN);
-
- vowel_posn_ix = 0;
- if(control==3)
- count = VowelChartDir(&dc, &bitmap);
- else
- count = VowelChartList(&dc, &bitmap, fname);
-
- if(control != 1)
- wxLogStatus(_T("Plotted %d vowels"),count);
- }
-
-
-
- void FindPhonemesUsed(void)
- {//========================
- int hash;
- char *p;
- unsigned int *pw;
- char *start;
- char *next;
- unsigned char c;
- int count = 0;
- int ignore;
- char phonetic[N_WORD_PHONEMES];
-
- // look through all the phoneme strings in the **_rules data
- // and mark these phoneme codes as used.
- p = translator->data_dictrules;
- while(*p != 0)
- {
- if(*p == RULE_CONDITION)
- p+=2;
- if(*p == RULE_LINENUM)
- p+=3;
- if(*p == RULE_GROUP_END)
- {
- p++;
- if(*p == 0) break;
- }
- if(*p == RULE_GROUP_START)
- {
- if(p[1] == RULE_REPLACEMENTS)
- {
- p++;
- pw = (unsigned int *)(((long)p+4) & ~3); // advance to next word boundary
- while(pw[0] != 0)
- {
- pw += 2; // find the end of the replacement list, each entry is 2 words.
- }
- p = (char *)(pw+1);
- continue;
- }
-
- if(p[1] == RULE_LETTERGP2)
- {
- while(*p != RULE_GROUP_END) p++;
- continue;
- }
-
- p += (strlen(p)+1);
- }
-
- while((c = *p) != 0)
- {
- if(c == RULE_CONDITION)
- p++; // next byte is the condition number, which may be 3 (= RULE_PHONEMES)
- if(c == RULE_PHONEMES)
- break;
- p++;
- }
-
-
- count++;
- if(c == RULE_PHONEMES)
- {
- ignore = 0;
- start = p;
- p++;
- while((c = *p) != 0)
- {
- if(c == phonSWITCH)
- ignore = 1;
-
- if(ignore == 0)
- phoneme_tab_flags[c] |= 2;
- p++;
- }
- }
- p++;
- }
-
- // NOTE, we should recognise langopts.textmode and ignore the *_list file (lang=zh)
-
- for(hash=0; hash<N_HASH_DICT; hash++)
- {
- p = translator->dict_hashtab[hash];
- if(p == NULL)
- continue;
-
- while(*p != 0)
- {
- next = p + p[0];
- if((p[1] & 0x80) == 0)
- {
- p += ((p[1] & 0x3f) + 2);
- strcpy(phonetic,p);
- p += strlen(phonetic) +1;
-
- // examine flags
- ignore = 0;
- while(p < next)
- {
- if(*p == BITNUM_FLAG_TEXTMODE)
- {
- ignore = 1;
- break;
- }
- p++;
- }
-
- if(ignore == 0)
- {
- p = phonetic;
- while((c = *p) != 0)
- {
- if(c == phonSWITCH)
- break;
-
- phoneme_tab_flags[c] |= 2;
- p++;
- }
- }
- }
- p = next;
- }
- }
- } // end of FindPhonemesUsed
-
-
-
- void MakeVowelLists(void)
- {//======================
- // For each phoneme table, make a list of its vowels and their
- // formant frequencies (f1,f2,f3) for use by VowelChart()
-
- int table;
- int ix;
- int phcode;
- PHONEME_TAB *ph;
- FILE *f;
- SPECT_SEQ *seq;
- SPECT_SEQK *seqk;
- frame_t *frame;
- int match_level;
- char dirname[sizeof(path_source)+20];
- char fname[sizeof(dirname)+40];
-
- progress = new wxProgressDialog(_T("Vowel charts"),_T(""),n_phoneme_tables);
-
- sprintf(dirname,"%s%s",path_source,"vowelcharts");
- mkdir(dirname,S_IRWXU | S_IRGRP | S_IROTH);
-
- for(table=0; table<n_phoneme_tables; table++)
- {
- sprintf(fname,"%s/%s",dirname,phoneme_tab_list[table].name);
- if((f = fopen(fname,"w"))==NULL) continue;
-
- progress->Update(table);
-
- // select the phoneme table by name
- // if(SetVoiceByName(phoneme_tab_list[table].name) != 0) continue;
- if(SelectPhonemeTableName(phoneme_tab_list[table].name) < 0) continue;
- FindPhonemesUsed();
-
- // phoneme table is terminated by a phoneme with no name (=0)
- for(phcode=1; phcode < n_phoneme_tab; phcode++)
- {
- //if((phoneme_tab_flags[phcode] & 3) == 0)
- // continue; // inherited, and not used
-
- ph = phoneme_tab[phcode];
-
- if(ph->type != phVOWEL)
- continue;
-
- if((ix = LookupSound(ph, phoneme_tab[phonPAUSE], 1, &match_level, 0)) == 0)
- continue;
-
- seq = (SPECT_SEQ *)(&spects_data[ix]);
- seqk = (SPECT_SEQK *)seq;
-
- if(seq->frame[0].frflags & FRFLAG_KLATT)
- frame = &seqk->frame[1];
- else
- frame = (frame_t *)&seq->frame[1];
-
- fprintf(f,"%s\t %3d %4d %4d",WordToString(ph->mnemonic),
- frame->ffreq[1],frame->ffreq[2],frame->ffreq[3]);
-
- if(seq->frame[0].frflags & FRFLAG_KLATT)
- frame = &seqk->frame[seqk->n_frames-1];
- else
- frame = (frame_t *)&seq->frame[seq->n_frames-1];
- fprintf(f," %3d %4d %4d\n",frame->ffreq[1],frame->ffreq[2],frame->ffreq[3]);
- }
- fclose(f);
-
- VowelChart(1,fname); // draw the vowel chart
- }
- LoadVoice(voice_name2,0); // reset the original phoneme table
- delete progress;
- }
-
-
- extern int n_envelopes;
- extern char envelope_paths[][80];
- extern unsigned char envelope_dat[][128];
-
- #define HT_ENV 140
- #define WD_ENV 128*2
-
- void DrawEnvelopes()
- {//================
- int ix_env;
- int y_base;
- int x;
- FILE *f_txt=NULL;
- unsigned char *env;
- char name[200];
-
- wxBitmap bitmap(WD_ENV,HT_ENV*n_envelopes);
-
- // Create a memory DC
- wxMemoryDC dc;
- dc.SelectObject(bitmap);
- dc.SetBrush(*wxWHITE_BRUSH);
- dc.SetFont(*wxSWISS_FONT);
- dc.Clear();
-
- sprintf(name,"%s%s",path_source,"envelopes.txt");
- // f_txt = fopen(name,"w");
-
- for(ix_env=0; ix_env<n_envelopes; ix_env++)
- {
- y_base = HT_ENV * ix_env;
- dc.SetPen(*wxLIGHT_GREY_PEN);
- dc.DrawLine(0,y_base+0,256,y_base+0);
- dc.DrawLine(0,y_base+64,256,y_base+64);
- dc.DrawLine(0,y_base+128,256,y_base+128);
- dc.DrawLine(128,y_base+0,128,y_base+128);
-
- dc.SetPen(*wxBLACK_PEN);
- strncpy0(name,envelope_paths[ix_env],sizeof(name));
- dc.DrawText(wxString(name,wxConvLocal),1,y_base);
-
- env = envelope_dat[ix_env];
- y_base = y_base+128;
- for(x=0; x<127; x++)
- {
- dc.DrawLine(x*2, y_base-env[x]/2, (x+1)*2, y_base-env[x+1]/2);
- }
-
- if(f_txt != NULL)
- {
- fprintf(f_txt,"%s\n",name);
- for(x=0; x<128; x++)
- {
- fprintf(f_txt," 0x%.2x,",env[x]);
- if((x & 0xf) == 0xf)
- fputc('\n',f_txt);
- }
- fputc('\n',f_txt);
- }
- }
-
- bitmap.SaveFile(path_phsource+_T("/envelopes.png"),wxBITMAP_TYPE_PNG);
- if(f_txt != NULL)
- fclose(f_txt);
- }
|