/*************************************************************************** * Copyright (C) 2005 to 2007 by Jonathan Duddington * * email: jonsd@users.sourceforge.net * * * * 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: * * . * ***************************************************************************/ #include "wx/wx.h" #include #include "speak_lib.h" #include "speech.h" #include "phoneme.h" #include "synthesize.h" #include "voice.h" #include "spect.h" #include "main.h" #include "wx/numdlg.h" #include "wx/txtstrm.h" #include "wx/datstrm.h" #define MAX_HARMONIC 400 // 400 * 50Hz = 20 kHz, more than enough int SpeakNextClause(FILE *f_text, const void *text_in, int control); extern void SetSynth(int length, int modn, frame_t *fr1, frame_t *fr2); extern int Wavegen(); extern void CloseWaveFile2(); extern FILE *f_wave; static int frame_width; int pk_select; wxBrush CREAM_BRUSH(wxColour(255,253,245),wxSOLID); wxPen BORDER_PEN(wxColour(255,240,0),4,wxSOLID); wxPen VLIGHT_GREY_PEN(wxColour(230,230,230),1,wxSOLID); float polint(float xa[],float ya[],int n,float x) {//============================================== // General polinomial interpolation routine, xa[1...n] ya[1...n] int i,m,ns=1; float den,dif,dift,ho,hp,w; float y; // result float c[9],d[9]; dif=fabs(x-xa[1]); for(i=1;i<=n;i++){ if((dift=fabs(x-xa[i])) < dif) { ns=i; dif=dift; } c[i]=ya[i]; d[i]=ya[i]; } y=ya[ns--]; for(m=1;m 0) frames = new SpectFrame* [n]; else frames = NULL; pk_select = 1; grid = 1; duration = 0; pitch1 = 0; pitch2 = 0; bass_reduction = 0; max_x = 3000; max_y = 1; } SpectSeq::~SpectSeq() {//================== int ix; if(frames != NULL) { for(ix=0; ixselected = yes; } int SpectSeq::CountSelected() {//========================== int ix; int count=0; for(ix=0; ixselected) count++; } return(count); } // end of SpectSeq::CountSelected void SpectSeq::DeleteSelected() {//============================ int ix; int count=0; for(ix=0; ixselected) { count++; } else if(count > 0) { *frames[ix-count] = *frames[ix]; } } numframes = numframes - count; } // end of SpectSeq::DeleteSelected void SpectSeq::ClipboardCopy() {//=========================== int ix; int nframes; int count=0; nframes = CountSelected(); if(nframes == 0) return; if(clipboard_spect != NULL) delete clipboard_spect; if((clipboard_spect = new SpectSeq(nframes))==NULL) return; for(ix=0; ixselected) { if((clipboard_spect->frames[count] = new SpectFrame(frames[ix])) == NULL) break; count++; } } } // end of SpectSeq::ClipboardCopy int SpectSeq::ClipboardInsert(int insert_at) {//========================================= int ix; int j; int total; int result=insert_at; float thistime=0; float timeinc=0; float timeoffset=0; SpectFrame **frames2; if(clipboard_spect == NULL) return(result); if(clipboard_spect->numframes == 0) return(result); timeoffset = clipboard_spect->frames[0]->time; if(CountSelected() == 0) insert_at = -1; total = numframes + clipboard_spect->numframes; frames2 = new SpectFrame* [total]; if(frames2 == NULL) return(result); total = 0; for(ix=0; ixtime; if(ix == insert_at) { result = total; for(j=0; jnumframes; j++) { frames2[total] = new SpectFrame(clipboard_spect->frames[j]); frames2[total]->time += (thistime - timeoffset); timeinc = frames2[total]->time - thistime + (frames2[total]->length/1000); total++; } } frames2[total] = new SpectFrame(frames[ix]); frames2[total++]->time += timeinc; } if(insert_at == -1) { // insert at the end result = total; for(j=0; jnumframes; j++) { frames2[total] = new SpectFrame(clipboard_spect->frames[j]); frames2[total++]->time += (thistime - timeoffset); } } delete frames; frames = frames2; numframes = total; return(result); } // end of SpectSeq::ClipboardInsert void SpectSeq::SetFrameLengths() {//============================= int frame; for(frame=0; framekeyframe) frames[frame]->length = GetFrameLength(frame,1,NULL); else frames[frame]->length = 0; } } // end of SetFrameLengths float SpectSeq::GetFrameLength(int frame, int plus, int *original) {//=============================================================== int ix; float adjust=0; if(frame >= numframes-1) return(0); // include the adjustment for this frame ? if(plus) adjust = frames[frame]->length_adjust; for(ix=frame+1; ixkeyframe) break; // reached next keyframe adjust += frames[ix]->length_adjust; } if(original != NULL) *original = int((frames[ix]->time - frames[frame]->time) * 1000.0 + 0.5); return ((frames[ix]->time - frames[frame]->time) * 1000.0 + adjust); } float SpectSeq::GetKeyedLength() {//============================= int ix; int first; int last=0; float adjust=0; first = -1; for(ix=0; ixkeyframe) { last = ix; if(first == -1) first = ix; } } if(first == -1) return(0); // no keyframes for(ix=first; ixlength_adjust; return((frames[last]->time - frames[first]->time) * 1000 + adjust); } void SpectSeq::Load2(wxInputStream& stream, int import, int n) {//=========================================================== // continuation of load/import int ix; wxString string; float time_offset; float time_acc=0; int set_max_y=0; if(n==0) return; if(frames != NULL) delete frames; frames = new SpectFrame* [n]; numframes = 0; max_x = 3000; if(max_y == 0) { set_max_y = 1; max_y = 1; } for(ix = 0; ix < n; ix++) { SpectFrame *frame = new SpectFrame; if(import==1) { if(frame->Import(stream) != 0) break; } else if(import==2) { if(frame->ImportSPC2(stream,time_acc) != 0) break; } else { if(frame->Load(stream) != 0) break; } frames[numframes++] = frame; if(set_max_y && (frame->max_y > max_y)) max_y = frame->max_y; if(frame->nx * frame->dx > max_x) max_x = int(frame->nx * frame->dx); } max_x = 9000; // disable auto-xscaling frame_width = int((FRAME_WIDTH*max_x)/MAX_DISPLAY_FREQ); if(frame_width > FRAME_WIDTH) frame_width = FRAME_WIDTH; // start times from zero time_offset = frames[0]->time; for(ix=0; ixtime -= time_offset; pitch1 = pitchenv.pitch1; pitch2 = pitchenv.pitch2; duration = int(frames[numframes-1]->time * 1000); if(max_y < 400) max_y = 200; else max_y = 29000; // disable auto height scaling } // end of SpectSeq::Load2 int SpectSeq::Import(wxInputStream& stream) {//======================================== int n = 0; wxTextInputStream text_stream(stream); name = _T(""); text_stream >> n; amplitude = 100; max_y = 0; Load2(stream,1,n); return(0); } // end of SpectSeq::Import int SPC2_size_cycle(CYCLE *cy) /****************************/ /* Find number of bytes in cycle record */ { int i; i = 44 + cy->n_harm; if(cy->flags & 1) { i += 4; /* label */ } return(i); } /* end of size_cycle */ int SpectSeq::ImportSPC2(wxInputStream & stream) {//============================================= // load an spectrum with an old "SPC2" format int n_cycles = 0; int x; CYCLE cy; int pos; /* count number of cycles */ while(!stream.Eof()) { pos = stream.TellI(); stream.Read(&cy,44); pos = stream.TellI(); if(stream.Eof()) break; n_cycles++; x = SPC2_size_cycle(&cy) - 44; stream.SeekI(x,wxFromCurrent); } if(n_cycles == 0) return(0); name = _T(""); amplitude = 100; max_y = 0; stream.SeekI(4); // rewind and skip header Load2(stream,2,n_cycles); return(0); } int SpectSeq::Load(wxInputStream & stream) {//======================================= int n; int ix; unsigned int id1, id2; wxDataInputStream s(stream); id1 = s.Read32(); id2 = s.Read32(); if(id1 == FILEID1_SPC2) { stream.SeekI(4); return(ImportSPC2(stream)); } if(id1 != FILEID1_SPECTSEQ || id2 != FILEID2_SPECTSEQ) { stream.SeekI(0); return(Import(stream)); } name = s.ReadString(); n = s.Read16(); amplitude = s.Read16(); max_y = s.Read16(); s.Read16(); Load2(stream,0,n); for(ix=0; ixkeyframe) frames[ix]->length_adjust = frames[ix]->length - GetFrameLength(ix,0,NULL); } return(0); } // end of SpectSeq::Load int SpectSeq::Save(wxOutputStream &stream, int selection) {//====================================================== int ix; int count=numframes; if(selection) { count = CountSelected(); } SetFrameLengths(); wxDataOutputStream s(stream); s.Write32(FILEID1_SPECTSEQ); s.Write32(FILEID2_SPECTSEQ); s.WriteString(name); s.Write16(count); s.Write16(amplitude); s.Write16(selection ? max_y : 0); s.Write16(0); // spare for(ix=0; ixselected) { if(frames[ix]->Save(stream) != 0) return(1); } } return(0); } // end of SpectSeq::Save void SpectSeq::ConstructVowel(void) {//================================ // not completed int ix; int j=0; int frames_selected[4]; for(ix=0; ixselected) { if(ix >= 4) break; frames_selected[j++] = ix; } } if(j==0 || j>= 4) return; if(frames_selected[0] == 0) return; } // end of ConstructVowel void SpectSeq::Draw(wxDC& dc, int start_y, int end_y) {//================================================== int fm; int f, f1, f2; int x; if(end_y < start_y) return; if((start_y -= 4) < 0) start_y = 0; f1 = start_y / FRAME_HEIGHT; f2 = end_y / FRAME_HEIGHT; scaley = double(FRAME_HEIGHT) / max_y; scalex = double(frame_width) / max_x; // scalex = 0.6; for(fm=f1; fm <= f2 && fm < numframes; fm++) { if(frames[fm]->keyframe) { dc.SetBrush(CREAM_BRUSH); dc.SetPen(BORDER_PEN); } else { dc.SetBrush(*wxWHITE_BRUSH); dc.SetPen(*wxTRANSPARENT_PEN); } if(frames[fm]->selected) dc.SetPen(*wxRED_PEN); dc.DrawRectangle(0,FRAME_HEIGHT*fm+2,frame_width, FRAME_HEIGHT-2); } if(grid==1) { for(f=500; f<=MAX_DISPLAY_FREQ; f+=500) { x = int(f * scalex); if(x > max_x) break; if(f==3000 || f==6000 || f==9000) dc.SetPen(*wxLIGHT_GREY_PEN); else dc.SetPen(VLIGHT_GREY_PEN); dc.DrawLine(x,start_y,x,numframes*FRAME_HEIGHT); } } for(fm=f1; fm <= f2 && fm < numframes; fm++) { frames[fm]->Draw(dc,FRAME_HEIGHT*(fm+1),frame_width, scalex,scaley); } } // end of SpectSeq::Draw void SpectSeq::InterpolatePeak(int peak) {//===================================== int f, f1=0, f2; peak_t *p, *p1=NULL, *p2; double t1=0, t2; double interval; double ratio; int first = 1; for(f2=0; f2 < numframes; f2++) { if(frames[f2]->keyframe) { t2 = frames[f2]->time; p2 = &frames[f2]->peaks[peak]; if(first) first = 0; else { interval = t2 - t1; for(f=f1+1; fpeaks[peak]; ratio = (frames[f]->time - t1)/interval; p->pkfreq = p1->pkfreq + int((p2->pkfreq - p1->pkfreq)*ratio); p->pkheight=p1->pkheight+int((p2->pkheight-p1->pkheight)*ratio); p->pkwidth = p1->pkwidth + int((p2->pkwidth - p1->pkwidth)*ratio); p->pkright =p1->pkright + int((p2->pkright - p2->pkright)*ratio); } } f1 = f2; t1 = t2; p1 = p2; } } } // end of SpectSeq::InterpolatePeak void SpectSeq::InterpolateAdjacent(void) {//===================================== int ix; int f1 = -1; int select = -1; int f2 = -1; float ratio; peak_t *p = NULL; peak_t *p1 = NULL; peak_t *p2 = NULL; for(ix=0; ixselected) select = ix; else if(frames[ix]->keyframe) { if(select >= 0) { f2 = ix; break; } else f1 = ix; } } if(f1 < 0) { wxLogError(_T("No previous keyframe")); return; } if(select < 0) { wxLogError(_T("No selected frame")); return; } if(f2 < 0) { wxLogError(_T("No subsequent keyframe")); return; } // get ratio ix = wxGetNumberFromUser(_T("Interpolate between adjacent frames"),_T("percent"),_T(""),50); ratio = (float)ix/100.0; for(ix=0; ixpeaks[ix]; p1 = &frames[f1]->peaks[ix]; p2 = &frames[f2]->peaks[ix]; p->pkfreq = p1->pkfreq + int((p2->pkfreq - p1->pkfreq)*ratio); p->pkheight=p1->pkheight+int((p2->pkheight-p1->pkheight)*ratio); p->pkwidth = p1->pkwidth + int((p2->pkwidth - p1->pkwidth)*ratio); p->pkright =p1->pkright + int((p2->pkright - p2->pkright)*ratio); } frames[select]->keyframe = 1; formantdlg->ShowFrame(this,select,1,0xff); } void SpectSeq::InterpolatePeaks(int control) {//========================================= // 0=turn off 1=turn on int f, peak; if(control==1) { for(peak=0; peakkeyframe == 0) frames[f]->ZeroPeaks(); } } } // end of SpectSeq::InterpolatePeaks void SpectSeq::CopyDown(int frame, int direction) {//============================================== // Copy peaks down from next earlier/later keyframe int f1; int pk; for(f1=frame+direction; f1>=0 && f1keyframe) { for(pk=0; pkpeaks[pk].pkfreq = frames[f1]->peaks[pk].pkfreq; frames[frame]->peaks[pk].pkheight = frames[f1]->peaks[pk].pkheight; frames[frame]->peaks[pk].pkwidth = frames[f1]->peaks[pk].pkwidth; frames[frame]->peaks[pk].pkright = frames[f1]->peaks[pk].pkright; } break; } } } // end of CopyDown void SpectSeq::MakePitchenv(PitchEnvelope &pitchenv, int start_frame, int end_frame) {//================================================================================= double f; double min=8000; double max=0; double diff; double t_start = -1; double t_end=0, t_diff; double yy; int ix; int x, y; int xx; int nx=0; float *ax, *ay; memset(pitchenv.env,127,128); for(ix=start_frame; ix<=end_frame; ix++) { if((f = frames[ix]->pitch) == 0) continue; nx++; t_end = frames[ix]->time; if(t_start < 0) t_start = t_end; if(f < min) min = f; if(f > max) max = f; } diff = max-min; t_diff = t_end - t_start; if(nx<2 || diff<=0 || t_diff<=0) { // no pitch info, use defaults pitchenv.pitch1=80; pitchenv.pitch2=120; return; } pitchenv.pitch1 = int(min); pitchenv.pitch2 = int(max); ax = new float [nx+1]; ay = new float[nx+1]; nx = 0; for(ix=start_frame; ix<=end_frame; ix++) { if((f = frames[ix]->pitch) == 0) continue; ax[++nx] = (frames[ix]->time - t_start) * 128 / t_diff; ay[nx] = (frames[ix]->pitch - min) * 255 / diff; } pitchenv.env[0] = int(ay[1]); pitchenv.env[127] = int(ay[nx]); // create pitch envelope by interpolating the time/pitch // values from the spectrum sequence xx = 1; for(x=1; x<127; x++) { while((ax[xx] < x) && (xx < nx)) xx++; if(xx < 3) yy = polint(&ax[xx-1],&ay[xx-1],3,(float)x); else if(xx > nx-1) yy = polint(&ax[xx-2],&ay[xx-2],3,(float)x); else yy = polint(&ax[xx-2],&ay[xx-2],4,(float)x); y = int(yy); if(y < 0) y = 0; if(y > 255) y = 255; pitchenv.env[x] = y; } delete ax; delete ay; } // end of SpectSeq::MakePitchenv void SpectSeq::ApplyAmp_adjust(SpectFrame *sp, peak_t *peaks) {//============================================================= int ix; int y; memcpy(peaks,sp->peaks,sizeof(*peaks)*N_PEAKS); for(ix=0; ixamp_adjust * amplitude; peaks[ix].pkheight = y / 10000; } } // end of ApplyAmp_adjust void PeaksToFrame(peak_t *pks, frame_t *fr) {//======================================== int ix; int x; for(ix=0; ixffreq[ix] = pks[ix].pkfreq; fr->fheight[ix] = pks[ix].pkheight >> 6; if(ix < 6) { if((x = (pks[ix].pkwidth >> 2)) > 255) x = 255; fr->fwidth[ix] = x; if((x = (pks[ix].pkright >> 2)) > 255) x = 255; fr->fright[ix] = x; } } } static void SetSynth_mS(int length_mS, peak_t *sp1, peak_t *sp2) {//============================================================= static frame_t fr1, fr2; PeaksToFrame(sp1,&fr1); PeaksToFrame(sp2,&fr2); SetSynth((length_mS * samplerate) / 1000, 0, &fr1, &fr2); // convert mS to samples } void SpectSeq::MakeWave(int start, int end, PitchEnvelope &pitch) {//============================================================== int ix; int length; int len_samples; int total_length; float sum_length=0; float prev_length=0; int first; int pbase; char *fname_speech; SpectFrame *sp1 = NULL; SpectFrame *sp2; double lfactor; peak_t peaks0[N_PEAKS]; peak_t peaks1[N_PEAKS]; peak_t peaks2[N_PEAKS]; SpeakNextClause(NULL,NULL,2); // stop speaking file if(numframes==0) return; SetFrameLengths(); // find overall length of sequence for(ix=0; ixkeyframe) { sum_length += prev_length; prev_length = frames[ix]->length; sp2 = frames[ix]; if(sp1 == NULL) sp1 = sp2; } } if(sp1 == NULL) { wxLogError(_T("(No frames have peaks set")); return; } total_length = int(sum_length); pbase = voice->pitch_base >> 12; if((start==end) || (total_length == 0)) { sp1->MakeWave(0,voicedlg->pitchenv,amplitude,duration); return; } if((duration > 0) && (duration < 40000)) lfactor = double(duration)/double(total_length); else { duration = total_length; lfactor = 1; } // if((start==end) || (total_length == 0)) // { // sp1->MakeWave(1, pitch, amplitude, duration); // return; // } len_samples = int(((total_length * lfactor + 50) * samplerate) / 1000); // SetPitch(len_samples,pitch.env,pitch.pitch1-pbase,pitch.pitch2-pbase); SetPitch(len_samples,pitch.env,9,44); fname_speech = WavFileName(); OpenWaveFile2(fname_speech); first = 0; if(start > 0) first=1; // a selection, use fade-in sp2 = NULL; for(ix=start; ix<=end; ix++) { if(frames[ix]->keyframe) { sp1 = sp2; sp2 = frames[ix]; if(sp1 != NULL) { ApplyAmp_adjust(sp1,peaks1); ApplyAmp_adjust(sp2,peaks2); if(first) { PeaksZero(peaks1,peaks0); // fade in SetSynth_mS(20,peaks0,peaks1); MakeWaveFile(); first=0; } length = int(sp1->length * lfactor); SetSynth_mS(length,peaks1,peaks2); MakeWaveFile(); } } } PeaksZero(peaks2,peaks0); // fade out SetSynth_mS(30,peaks2,peaks0); MakeWaveFile(); CloseWaveFile2(); PlayWavFile(fname_speech); } // end of SpectSeq::MakeWave void SpectFrame::MakeHtab(int numh, int *htab, int pitch) {//====================================================== // interpolate the spectrum to give a harmonic table for // the given pitch (Hz<<12) } // end of SpectFrame::MakeHtab void SpectFrame::MakeWave(int control, PitchEnvelope &pitche, int amplitude, int duration) {//====================================================================================== // amplitude: percentage adjustment int ix; int length; // mS int len_samples; int y; peak_t peaks0[N_PEAKS]; peak_t peaks1[N_PEAKS]; int ipitch; int pbase; char *fname_speech; // USHORT htab0[600]; SpeakNextClause(NULL,NULL,2); // stop speaking file length = duration; ipitch = int(pitch) << 16; if(length==0) length = 200; // default length, mS len_samples = (length * samplerate) / 1000; pbase = voice->pitch_base >> 12; // SetPitch(len_samples + 50,pitche.env,pitche.pitch1-pbase,pitche.pitch2-pbase); SetPitch(len_samples + 50,pitche.env,9,44); fname_speech = WavFileName(); if(OpenWaveFile2(fname_speech) != 0) return; if(control==0) { memcpy(peaks1,peaks,sizeof(peaks1)); for(ix=0; ix= 600) maxh = 600-1; for(ix=0; ix<=maxh; ix++) htab0[ix] = 0; SetSynthHtab(20,htab0,maxh,dx,spect,maxh,dx); MakeWaveFile(); SetSynthHtab(length,spect,maxh,dx,spect,maxh,dx); MakeWaveFile(); SetSynthHtab(30,spect,maxh,dx,htab0,maxh,dx); MakeWaveFile(); #endif } CloseWaveFile2(); PlayWavFile(fname_speech); } // end of SpectFrame::MakeWaveFrame