/*************************************************************************/ /* */ /* Centre for Speech Technology Research */ /* University of Edinburgh, UK */ /* Copyright (c) 1995,1996 */ /* All Rights Reserved. */ /* Permission is hereby granted, free of charge, to use and distribute */ /* this software and its documentation without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of this work, and to */ /* permit persons to whom this work is furnished to do so, subject to */ /* the following conditions: */ /* 1. The code must retain the above copyright notice, this list of */ /* conditions and the following disclaimer. */ /* 2. Any modifications must be clearly marked as such. */ /* 3. Original authors' names are not deleted. */ /* 4. The authors' names are not used to endorse or promote products */ /* derived from this software without specific prior written */ /* permission. */ /* THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK */ /* DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING */ /* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT */ /* SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE */ /* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES */ /* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN */ /* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */ /* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */ /* THIS SOFTWARE. */ /* */ /*************************************************************************/ /* Author : Paul Taylor */ /* Date : April 1994 */ /*************************************************************************/ #include "global.h" #include "RageUtil.h" #include "RageLog.h" #include "RageSoundReader_WAV.h" #include "RageSurface_Load.h" #include "PitchDetectionTestUtil.h" #include "EST_String.h" #include "EST_Chunk.h" #include "EST_Val.h" #include "EST_Features.h" #include "EST_Token.h" #include "EST_Option.h" #include "EST_Track.h" #include "PitchDetectionTest.h" // EST Library: http://www.cstr.ed.ac.uk/projects/speech_tools/ // "pda" executable options: http://festvox.org/docs/speech_tools-1.2.0/x2152.htm // Autocorrelation explanation: http://cnx.org/content/m11714/latest/ // PDA algorithm tradeoffs: http://www-scf.usc.edu/~chinghuc/pitch_detection_algorithms.htm // MIDI note numbers: http://tomscarff.tripod.com/midi_analyser/midi_note_frequency.htm // MIDI note numbers: http://www.sengpielaudio.com/calculator-notenames.htm EST_String::EST_String(const char *s) { CHECK_STRING_ARG(s); size=safe_strlen(s); if (size != 0) memory = chunk_allocate(size+1, s, size); else memory=NULL; } void EST_Chunk::operator delete (void *it) { #if defined(__CHUNK_USE_WALLOC__) wfree(it); #else delete it; #endif } val_type val_unset = "unset"; val_type val_int = "int"; val_type val_float = "float"; val_type val_string = "string"; #define VAL_REGISTER_CLASS_DCLS(NAME,CLASS) \ extern val_type val_type_##NAME; \ class CLASS *NAME(const EST_Val &v); \ EST_Val est_val(const class CLASS *v); VAL_REGISTER_CLASS_DCLS(feats,EST_Features) #define VAL_REGISTER_CLASS(NAME,CLASS) \ val_type val_type_##NAME=#NAME; \ class CLASS *NAME(const EST_Val &v) \ { \ if (v.type() == val_type_##NAME) \ return (class CLASS *)v.internal_ptr(); \ else \ exit(1); /*EST_error("val not of type val_type_"#NAME);*/ \ return NULL; \ } \ \ static void val_delete_##NAME(void *v) \ { \ delete (class CLASS *)v; \ } \ \ EST_Val est_val(const class CLASS *v) \ { \ return EST_Val(val_type_##NAME, \ (void *)v,val_delete_##NAME); \ } \ VAL_REGISTER_CLASS(feats,EST_Features) EST_Val::~EST_Val(void) { if ((t != val_int) && (t != val_float) && (t != val_unset) && (t != val_string)) delete v.pval; } EST_Chunk::~EST_Chunk () { if (count > 0) { //cerr << "deleting chunk with non-zero count\n"; exit(1); } // cerr << "deleted "<< hex << (int)&memory << "," << dec << size <<"\n"; } void EST_Features::set_path(const EST_String &name, const EST_Val &sval) { // Builds sub features (if necessary) if (strchr(name,'.') == NULL) set_val(name,sval); else { EST_String nname = name; EST_String fname = nname.before("."); if (present(fname)) { const EST_Val &v = val(fname); if (v.type() == val_type_feats) feats(v)->set_path(nname.after("."),sval); else exit(1);//EST_error("Feature %s not feature valued\n", // (const char *)fname); } else { EST_Features f; set(fname,f); A(fname).set_path(nname.after("."),sval); } } } template V &EST_TKVL::val(const K &rkey, int must) { EST_Litem *ptr = find_pair_key(rkey); if (ptr == 0) { if (must) EST_error("No value set for '%s'", error_name(rkey)); return *default_val; } else return list.item(ptr).v; } EST_String &EST_TKVL::val(const EST_String &rkey, int must) { EST_Litem *ptr = find_pair_key(rkey); if (ptr == 0) { if (must) exit(1);//EST_error("No value set for '%s'", error_name(rkey)); return *default_val; } else return list.item(ptr).v; } template int EST_TKVL::add_item(const K &rkey, const V &rval, int no_search) { if (!no_search) if (change_val(rkey, rval)) // first see if key exists return 1; EST_TKVI item; item.k = rkey; item.v = rval; list.append(item); return 1; } int EST_TKVL::add_item(const EST_String &rkey, const EST_String &rval, int no_search) { if (!no_search) if (change_val(rkey, rval)) // first see if key exists return 1; EST_TKVI item; item.k = rkey; item.v = rval; list.append(item); return 1; } template const int EST_TKVL::present(const K &rkey) const { if (find_pair_key(rkey) == 0) return 0; else return 1; } const int EST_TKVL::present(const EST_String &rkey) const { if (find_pair_key(rkey) == 0) return 0; else return 1; } static const EST_String Empty_String(""); int EST_Option::ival(const EST_String &rkey, int must) const { const EST_String &tval = val_def(rkey, Empty_String); if (tval != "") return atoi(tval); if (must) exit(1);//cerr << "EST_Option: No value set for " << rkey << endl; return 0; } EST_String &EST_String::operator = (const char *str) { CHECK_STRING_ARG(str); int len = safe_strlen(str); if (!len) memory = NULL; else if (!shareing() && len < size) memcpy((char *)memory, str, len+1); else if (len) memory = chunk_allocate(len+1, str, len); size=len; return *this; } EST_String &EST_String::operator = (const char c) { memory = chunk_allocate(2, &c, 1); size=1; return *this; } EST_String &EST_String::operator = (const EST_String &s) { #if 1 static EST_ChunkPtr hack = s.memory; memory = NON_CONST_CHUNKPTR(s.memory); size = s.size; #else *(struct EST_dumb_string *)this = *(struct EST_dumb_string *)(&s); #endif return *this; } int operator == (const char *a, const EST_String &b) { CHECK_STRING_ARG(a); if (!a) return 0; else if (b.size==0) return *a == '\0'; else return (*a == b(0)) && strcmp(a, b.str())==0; } const EST_Val &EST_Features::val_path(const EST_String &name, const EST_Val &d) const { // For when name contains references to sub-features if (strchr(name,'.') == NULL) return val(name, d); else { EST_String nname = name; EST_String fname = nname.before("."); const EST_Val &v = val(fname, d); if (v.type() == val_type_feats) return feats(v)->val_path(nname.after("."), d); else return d; } } EST_String itoString(int n) { char tmp[1000]; sprintf(tmp, "%d", n); return EST_String(tmp); } EST_String ftoString(float n, int pres=3, int width=0, int right_justify=0) { (void)right_justify; EST_String val; char tmp[1000]; char spec[10]; strcpy(spec, "%"); if (width != 0) strcat(spec, itoString(width)); strcat(spec, "."); strcat(spec, itoString(pres)); strcat(spec, "f"); sprintf(tmp, spec, n); val = tmp; return val; } const EST_String &EST_Val::to_str(void) const { // coerce this to and save it for later // This requires the following casting, so we can still tell the // compiler this is a const function. If this was properly declared // non-const vast amounts of the rest of this would also have to be // non-const. So we do one nasty bit here for uniformity elsewhere. // Not saving the result is also a possibility but probably too // inefficient (maybe not with rjc's string class) EST_String *n = (EST_String *)((void *)&sval); if (t==val_int) *n = itoString(v.ival); else if (t==val_float) { if (v.fval == 0) *n = "0"; // to be compatible with other's notion of fstrings else *n = ftoString(v.fval); } else if (t != val_string) *n = EST_String("[Val ")+t+"]"; return sval; } int EST_Features::present(const EST_String &name) const { if (strchr(name,'.') == NULL) return features->present(name); EST_String nname = name; if (features->present(nname.before("."))) { const EST_Val &v = val(nname.before(".")); if (v.type() == val_type_feats) return feats(v)->present(nname.after(".")); else return FALSE; } else return FALSE; } const EST_Val &EST_Features::val_path(const EST_String &name) const { // For when name contains references to sub-features if (strchr(name,'.') == NULL) return val(name); else { EST_String nname = name; EST_String fname = nname.before("."); const EST_Val &v = val(fname); if (v.type() == val_type_feats) return feats(v)->val_path(nname.after(".")); else exit(1);//EST_error("Feature %s not feature valued\n", (const char *)fname); return feature_default_value; // wont get here } } EST_Track::~EST_Track(void) { // clear_features(); } void EST_Track::default_channel_names() { for (int i = 0; i < num_channels(); ++i) set_channel_name("track" + itoString(i), i); } EST_Track::EST_Track() { default_vals(); } int fastlog2(int n) { int num_bits, power = 0; if ((n < 2) || (n % 2 != 0)) return(0); num_bits = sizeof(int) * 8; /* How big are ints on this machine? */ while(power <= num_bits) { n >>= 1; power += 1; if (n & 0x01) { if (n > 1) return(0); else return(power); } } return(0); } #define PI 3.14159265358979323846 static int slowFFTsub(EST_FVector &real, EST_FVector &imag, float f) { // f = -1 for FFT, 1 for IFFT // would be nicer if we used a complex number class, // but we don't, so it isn't // taken from the FORTRAN old chestnut // in various sig proc books // FORTRAN uses 1..n arrays, so subtract 1 all over the place float u_real,u_imag; float w_real,w_imag; float t_real,t_imag; float tmp_real,tmp_imag; int M,N; int i,j,k,l; M = fastlog2(real.n()); N = (int)powf(2,(float)M); if (N != real.n()) { exit(1);//EST_warning("Illegal FFT order %d", real.n()); return -1; } for(l=1;l<=M;l++){ int le = (int)powf(2,(float)(M+1-l)); int le1=le/2; u_real = 1.0; u_imag = 0.0; w_real=cos(PI/le1); w_imag=f * sin(PI/le1); for (j=1;j<=le1;j++) { for (i=j;i<=N-le1;i+=le) { int ip=i+le1; t_real = real.a_no_check(i-1) + real.a_no_check(ip-1); t_imag = imag.a_no_check(i-1) + imag.a_no_check(ip-1); tmp_real = real.a_no_check(i-1) - real.a_no_check(ip-1); tmp_imag = imag.a_no_check(i-1) - imag.a_no_check(ip-1); real.a_no_check(ip-1) = tmp_real*u_real - tmp_imag*u_imag; imag.a_no_check(ip-1) = tmp_real*u_imag + tmp_imag*u_real; real.a_no_check(i-1) = t_real; imag.a_no_check(i-1) = t_imag; } tmp_real = u_real*w_real - u_imag*w_imag; tmp_imag = u_real*w_imag + u_imag*w_real; u_real=tmp_real; u_imag=tmp_imag; } } int NV2=N/2; int NM1=N-1; j=1; for (i=1; i<=NM1;i++) { if (i < j) { t_real=real(j-1); t_imag=imag(j-1); real[j-1] = real(i-1); imag[j-1] = imag(i-1); real[i-1] = t_real; imag[i-1] = t_imag; } k=NV2; while(k < j) { j=j-k; k=k/2; } j=j+k; } return 0; } int slowIFFT(EST_FVector &real, EST_FVector &imag) { int N=real.n(); if (N <=0 ) return -1; if (slowFFTsub(real,imag,1.0) != 0) return -1; for(int i=1;i<=N;i++){ real[i-1] /= (float)N; imag[i-1] /= (float)N; } return 0; } EST_FVector design_FIR_filter(const EST_FVector &frequency_response, int filter_order) { // frequency_response contains the desired filter reponse, // on a scale 0...sampling frequency // check filter_order is odd if((filter_order & 1) == 0){ cerr << "Requested filter order must be odd" << endl; return EST_FVector(0); } // check frequency_response has dimension 2^N int N = fastlog2(frequency_response.n()); if(frequency_response.n() != (int)powf(2,(float)N)){ cerr << "Desired frequency response must have dimension 2^N" << endl; return EST_FVector(0); } int i; EST_FVector filt(frequency_response); EST_FVector dummy(frequency_response.n()); for(i=0;ismooth_double = FALSE; ms->apply_hanning = TRUE; ms->extrapolate = TRUE; ms->first_median = 11; ms->second_median = 1; ms->window_length = 7; ms->breaker = -1.0; return (ms); } int parse_ms_list(EST_Features &al, struct Ms_Op *ms) { default_ms_op(ms); if (al.present("smooth_double")) ms->smooth_double = al.I("smooth_double"); if (al.present( "hanning")) ms->apply_hanning = al.I("hanning"); if (al.present("extrapolate")) ms->extrapolate = al.I("extrapolate"); if (al.present("first_length")) ms->first_median = al.I("first_length"); if (al.present("second_length")) ms->second_median = al.I("second_length"); if (al.present("window_length")) ms->window_length = al.I("window_length"); return 0; } #define MAX_LEN 127 #define TWO_PI 6.28318530717958647698 void mk_window_coeffs (int length, float win_coeff[]) { int i; double x; for (i = 0; i < length; i++) { x = TWO_PI * (i + 1.0) / (length + 1.0); win_coeff[i] = (1.0 - (float) cos (x)) / (length + 1.0); } } float median (int *counter, float valin, float valbuf[], int lmed, int mmed) { int i, j; float tmp, filmed[MAX_LEN]; for (i = lmed - 1; i > 0; i--) valbuf[i] = valbuf[i - 1]; valbuf[0] = valin; if (*counter > 0) { (*counter)--; return (0.0); } else { *counter = -1; for (i = 0; i < lmed; i++) filmed[i] = valbuf[i]; for (j = lmed - 1; j > 0; j--) for (i = 0; i < j; i++) if (filmed[i] > filmed[i + 1]) { tmp = filmed[i + 1]; filmed[i + 1] = filmed[i]; filmed[i] = tmp; } return (filmed[mmed]); } } float hanning (int *counter, float valin, float valhan[], float win_coeff[], struct Ms_Op *par) { int i, j, k = 0; float valout = 0.0, weight[MAX_LEN]; for (i = par->window_length - 1; i > 0; i--) valhan[i] = valhan[i - 1]; valhan[0] = valin; if (*counter > 0) { (*counter)--; return (0.0); } else { *counter = -1; for (i = 0; i < par->window_length; i++) if (valhan[i] == par->breaker) k++; if (!k) for (i = 0; i < par->window_length; i++) valout += valhan[i] * win_coeff[i]; else if (k <= par->window_length / 2 && par->extrapolate) { mk_window_coeffs (par->window_length - k, weight); for (i = 0, j = 0; i < par->window_length; i++) if (valhan[i] != par->breaker) valout += valhan[i] * weight[j++]; } else valout = par->breaker; return (valout); } } void array_smoother (float *p_array, int arraylen, struct Ms_Op *ms) { int i, j, mid1, mid2 = 0, filler, nloops; int C1, C2 = 0, C3 = 0, C4 = 0, c1, c2, c3, c4; int delay, delx = 0, dely = 0; int in = 0, out = 0; float input, output; float *inarray; float xdel[2 * MAX_LEN - 2], ydel[2 * MAX_LEN - 2]; float medbuf1[MAX_LEN], medbuf2[MAX_LEN]; float hanbuf1[MAX_LEN], hanbuf2[MAX_LEN], win_coeffs[MAX_LEN]; float medval1, medval2, hanval1, hanval2, zatn; inarray = new float[arraylen]; for (i = 0; i < arraylen; ++i) inarray[i] = p_array[i]; if (ms == NULL) { ms = new Ms_Op; default_ms_op(ms); } mk_window_coeffs (ms->window_length, win_coeffs); /* determine the size and delay of each stage concerned */ mid1 = ms->first_median / 2; C1 = delay = ms->first_median - 1; if (ms->apply_hanning) { C2 = ms->window_length - 1; delay = ms->first_median + ms->window_length - 2; } if (ms->smooth_double) { mid2 = ms->second_median / 2; C3 = ms->second_median - 1; if (!ms->apply_hanning) { delx = ms->first_median; dely = ms->second_median; } else { C4 = ms->window_length - 1; delx = ms->first_median + ms->window_length - 1; dely = ms->second_median + ms->window_length - 1; } delay = delx + dely - 2; } /* prepare for smoothing */ c1 = C1; c2 = C2; c3 = C3; c4 = C4; if (!ms->extrapolate) { /* pad with breakers at the beginning */ for (i = 0; i < delay / 2; i++) p_array[out++] = ms->breaker; filler = 0; nloops = arraylen; } else { /* extrapolate by initialising filter with dummy breakers */ filler = delay / 2; nloops = arraylen + delay; } /* smooth track element by track element */ for (j = 0; j < nloops; j++) { if (j < filler || j >= nloops - filler) input = ms->breaker; else input = inarray[in++]; /* store input value if double smoothing */ if (ms->smooth_double) { for (i = delx - 1; i > 0; i--) xdel[i] = xdel[i - 1]; xdel[0] = input; } /* first median smoothing */ medval1 = median (&c1, input, medbuf1, ms->first_median, mid1); if (c1 == -1) { output = medval1; /* first hanning window (optional) */ if (ms->apply_hanning) { hanval1 = hanning (&c2, medval1, hanbuf1, win_coeffs, ms); if (c2 == -1) output = hanval1; else continue; } /* procedures for double smoothing (optional) */ if (ms->smooth_double) { /* compute rough component z(n) */ if (output != ms->breaker && xdel[delx - 1] != ms->breaker) zatn = xdel[delx - 1] - output; else zatn = ms->breaker; /* store results of first smoothing */ for (i = dely - 1; i > 0; i--) ydel[i] = ydel[i - 1]; ydel[0] = output; /* second median smoothing */ medval2 = median (&c3, zatn, medbuf2, ms->second_median, mid2); if (c3 == -1) { output = medval2; /* second hanning smoothing (optional) */ if (ms->apply_hanning) { hanval2 = hanning (&c4, medval2, hanbuf2, win_coeffs, ms); if (c4 == -1) output = hanval2; else continue; } if (output != ms->breaker && ydel[dely - 1] != ms->breaker) output += ydel[dely - 1]; else output = ms->breaker; } else continue; } /* write filtered result */ p_array[out++] = output; } } if (!ms->extrapolate) /* pad with breakers at the end */ for (i = 0; i < delay / 2; i++) p_array[out++] = ms->breaker; delete inarray; } void smooth_portion(EST_Track &c, EST_Features &op) { int i; float *a; // need float * so it can be passed to array_smoother struct Ms_Op *ms; ms = new Ms_Op; default_ms_op(ms); parse_ms_list(op, ms); if (op.present("point_window_size")) ms->window_length = op.I("point_window_size"); a = new float[c.num_frames()]; for (i = 0; i < c.num_frames(); ++i) a[i] = c.track_break(i) ? -1.0 : c.a(i); array_smoother(a, c.num_frames(), ms); for (i = 0; i < c.num_frames(); ++i) { // occasionally NaNs result... if (isnanf(a[i])) { c.set_break(i); c.a(i) = 0.0; } else { if (a[i] < 0.0) c.set_break(i); else c.set_value(i); c.a(i) = a[i]; } } delete a; } static void interp(const EST_Track &c, const EST_Track &speech, int fill, EST_Track &interp) { // Interpolate between unvoiced sections, and ensure breaks // during silences int i, n, p; float m; float n_val, p_val; float f = c.shift(); interp = c; // copy track if (speech.num_frames() < c.num_frames()) interp.resize(speech.num_frames(), interp.num_channels()); for (i = 1; i < interp.num_frames(); ++i) { if ((fill == 1) || (speech.a(i) > 0.5)) { if (!interp.track_break(i)) continue; // already has a value p = i - 1; if ((n = interp.next_non_break(i)) == 0) n = interp.num_frames() - 1; n_val = interp.a(n); p_val = interp.a(p); if (n_val <= 0) n_val = p_val; if (p_val <= 0) p_val = n_val; // if they are both zero, well we'll learn to live it. m = (n_val - p_val) / ( interp.t(n) - interp.t(p)); interp.a(i) = (m * f) + p_val; interp.set_value(i); } else interp.set_break(i); } } void smooth_phrase(EST_Track &fz, EST_Track &speech, EST_Features &op, EST_Track &smi_fz) { int n=0; EST_Track sm_fz; char nstring[10]; if (fz.empty()) { smi_fz = fz; return; } sm_fz = fz; sm_fz.set_channel_name("F0", 0); n = (int)(op.F("window_length") / fz.shift()); sprintf(nstring, "%d", n); op.set("point_window_size", nstring); if (!op.present("icda_no_smooth")) smooth_portion(sm_fz, op); if (op.present("icda_no_interp")) { sm_fz = fz; return; // no unvoiced interpolation } int fill = op.present("icda_fi") ? 1 : 0; interp(sm_fz, speech, fill, smi_fz); // fill unvoiced region n = (int)(op.F("second_length") / fz.shift()); sprintf(nstring, "%d", n); op.set("point_window_size", nstring); if (!op.present("icda_no_smooth")) smooth_portion(smi_fz, op); } const int EST_Val::to_int(void) const { // coerce this to an int if (t==val_float) return (int)v.fval; else if (t==val_string) return atoi(sval); else return v.ival; // just for completeness } struct SEGMENT_ { /* segment of speech data */ int size, shift, length; /* in samples */ short *data; }; struct CROSS_CORR_ { int size; double *coeff; }; void end_structure_use(SEGMENT_ *p_seg, CROSS_CORR_ *p_cc) { wfree (p_seg->data); wfree (p_cc->coeff); return; } static bool bounds_check(const EST_Track &t, int f, int c, int set) { const char *what = set? "set" : "access"; if (f<0 || f >= t.num_frames()) { cerr << "Attempt to " << what << " frame " << f << " of " << t.num_frames() << " frame track\n"; return FALSE; } if (c<0 || c >= t.num_channels()) { cerr << "Attempt to " << what << " channel " << c << " of " << t.num_channels() << " channel track\n"; return FALSE; } return TRUE; } float &EST_Track::a(int i, int c) { if (!bounds_check(*this, i,c,0)) return *(p_values.error_return); return p_values.a_no_check(i,c); } EST_ChunkPtr chunk_allocate(int bytes, const char *initial, int initial_len) { if (initial_len >= bytes) { cerr<<"initialiser too long\n"; abort(); } EST_Chunk *cp = new(bytes) EST_Chunk; memcpy(cp->memory, initial, initial_len); cp->memory[initial_len] = '\0'; return (EST_ChunkPtr)cp; } void wfree(void *p) { if (p != NULL) free(p); } EST_Features::EST_Features() { features = new EST_TKVL; } EST_Features::~EST_Features() { if (features != NULL) { delete features; features=NULL; } } const EST_Val &EST_Features::val(const char *name) const { // Because so many access are from char* literals we all access // directly rather than requiring the creation of an EST_String EST_Litem *p; for (p=features->list.head(); p; p=next(p)) { if (features->list(p).k == name) return features->list(p).v; } exit(1);//EST_error("{FND} Feature %s not defined\n", name); return feature_default_value; } EST_String EST_String::chop_internal(const char *it, int len, int from, EST_chop_direction mode) const { CHECK_STRING_ARG(it); int start, end; if (it && locate(it, len, from, start, end)) switch (mode) { case Chop_Before: return EST_String(str(), size, 0, start); break; case Chop_At: return EST_String(str(), size, start, end-start); break; case Chop_After: return EST_String(str(), size, end, -1); } return EST_String(); } EST_Val::EST_Val(val_type type,void *p, void (*f)(void *)) { t=type; v.pval = new EST_Contents; v.pval->set_contents(p,f); } EST_Features::EST_Features(const EST_Features &f) { features = new EST_TKVL; *features = *f.features; } void EST_TokenStream::close(void) { // close any files (if they were used) switch (type) { case tst_none: break; case tst_file: if (close_at_end) fclose(fp); case tst_pipe: // close(fd); break; case tst_istream: break; case tst_string: delete [] buffer; buffer = 0; break; default: cerr << "EST_TokenStream: unknown type" << endl; break; } type = tst_none; peeked_charp = FALSE; peeked_tokp = FALSE; } EST_TokenStream::~EST_TokenStream() { if (type != tst_none) close(); delete [] tok_wspace; delete [] tok_stuff; delete [] tok_prepuncs; } int EST_TokenStream::open(const EST_String &filename) { if (type != tst_none) close(); default_values(); fp = fopen(filename,"rb"); if (fp == NULL) { exit(1);//cerr << "Cannot open file " << filename << " as tokenstream" // << endl; return -1; } Origin = filename; type = tst_file; return 0; } const EST_String Token_Origin_FD = "existing file descriptor"; int EST_TokenStream::open(FILE *ofp, int close_when_finished) { // absorb already open stream if (type != tst_none) close(); default_values(); fp = ofp; if (fp == NULL) { cerr << "Cannot absorb NULL filestream as tokenstream" << endl; return -1; } Origin = Token_Origin_FD; type = tst_file; close_at_end = close_when_finished; return 0; } EST_TokenStream::EST_TokenStream() { tok_wspacelen = 64; // will grow if necessary tok_wspace = new char[tok_wspacelen]; tok_stufflen = 512; // will grow if necessary tok_stuff = new char[tok_stufflen]; tok_prepuncslen = 32; // will grow if necessary tok_prepuncs = new char[tok_prepuncslen]; default_values(); } #define Instantiate_KVL_T(KEY, VAL, TAG) \ template class EST_TKVL; \ template class EST_TKVI; \ ostream &operator<<(ostream &s, EST_TKVI< KEY , VAL > const &i){ return s << i.k << "\t" << i.v << "\n"; } \ ostream& operator << (ostream& s, EST_TKVL< KEY , VAL > const &l) {EST_Litem *p; for (p = l.list.head(); p ; p = next(p)) s << l.list(p).k << "\t" << l.list(p).v << endl; return s;} \ Instantiate_TIterator_T(KVL_ ## TAG ## _t, KVL_ ## TAG ## _t::IPointer_k, KEY, KVL_ ## TAG ##_kitt) \ Instantiate_TStructIterator_T(KVL_ ## TAG ## _t, KVL_ ## TAG ## _t::IPointer, KVI_ ## TAG ## _t, KVL_ ## TAG ##_itt) \ Instantiate_TIterator_T(KVL_ ## TAG ## _t, KVL_ ## TAG ## _t::IPointer, KVI_ ## TAG ## _t, KVL_ ## TAG ##_itt) \ Instantiate_TList(KVI_ ## TAG ## _t) // template ostream & operator<<(ostream &s, EST_TKVI const &i); #define Instantiate_KVL(KEY, VAL) \ Instantiate_KVL_T(KEY, VAL, KEY ## VAL) #define Declare_KVL_TN(KEY, VAL, MaxFree, TAG) \ typedef EST_TKVI KVI_ ## TAG ## _t; \ typedef EST_TKVL KVL_ ## TAG ## _t; \ \ static VAL TAG##_kv_def_val_s; \ static KEY TAG##_kv_def_key_s; \ \ template <> VAL *EST_TKVL< KEY, VAL >::default_val=&TAG##_kv_def_val_s; \ template <> KEY *EST_TKVL< KEY, VAL >::default_key=&TAG##_kv_def_key_s; \ \ Declare_TList_N(KVI_ ## TAG ## _t, MaxFree) #define Declare_KVL_T(KEY, VAL, TAG) \ Declare_KVL_TN(KEY, VAL, 0, TAG) #define Declare_KVL_Base_TN(KEY, VAL, DEFV, DEFK, MaxFree, TAG) \ typedef EST_TKVI KVI_ ## TAG ## _t; \ typedef EST_TKVL KVL_ ## TAG ## _t; \ \ static VAL TAG##_kv_def_val_s=DEFV; \ static KEY TAG##_kv_def_key_s=DEFK; \ \ template <> VAL *EST_TKVL< KEY, VAL >::default_val=&TAG##_kv_def_val_s; \ template <> KEY *EST_TKVL< KEY, VAL >::default_key=&TAG##_kv_def_key_s; \ \ Declare_TList_N(KVI_ ## TAG ## _t, MaxFree) #define Declare_KVL_Base_T(KEY, VAL, DEFV, DEFK, TAG) \ Declare_KVL_Base_TN(KEY, VAL, DEFV, DEFK, 0, TAG) #define Declare_KVL_Class_TN(KEY, VAL, DEFV, DEFK, MaxFree, TAG) \ typedef EST_TKVI KVI_ ## TAG ## _t; \ typedef EST_TKVL KVL_ ## TAG ## _t; \ \ static VAL TAG##_kv_def_val_s(DEFV); \ static KEY TAG##_kv_def_key_s(DEFK); \ \ template <> VAL *EST_TKVL< KEY, VAL >::default_val=&TAG##_kv_def_val_s; \ template <> KEY *EST_TKVL< KEY, VAL >::default_key=&TAG##_kv_def_key_s; \ \ Declare_TList_N(KVI_ ## TAG ## _t, MaxFree) #define Declare_KVL_Class_T(KEY, VAL, DEFV, DEFK,TAG) \ Declare_KVL_Class_TN(KEY, VAL, DEFV, DEFK, 0, TAG) #define Declare_KVL_N(KEY, VAL, MaxFree) \ Declare_KVL_TN(KEY, VAL, MaxFree, KEY ## VAL) #define Declare_KVL(KEY, VAL) \ Declare_KVL_N(KEY, VAL, 0) #define Declare_KVL_Base_N(KEY, VAL, DEFV, DEFK, MaxFree) \ Declare_KVL_Base_TN(KEY, VAL, DEFV, DEFK, , MaxFree, KEY ## VAL) #define Declare_KVL_Base(KEY, VAL, DEFV, DEFK) \ Declare_KVL_Base_N(KEY, VAL, DEFV, DEFK, 0) #define Declare_KVL_Class_N(KEY, VAL, DEFV, DEFK, MaxFree) \ Declare_KVL_Class_TN(KEY, VAL, DEFV, DEFK, MaxFree, KEY ## VAL) #define Declare_KVL_Class(KEY, VAL, DEFV, DEFK) \ Declare_KVL_Class_N(KEY, VAL, DEFV, DEFK, 0) Declare_KVL(EST_String,EST_String) template EST_Litem *EST_TKVL::find_pair_key(const K &key) const { EST_Litem *ptr; for (ptr = list.head(); ptr != 0; ptr= next(ptr)) if (list.item(ptr).k == key) return ptr; return 0; } // look for key rkey in list. If found, change its value to rval and // return true, otherwise return false. template int EST_TKVL::change_val(const K &rkey,const V &rval) { EST_Litem *ptr=find_pair_key(rkey); if (ptr == 0) return 0; else { list.item(ptr).v = rval; return 1; } } template const V &EST_TKVL::val_def(const K &rkey, const V &def) const { EST_Litem *ptr = find_pair_key(rkey); if (ptr == 0) return def; else return list.item(ptr).v; } void make_updatable(EST_ChunkPtr &cp) { if (cp.ptr && cp.ptr->count > 1) { EST_Chunk *newchunk = new(cp.ptr->size) EST_Chunk; memcpy(newchunk->memory, cp.ptr->memory, cp.ptr->size); cp = newchunk; } } const EST_Val &EST_Features::val(const char *name, const EST_Val &def) const { // Because so many access are from char* literals we all access // directly rather than requiring the creation of an EST_String EST_Litem *p; for (p=features->list.head(); p; p=next(p)) { if (features->list(p).k == name) return features->list(p).v; } return def; } EST_String operator + (const EST_String &a, const char *b) { CHECK_STRING_ARG(b); int al = a.size; int bl = safe_strlen(b); if (al == 0) return EST_String(b, 0, bl); if (bl == 0) return EST_String(a); EST_ChunkPtr c = chunk_allocate(al+bl+1, a.str(), al); if (bl>0) memmove((char *)c + al, b, bl); c(al+bl)='\0'; return EST_String(al+bl, c); } EST_Val EST_Features::feature_default_value("0"); EST_Featured::~EST_Featured(void) { clear_features(); } template EST_TMatrix::~EST_TMatrix() { p_num_rows = 0; p_row_step=0; } template EST_TVector::~EST_TVector() { p_num_columns = 0; p_offset=0; p_column_step=0; if (p_memory != NULL && !p_sub_matrix) { delete [] (p_memory-p_offset); p_memory = NULL; } } void EST_Track::set_channel_name(const EST_String &fn, int i) { p_channel_names[i] = fn; } EST_String operator + (const char *a, const EST_String &b) { CHECK_STRING_ARG(a); int al = safe_strlen(a); int bl = b.size; if (bl == 0) return EST_String(a, 0, al); if (al == 0) return EST_String(b); EST_ChunkPtr c = chunk_allocate(al+bl+1, a, al); memmove((char *)c + al, b.str(), bl); c(al+bl)='\0'; return EST_String(al+bl, c); } void EST_Track::default_vals(void) { p_equal_space = FALSE; p_single_break = FALSE; p_values.resize(0, 0); p_times.resize(0); p_is_val.resize(0); p_aux.resize(0, 0); p_aux_names.resize(0); p_channel_names.resize(0); p_map = NULL; p_t_offset=0; init_features(); } template EST_TVector::EST_TVector() { default_vals(); } template EST_TMatrix::EST_TMatrix() { default_vals(); } EST_Featured::EST_Featured(void) { init_features(); } template EST_TSimpleVector::EST_TSimpleVector(const EST_TSimpleVector &in) { this->default_vals(); copy(in); } void *safe_walloc(int size) { char *p; if (size == 0) /* Some mallocs return NULL for size 0, which means you can't tell if it failed or not. So we'll avoid that problem by never asking for 0 bytes */ p = (char*)calloc(1,1); else p = (char*)calloc(size,1); if (p == NULL) { fprintf(stderr,"WALLOC: failed to malloc %d bytes\n",size); exit(-1); /* I'd rather not do this but this is the only safe */ /* thing to do */ } return p; } /* return the lesser of the two values */ #define Lof(a, b) (((a) < (b)) ? (a) : (b)) template void EST_TSimpleMatrix::resize(int new_rows, int new_cols, int set) { T* old_vals=NULL; int old_offset = this->p_offset; if (new_rows<0) new_rows = this->num_rows(); if (new_cols<0) new_cols = this->num_columns(); if (set) { if (!this->p_sub_matrix && new_cols == this->num_columns() && new_rows != this->num_rows()) { int copy_r = Lof(this->num_rows(), new_rows); just_resize(new_rows, new_cols, &old_vals); memcpy((void *)this->p_memory, (const void *)old_vals, copy_r*new_cols*sizeof(T)); int i,j; if (new_rows > copy_r) if (*this->def_val == 0) { memset((void *)(this->p_memory + copy_r*this->p_row_step), 0, (new_rows-copy_r)*new_cols*sizeof(T)); } else { for(j=0; ja_no_check(i,j) = *this->def_val; } } else if (!this->p_sub_matrix) { int old_row_step = this->p_row_step; int old_column_step = this->p_column_step; int copy_r = Lof(this->num_rows(), new_rows); int copy_c = Lof(this->num_columns(), new_cols); just_resize(new_rows, new_cols, &old_vals); set_values(old_vals, old_row_step, old_column_step, 0, copy_r, 0, copy_c); int i,j; for(i=0; ia_no_check(i,j) = *this->def_val; if (new_rows > copy_r) if (*this->def_val == 0) { memset((void *)(this->p_memory + copy_r*this->p_row_step), 0, (new_rows-copy_r)*new_cols*sizeof(T)); } else { for(j=0; ja_no_check(i,j) = *this->def_val; } } else EST_TMatrix::resize(new_rows, new_cols, 1); } else EST_TMatrix::resize(new_rows, new_cols, 0); if (old_vals && old_vals != this->p_memory) delete [] (old_vals-old_offset); } void EST_Track::set_value(int i) // make location i hold a value { p_is_val[i] = 0; } void EST_Track::set_break(int i) // make location i hold a break { if (i >= num_frames()) cerr << "Requested setting of break value of the end of the array\n"; p_is_val[i] = 1; } float EST_Track::shift() const { int j1 = 0; int j2 = 0; if (!p_equal_space) exit(1);//EST_error("Tried to take shift from non-fixed contour\n"); do { j1 = next_non_break(++j1); j2 = next_non_break(j1); // cout << "j1:" << j1 << " j2:" << j2 << endl; } while ((j2 != 0) && (j2 != (j1 +1))); if (j2 == 0) { if (num_frames() > 1) return p_times(1) - p_times(0); else exit(1);//EST_error("Couldn't determine shift size\n"); } return (p_times(j2) - p_times(j1)); } EST_Track &EST_Track::operator=(const EST_Track& a) { copy(a); return *this; } int EST_Track::empty() const { int i, num; for (i = num = 0; i < num_frames(); ++i) if (val(i)) return 0; // i.e. false return 1; // i.e. true } const float EST_Val::to_flt(void) const { // coerce this to a float if (t==val_int) return (float)v.ival; else if (t==val_string) return atof(sval); else return v.fval; // just for completeness } int EST_Track::next_non_break(int j) const { int i = j; for (++i; i < num_frames(); ++i) { // cout << "i: " << i << " " << value[i] << endl; if (!track_break(i)) return i; } return 0; } float EST_Track::a(int i, int c) const { return ((EST_Track *)this)->a(i,c); } void EST_Track::resize(int new_num_frames, int new_num_channels, bool set) { int old_num_frames = num_frames(); if (new_num_frames<0) new_num_frames = num_frames(); if (new_num_channels<0) new_num_channels = num_channels(); p_channel_names.resize(new_num_channels); // this ensures the new channels have a default name if (new_num_channels > num_channels()) for (int i = num_channels(); i < new_num_channels; ++i) set_channel_name("track_" + itoString(i), i); p_values.resize(new_num_frames, new_num_channels, set); p_times.resize(new_num_frames, set); p_is_val.resize(new_num_frames, set); p_aux.resize(new_num_frames, num_aux_channels(), set); // Its important that any new vals get set to 0 for (int i = old_num_frames; i < num_frames(); ++i) p_is_val.a_no_check(i) = 0; } Declare_TVector(float) EST_Chunk::EST_Chunk () { count = 0; memory[0] = '\0'; // cerr<<"created " << hex << (int)&memory << "," << dec << size <<"\n"; } void *EST_Chunk::operator new (size_t size, int bytes) { if (bytes > MAX_CHUNK_SIZE) { cerr<<"trying to make chunk of size "< size = bytes; return it; } EST_String::EST_String(const char *s, int s_size, int start, int len) { CHECK_STRING_ARG(s); if (len <0) len=s_size-start; size=len; if (size != 0) memory = chunk_allocate(len+1, s+start, len); else memory=NULL; } int EST_String::locate(const char *s, int len, int from, int &start, int &end) const { CHECK_STRING_ARG(s); const char *sub=NULL; if (!s) return 0; if (from < 0 && -from < size) { int endpos=size+from+1; int p=0; const char *nextsub; while ((nextsub=strstr(str()+p, s))) { p=nextsub-str()+1; if (p > endpos) break; sub=nextsub; } } else if (from>=0 && from <= size) sub= strstr(str()+from, s); if (sub != NULL) { start = sub-str(); end = start + len; return 1; } else { return 0; } } Declare_KVL_N(EST_String, EST_Val, 100) template EST_TKVL &EST_TKVL::operator = (const EST_TKVL &kv) { list = kv.list; return *this; } #define SWAPINT(x) ((((unsigned)x) & 0xff) << 24 | \ (((unsigned)x) & 0xff00) << 8 | \ (((unsigned)x) & 0xff0000) >> 8 | \ (((unsigned)x) & 0xff000000) >> 24) #define SWAPSHORT(x) ((((unsigned)x) & 0xff) << 8 | \ (((unsigned)x) & 0xff00) >> 8) #define WAVE_FORMAT_PCM 0x0001 #define WAVE_FORMAT_ADPCM 0x0002 #define WAVE_FORMAT_ALAW 0x0006 #define WAVE_FORMAT_MULAW 0x0007 void swap_bytes_short(short *data, int length) { /* Swap shorts in an array */ int i; for (i=0; i INFO &EST_TValuedEnumI::info (ENUM token) const { int i; for(i=0; indefinitions; i++) if (this->definitions[i].token == token) return this->definitions[i].info; exit(1);//cerr << "Fetching info for invalid entry\n"; abort(); static INFO dummyI; return dummyI; } template ENUM EST_TValuedEnumI::token (VAL value) const { int i,j; for(i=0; indefinitions; i++) for(j=0; jdefinitions[i].values[j] ; j++) if (eq_vals(this->definitions[i].values[j], value)) return this->definitions[i].token; return this->p_unknown_enum; } void EST_TokenStream::default_values() { type = tst_none; peeked_tokp = FALSE; peeked_charp = FALSE; eof_flag = FALSE; quotes = FALSE; p_filepos = 0; linepos = 1; WhiteSpaceChars = EST_Token_Default_WhiteSpaceChars; SingleCharSymbols = EST_String::Empty; PrePunctuationSymbols = EST_String::Empty; PunctuationSymbols = EST_String::Empty; build_table(); close_at_end=TRUE; } template VAL EST_TValuedEnumI::value (ENUM token, int n) const { int i; for(i=0; indefinitions; i++) if (this->definitions[i].token == token) return this->definitions[i].values[n]; return this->p_unknown_value; } int EST_TokenStream::seek(int position) { peeked_charp = FALSE; peeked_tokp = FALSE; switch (type) { case tst_none: exit(1);//cerr << "EST_TokenStream unset" << endl; return -1; break; case tst_file: p_filepos = position; return fseek(fp,position,SEEK_SET); case tst_pipe: exit(1);//cerr << "EST_TokenStream seek on pipe not supported" << endl; return -1; break; case tst_istream: exit(1);//cerr << "EST_TokenStream seek on istream not yet supported" << endl; return -1; break; case tst_string: if (position >= pos) { pos = position; return -1; } else { pos = position; return 0; } break; default: exit(1);//cerr << "EST_TokenStream: unknown type" << endl; return -1; } return -1; // can't get here } template int EST_TValuedEnumI::n(void) const { return this->ndefinitions; } EST_String::EST_String(const char *s, int start_or_fill, int len) { if (s) { int start= start_or_fill; if (len <0) len=safe_strlen(s)-start; size=len; if (size != 0) memory = chunk_allocate(len+1, s+start, len); else memory=NULL; } else { char fill = start_or_fill; if (len<0) len=0; size=len; if (size != 0) { memory = chunk_allocate(len+1); char *p = memory; for(int j=0; j void EST_TVector::resize(int new_cols, int set) { int i; T * old_vals = p_memory; int old_cols = num_columns(); int old_offset = p_offset; int old_column_step = p_column_step; just_resize(new_cols, &old_vals); if (set) { int copy_c = 0; if (!old_vals) copy_c=0; else if (old_vals != p_memory) { copy_c = Lof(num_columns(), old_cols); for(i=0; i void EST_TMatrix::resize(int new_rows, int new_cols, int set) { int i,j; T * old_vals = this->p_memory; int old_rows = num_rows(); int old_cols = num_columns(); int old_row_step = p_row_step; int old_offset = this->p_offset; int old_column_step = this->p_column_step; if (new_rows<0) new_rows = old_rows; if (new_cols<0) new_cols = old_cols; just_resize(new_rows, new_cols, &old_vals); if (set) { int copy_r = 0; int copy_c = 0; if (old_vals != NULL) { copy_r = Lof(num_rows(), old_rows); copy_c = Lof(num_columns(), old_cols); set_values(old_vals, old_row_step, old_column_step, 0, copy_r, 0, copy_c); } else { copy_r = old_rows; copy_c = old_cols; } for(i=0; idef_val; for(i=copy_r; idef_val; } if (old_vals && old_vals != this->p_memory && !this->p_sub_matrix) delete [] (old_vals-old_offset); } // should copy from and delete old version first template void EST_TSimpleVector::resize(int newn, int set) { int oldn = this->n(); T *old_vals =NULL; int old_offset = this->p_offset; just_resize(newn, &old_vals); if (set && old_vals) { int copy_c = 0; if (this->p_memory != NULL) { copy_c = Lof(this->n(), oldn); memcpy((void *)this->p_memory, (const void *)old_vals, copy_c* sizeof(T)); } for (int i=copy_c; i < this->n(); ++i) this->p_memory[i] = *this->def_val; } if (old_vals != NULL && old_vals != this->p_memory && !this->p_sub_matrix) delete [] (old_vals - old_offset); } void EST_Track::copy(const EST_Track& a) { copy_setup(a); p_values = a.p_values; p_times = a.p_times; p_is_val = a.p_is_val; p_t_offset = a.p_t_offset; p_aux = a.p_aux; p_aux_names = a.p_aux_names; } int EST_Track::val(int i) const { return !p_is_val(i); } void uchar_to_short(const unsigned char *chars,short *data,int length) { /* Convert 8 bit data to shorts UNSIGNED CHAR */ int i; for (i=0; i> 4 ) & 0x07; mantissa = ulawbyte & 0x0F; sample = exp_lut[exponent] + ( mantissa << ( exponent + 3 ) ); if ( sign != 0 ) sample = -sample; return sample; } void ulaw_to_short(const unsigned char *ulaw,short *data,int length) { /* Convert ulaw to shorts */ int i; for (i=0; i void EST_TMatrix::set_memory(T *buffer, int offset, int rows, int columns, int free_when_destroyed) { EST_TVector::set_memory(buffer, offset, columns, free_when_destroyed); p_num_rows = rows; p_row_step = columns; } void EST_TokenStream::build_table() { int i; const char *p; unsigned char c; for (i=0; i<256; ++i) p_table[i]=0; for (p=WhiteSpaceChars; *p; ++p) if (p_table[c=(unsigned char)*p]) exit(1);//EST_warning("Character '%c' has two classes, '%c' and '%c'", // *p, c, ' '); else p_table[c] = ' '; for (p=SingleCharSymbols; *p; ++p) if (p_table[c=(unsigned char)*p]) exit(1);//EST_warning("Character '%c' has two classes, '%c' and '%c'", // *p, p_table[c], '!'); else p_table[c] = '@'; for (p=PunctuationSymbols; *p; ++p) if (p_table[c=(unsigned char)*p] == '@') continue; else if (p_table[c]) exit(1);//EST_warning("Character '%c' has two classes, '%c' and '%c'", // *p, p_table[c], '.'); else p_table[c] = '.'; for(p=PrePunctuationSymbols; *p; ++p) if (p_table[c=(unsigned char)*p] == '@') continue; else if (p_table[c] == '.') p_table[c] = '"'; else if (p_table[c]) exit(1);//EST_warning("Character '%c' has two classes, '%c' and '%c'", // *p, p_table[c], '$'); else p_table[c] = '$'; p_table_wrong=0; } const EST_String EST_String::Empty(""); const EST_String EST_Token_Default_WhiteSpaceChars = " \t\n\r"; EST_ChunkPtr chunk_allocate(int bytes) { EST_Chunk *cp = new(bytes) EST_Chunk; return (EST_ChunkPtr)cp; } template EST_TVector &EST_TVector::operator=(const EST_TVector &in) { copy(in); return *this; } template EST_TMatrix &EST_TMatrix::operator=(const EST_TMatrix &in) { copy(in); return *this; } template EST_TSimpleVector &EST_TSimpleVector::operator=(const EST_TSimpleVector &in) { copy(in); return *this; } void EST_Track::copy_setup(const EST_Track& a) { p_equal_space = a.p_equal_space; p_single_break = a.p_single_break; p_channel_names = a.p_channel_names; p_map = a.p_map; copy_features(a); } template EST_TSimpleMatrix &EST_TSimpleMatrix::operator=(const EST_TSimpleMatrix &in) { copy(in); return *this; } template void EST_TSimpleMatrix::copy(const EST_TSimpleMatrix &a) { if (this->num_rows() != a.num_rows() || this->num_columns() != a.num_columns()) resize(a.num_rows(), a.num_columns(), 0); copy_data(a); } void EST_Featured::copy_features(const EST_Featured &f) { clear_features(); if (f.p_features) p_features = new EST_Features(*(f.p_features)); } template EST_TVector::EST_TVector(int n) { default_vals(); resize(n); } void EST_UList::clear_and_free(void (*item_free)(EST_UItem *p)) { EST_UItem *p, *np; for (p=head(); p != 0; p = np) { np=next(p); if (item_free) item_free(p); else delete p; } h = t = 0; } template void EST_TList::free_item(EST_UItem *item) { EST_TItem::release((EST_TItem *)item); } void EST_UList::append(EST_UItem *new_item) { if (new_item == 0) return; new_item->n = 0; new_item->p = t; if (t == 0) h = new_item; else t->n = new_item; t = new_item; } template EST_TItem *EST_TItem::make(const T &val) { EST_TItem *it=NULL; if (s_free!=NULL) { void *mem = s_free; s_free=(EST_TItem *)s_free->n; s_nfree--; // Create an item in the retrieved memory. it=new (mem) EST_TItem(val); } else it = new EST_TItem(val); return it; } template ENUM EST_TValuedEnumI::nth_token (int n) const { if (n>=0 && n < this->ndefinitions) return this->definitions[n].token; return this->p_unknown_enum; } EST_TrackMap::~EST_TrackMap() { } template void EST_TVector::default_vals() { p_num_columns = 0; p_offset=0; p_column_step=0; p_memory = NULL; p_sub_matrix=FALSE; } template void EST_TMatrix::default_vals() { EST_TVector::default_vals(); p_num_rows = 0; p_row_step=0; } template void EST_TMatrix::set_values(const T *data, int r_step, int c_step, int start_r, int num_r, int start_c, int num_c ) { for(int r=start_r, i=0, rp=0; i< num_r; i++, r++, rp+=r_step) for(int c=start_c, j=0, cp=0; j< num_c; j++, c++, cp+=c_step) a_no_check(r,c) = data[rp+cp]; } template void EST_TMatrix::just_resize(int new_rows, int new_cols, T** old_vals) { T *new_m; if (num_rows() != new_rows || num_columns() != new_cols || this->p_memory == NULL ) { if (this->p_sub_matrix) exit(1);//EST_error("Attempt to resize Sub-Matrix"); if (new_cols < 0 || new_rows < 0) exit(1);//EST_error("Attempt to resize matrix to negative size: %d x %d", // new_rows, // new_cols); new_m = new T[new_rows*new_cols]; if (this->p_memory != NULL) if (old_vals != NULL) *old_vals = this->p_memory; else if (!this->p_sub_matrix) delete [] (this->p_memory-this->p_offset); p_num_rows = new_rows; this->p_num_columns = new_cols; this->p_offset=0; p_row_step=this->p_num_columns; this->p_column_step=1; this->p_memory = new_m; } else *old_vals = this->p_memory; } template void EST_TSimpleVector::copy(const EST_TSimpleVector &a) { if (this->p_column_step==1 && a.p_column_step==1) { resize(a.n(), FALSE); memcpy((void *)(this->p_memory), (const void *)(a.p_memory), this->n() * sizeof(T)); } else ((EST_TVector *)this)->copy(a); } template void EST_TVector::just_resize(int new_cols, T** old_vals) { T *new_m; if (num_columns() != new_cols || p_memory == NULL ) { if (p_sub_matrix) exit(1);//EST_error("Attempt to resize Sub-Vector"); if (new_cols < 0) exit(1);//EST_error("Attempt to resize vector to negative size: %d", // new_cols); new_m = new T[new_cols]; if (p_memory != NULL) if (old_vals != NULL) *old_vals = p_memory; else if (!p_sub_matrix) delete [] (p_memory-p_offset); p_memory = new_m; //cout << "vr: mem: " << p_memory << " (" << (int)p_memory << ")\n"; p_offset=0; p_num_columns = new_cols; p_column_step=1; } else *old_vals = p_memory; } template void EST_TVector::set_memory(T *buffer, int offset, int columns, int free_when_destroyed) { if (p_memory != NULL && !p_sub_matrix) delete [] (p_memory-p_offset); p_memory = buffer-offset; p_offset=offset; p_num_columns = columns; p_column_step=1; p_sub_matrix = !free_when_destroyed; } template void EST_TSimpleMatrix::copy_data(const EST_TSimpleMatrix &a) { if (!a.p_sub_matrix && !this->p_sub_matrix) memcpy((void *)&this->a_no_check(0,0), (const void *)&a.a_no_check(0,0), this->num_rows()*this->num_columns()*sizeof(T) ); else { for (int i = 0; i < this->num_rows(); ++i) for (int j = 0; j < this->num_columns(); ++j) this->a_no_check(i,j) = a.a_no_check(i,j); } } Declare_TVector(short) template void EST_TItem::release(EST_TItem *it) { if (s_nfree < s_maxFree) { // Destroy the value in case it holds resources. it->EST_TItem::~EST_TItem(); // I suppose it's a bit weird to use 'n' after calling the destructor. it->n=s_free; s_free=it; s_nfree++; } else delete it; } EST_Val &EST_Val::operator=(const EST_Val &c) { // Have to be careful with the case where they are different types if ((t != val_int) && (t != val_float) && (t != val_unset) && (t != val_string)) delete v.pval; if (c.t == val_string) sval = c.sval; else if (c.t == val_int) v.ival = c.v.ival; else if (c.t == val_float) v.fval = c.v.fval; else if (c.t != val_unset) { // does references not a real copy v.pval = new EST_Contents; *v.pval = *c.v.pval; } t=c.t; return *this; } template EST_TList &EST_TList::operator=(const EST_TList &a) { clear(); // clear out all current items in list. copy_items(a); return *this; } int operator == (const EST_String &a, const EST_String &b) { if (a.size==0) return b.size == 0; else if (b.size == 0) return 0; else return a.size == b.size && a(0) == b(0) && memcmp(a.str(),b.str(),a.size)==0; }; Declare_TVector(char) Declare_TVector(EST_Val) Declare_TVector(EST_String) template void EST_TMatrix::copy(const EST_TMatrix &a) { resize(a.num_rows(), a.num_columns(), 0); copy_data(a); } template void EST_TVector::copy(const EST_TVector &a) { resize(a.n(), FALSE); copy_data(a); } template void EST_TList::copy_items(const EST_TList &l) { EST_UItem *p; for (p = l.head(); p; p = next(p)) append(l.item(p)); } template void EST_TMatrix::copy_data(const EST_TMatrix &a) { set_values(a.p_memory, a.p_row_step, a.p_column_step, 0, a.num_rows(), 0, a.num_columns()); } template void EST_TVector::copy_data(const EST_TVector &a) { set_values(a.p_memory, a.p_column_step, 0, num_columns()); } EST_Val::EST_Val(const EST_Val &c) { if (c.t == val_string) sval = c.sval; else if (c.t == val_int) v.ival = c.v.ival; else if (c.t == val_float) v.fval = c.v.fval; else if (c.t != val_unset) { // does references not a real copy v.pval = new EST_Contents; *v.pval = *c.v.pval; } t=c.t; } template void EST_TVector::set_values(const T *data, int step, int start_c, int num_c) { for(int i=0, c=start_c, p=0; i void EST_TValuedEnumI::initialise(const void *vdefs) { int n=0; typedef EST_TValuedEnumDefinition defn; const defn *defs = (const defn *)vdefs; for(n=1; defs[n].token != defs[0].token; n++) ; this->ndefinitions = n; this->definitions = new defn[n]; this->definitions[0] = defs[0]; for(n=1; defs[n].token != defs[0].token; n++) this->definitions[n] = defs[n]; this->p_unknown_enum = defs[n].token; this->p_unknown_value = defs[n].values[0]; } template EST_TValuedEnumI::~EST_TValuedEnumI(void) { if (this->definitions) delete[] this->definitions; } struct Srpd_Op { int sample_freq; /* Hz */ int Nmax, Nmin; double shift, length; /* ms */ double min_pitch; /* Hz */ double max_pitch; /* Hz */ int L; /* Decimation factor (samples) */ double Tmin, Tmax_ratio, Thigh, Tdh; int Tsilent; int make_ascii; int peak_tracking; }; #define MINARG 5 #define BREAK_NUMBER 0.0 #define DEFAULT_DECIMATION 4 /* samples */ #define DEFAULT_MIN_PITCH 60.0 /* Hz */ #define DEFAULT_MAX_PITCH 600.0 /* Hz */ #define DEFAULT_SF 20000 /* Hz. Sampling Frequency */ #define DEFAULT_SHIFT 5.0 /* ms */ #define DEFAULT_LENGTH 10.0 /* ms */ #define DEFAULT_TSILENT 120 /* max. abs sample amplitude of noise */ #define DEFAULT_TMIN 0.75 #define DEFAULT_TMAX_RATIO 0.85 #define DEFAULT_THIGH 0.88 #define DEFAULT_TDH 0.77 enum Voice { UNVOICED = 0, VOICED = 1, SILENT = 2, }; enum Hold { HOLD = 1, HELD = 1, SEND = 2, SENT = 2, }; static struct Srpd_Op *default_srpd_op(struct Srpd_Op *srpd) { srpd->L = DEFAULT_DECIMATION; srpd->min_pitch = DEFAULT_MIN_PITCH; srpd->max_pitch = DEFAULT_MAX_PITCH; srpd->shift = DEFAULT_SHIFT; srpd->length = DEFAULT_LENGTH; srpd->Tsilent = DEFAULT_TSILENT; srpd->Tmin = DEFAULT_TMIN; srpd->Tmax_ratio = DEFAULT_TMAX_RATIO; srpd->Thigh = DEFAULT_THIGH; srpd->Tdh = DEFAULT_TDH; srpd->make_ascii = 0; srpd->peak_tracking = 0; srpd->sample_freq = DEFAULT_SF; /* p_par->Nmax and p_par->Nmin cannot be initialised */ return(srpd); } struct STATUS_ { double pitch_freq; Voice v_uv; Hold s_h; double cc_max, threshold; }; #define rint(N) ((float)(int)((N)+0.5)) void initialise_structures (struct Srpd_Op *p_par, SEGMENT_ *p_seg, CROSS_CORR_ *p_cc) { p_par->Nmax = (int) ceil((float)p_par->sample_freq / p_par->min_pitch); p_par->Nmin = (int) floor((float)p_par->sample_freq / p_par->max_pitch); p_par->min_pitch = (float)p_par->sample_freq / (float)p_par->Nmax; p_par->max_pitch = (float)p_par->sample_freq / (float)p_par->Nmin; p_seg->size = 3 * p_par->Nmax; p_seg->shift = (int) rint( p_par->shift / 1000.0 * (float)p_par->sample_freq ); p_seg->length = (int) rint( p_par->length / 1000.0 * (float)p_par->sample_freq ); p_seg->data = walloc(short,p_seg->size); p_cc->size = p_par->Nmax - p_par->Nmin + 1; p_cc->coeff = walloc(double,p_cc->size); } void initialise_status (const Srpd_Op ¶s, STATUS_ *p_status) { p_status->pitch_freq = BREAK_NUMBER; p_status->v_uv = SILENT; p_status->s_h = SEND; /* SENT */ p_status->cc_max = 0.0; p_status->threshold = paras.Thigh; return; } short ConvertFloatTo16Bit( float fSample ) { short ret = fSample * 32768.0f; return ret; } int read_next_wave_segment2(RageSoundReader_FileReader *sample, const Srpd_Op ¶s, SEGMENT_ *p_seg) { printf("read: size %d shift %d length %d\n", p_seg->size, p_seg->shift, p_seg->length); ASSERT( sample->GetNumChannels() == 1 ); int iSize = p_seg->size; float *pfData = new float[iSize]; int iNumRead = sample->Read( pfData, iSize ); for( int i=0; idata[i] = d; } else { p_seg->data[i] = 0; } } SAFE_DELETE_ARRAY( pfData ); return iNumRead > 0; } typedef struct list { int N0, score; struct list *next_item; } LIST_; void add_to_list (LIST_ **p_list_hd, LIST_ **p_list_tl, int N_val, int score_val) { LIST_ *new_node, *last_node; new_node = walloc(LIST_ ,1); last_node = *p_list_tl; new_node->N0 = N_val; new_node->score = score_val; new_node->next_item = NULL; if (*p_list_hd == NULL) *p_list_hd = new_node; else last_node->next_item = new_node; *p_list_tl = new_node; } void free_list (LIST_ **p_list_hd) { LIST_ *next; while (*p_list_hd != NULL) { next = (*p_list_hd)->next_item; wfree (*p_list_hd); *p_list_hd = next; } } void super_resolution_pda (const Srpd_Op ¶s, const SEGMENT_ &seg, CROSS_CORR_ *p_cc, STATUS_ *p_status) { static int zx_lft_N, zx_rht_N; static double prev_pf = BREAK_NUMBER; int n, j, k, N0 = 0, N1, N2, N_, q, lower_found = 0, score = 1, apply_bias; int x_index, y_index, z_index; int zx_rate = 0, zx_at_N0 = 0, prev_sign; int seg1_zxs = 0, seg2_zxs = 0, total_zxs; short prev_seg1, prev_seg2; short x_max = -MAXSHORT, x_min = MAXSHORT; short y_max = -MAXSHORT, y_min = MAXSHORT; double xx = 0.0, yy = 0.0, zz = 0.0, xy = 0.0, yz = 0.0, xz = 0.0; double max_cc = 0.0, coefficient, coeff_weight; double xx_N, yy_N, xy_N, y1y1_N, xy1_N, yy1_N, beta; LIST_ *sig_pks_hd, *sig_pks_tl, *sig_peak, *head, *tail; sig_pks_hd = head = NULL; sig_pks_tl = tail = NULL; /* set correlation coefficient threshold */ if (p_status->v_uv == UNVOICED || p_status->v_uv == SILENT) p_status->threshold = paras.Thigh; else /* p_status->v_uv == VOICED */ p_status->threshold = (paras.Tmin > paras.Tmax_ratio * p_status->cc_max) ? paras.Tmin : paras.Tmax_ratio * p_status->cc_max; /* determine if a bias should be applied */ if (paras.peak_tracking && prev_pf != BREAK_NUMBER && p_status->v_uv == VOICED && p_status->s_h != HOLD && p_status->pitch_freq < 1.75 * prev_pf && p_status->pitch_freq > 0.625 * prev_pf) apply_bias = 1; else apply_bias = 0; /* consider first two segments of period n = Nmin */ prev_seg1 = seg.data[paras.Nmax - paras.Nmin] < 0 ? -1 : 1; prev_seg2 = seg.data[paras.Nmax] < 0 ? -1 : 1; for (j = 0; j < paras.Nmin; j += paras.L) { /* find max and min amplitudes in x and y segments */ x_index = paras.Nmax - paras.Nmin + j; y_index = paras.Nmax + j; if (seg.data[x_index] > x_max) x_max = seg.data[x_index]; if (seg.data[x_index] < x_min) x_min = seg.data[x_index]; if (seg.data[y_index] > y_max) y_max = seg.data[y_index]; if (seg.data[y_index] < y_min) y_min = seg.data[y_index]; /* does new sample in x or y segment represent an input zero-crossing */ if (seg.data[x_index] * prev_seg1 < 0) { prev_seg1 *= -1; seg1_zxs++; } if (seg.data[y_index] * prev_seg2 < 0) { prev_seg2 *= -1; seg2_zxs++; } /* calculate parts for first correlation coefficient */ xx += (double) seg.data[x_index] * seg.data[x_index]; yy += (double) seg.data[y_index] * seg.data[y_index]; xy += (double) seg.data[x_index] * seg.data[y_index]; } /* low amplitude segment represents silence */ if (abs (x_max) + abs (x_min) < 2 * paras.Tsilent || abs (y_max) + abs (y_min) < 2 * paras.Tsilent) { for (q = 0; q < p_cc->size; p_cc->coeff[q++] = 0.0); prev_pf = p_status->pitch_freq; p_status->pitch_freq = BREAK_NUMBER; p_status->v_uv = SILENT; p_status->s_h = SEND; p_status->cc_max = 0.0; return; } /* determine first correlation coefficients, for period n = Nmin */ p_cc->coeff[0] = p_status->cc_max = xy / sqrt (xx) / sqrt (yy); for (q = 1; q < p_cc->size && q < paras.L; p_cc->coeff[q++] = 0.0); total_zxs = seg1_zxs + seg2_zxs; prev_sign = p_cc->coeff[0] < 0.0 ? -1 : 1; prev_seg1 = seg.data[paras.Nmax - paras.Nmin] < 0 ? -1 : 1; /* iteratively determine correlation coefficient for next possible period */ for (n = paras.Nmin + paras.L; n <= paras.Nmax; n += paras.L, j += paras.L) { x_index = paras.Nmax - n; y_index = paras.Nmax + j; /* does new samples in x or y segment represent an input zero-crossing */ if (seg.data[x_index] * prev_seg1 < 0) { prev_seg1 *= -1; total_zxs++; } if (seg.data[y_index] * prev_seg2 < 0) { prev_seg2 *= -1; total_zxs++; } /* determine next coefficient */ xx += (double) seg.data[x_index] * seg.data[x_index]; yy += (double) seg.data[y_index] * seg.data[y_index]; for (k = 0, xy = 0.0; k < n; k += paras.L) xy += (double) seg.data[paras.Nmax - n + k] * seg.data[paras.Nmax + k]; p_cc->coeff[n - paras.Nmin] = xy / sqrt (xx) / sqrt (yy); if (p_cc->coeff[n - paras.Nmin] > p_status->cc_max) p_status->cc_max = p_cc->coeff[n - paras.Nmin]; /* set unknown coefficients to zero */ for (q = n - paras.Nmin + 1; q < p_cc->size && q < n - paras.Nmin + paras.L; p_cc->coeff[q++] = 0.0); /* is there a slope with positive gradient in the coefficients track yet */ if (p_cc->coeff[n - paras.Nmin] > p_cc->coeff[n - paras.Nmin - paras.L]) lower_found = 1; /* has new coefficient resulted in a zero-crossing */ if (p_cc->coeff[n - paras.Nmin] * prev_sign < 0.0) { prev_sign *= -1; zx_rate++; } /* does the new coefficient represent a pitch period candidate */ if (N0 != 0 && zx_rate > zx_at_N0) { add_to_list (&sig_pks_hd, &sig_pks_tl, N0, 1); N0 = 0; max_cc = 0.0; } if (apply_bias && n > zx_lft_N && n < zx_rht_N) coeff_weight = 2.0; else coeff_weight = 1.0; if (p_cc->coeff[n - paras.Nmin] > max_cc && total_zxs > 3 && lower_found) { max_cc = p_cc->coeff[n - paras.Nmin]; if (max_cc * coeff_weight >= p_status->threshold) { zx_at_N0 = zx_rate; N0 = n; } } } /* unvoiced if no significant peak found in coefficients track */ if (sig_pks_hd == NULL) { prev_pf = p_status->pitch_freq; p_status->pitch_freq = BREAK_NUMBER; p_status->v_uv = UNVOICED; p_status->s_h = SEND; return; } /* find which significant peak in list corresponds to true pitch period */ sig_peak = sig_pks_hd; while (sig_peak != NULL) { yy = zz = yz = 0.0; for (j = 0; j < sig_peak->N0; j++) { y_index = paras.Nmax + j; z_index = paras.Nmax + sig_peak->N0 + j; yy += (double) seg.data[y_index] * seg.data[y_index]; zz += (double) seg.data[z_index] * seg.data[z_index]; yz += (double) seg.data[y_index] * seg.data[z_index]; } if (yy == 0.0 || zz == 0.0) coefficient = 0.0; else coefficient = yz / sqrt (yy) / sqrt (zz); if (apply_bias && sig_peak->N0 > zx_lft_N && sig_peak->N0 < zx_rht_N) coeff_weight = 2.0; else coeff_weight = 1.0; if (coefficient * coeff_weight >= p_status->threshold) { sig_peak->score = 2; if (head == NULL) { head = sig_peak; score = 2; } tail = sig_peak; } sig_peak = sig_peak->next_item; } if (head == NULL) head = sig_pks_hd; if (tail == NULL) tail = sig_pks_tl; N0 = head->N0; if (tail != head) { xx = 0.0; for (j = 0; j < tail->N0; j++) xx += (double) seg.data[paras.Nmax - tail->N0 + j] * seg.data[paras.Nmax - tail->N0 + j]; sig_peak = head; while (sig_peak != NULL) { if (sig_peak->score == score) { xz = zz = 0.0; for (j = 0; j < tail->N0; j++) { z_index = paras.Nmax + sig_peak->N0 + j; xz += (double) seg.data[paras.Nmax - tail->N0 + j] * seg.data[z_index]; zz += (double) seg.data[z_index] * seg.data[z_index]; } coefficient = xz / sqrt (xx) / sqrt (zz); if (sig_peak == head) max_cc = coefficient; else if (coefficient * paras.Tdh > max_cc) { N0 = sig_peak->N0; max_cc = coefficient; } } sig_peak = sig_peak->next_item; } } p_status->cc_max = p_cc->coeff[N0 - paras.Nmin]; /* voiced segment period now found */ if ((tail == head && score == 1 && p_status->v_uv != VOICED) || p_cc->coeff[N0 - paras.Nmin] < p_status->threshold) p_status->s_h = HOLD; else p_status->s_h = SEND; /* find left and right boundaries of peak in coefficients track */ zx_lft_N = zx_rht_N = 0; for (q = N0; q >= paras.Nmin; q -= paras.L) if (p_cc->coeff[q - paras.Nmin] < 0.0) { zx_lft_N = q; break; } for (q = N0; q <= paras.Nmax; q += paras.L) if (p_cc->coeff[q - paras.Nmin] < 0.0) { zx_rht_N = q; break; } /* define small region around peak */ if (N0 - paras.L < paras.Nmin) { N1 = N0; N2 = N0 + 2 * paras.L; } else if (N0 + paras.L > paras.Nmax) { N1 = N0 - 2 * paras.L; N2 = N0; } else { N1 = N0 - paras.L; N2 = N0 + paras.L; } /* compensate for decimation factor L */ if (paras.L != 1) { xx = yy = xy = 0.0; for (j = 0; j < N1; j++) { x_index = paras.Nmax - N1 + j; y_index = paras.Nmax + j; xx += (double) seg.data[x_index] * seg.data[x_index]; xy += (double) seg.data[x_index] * seg.data[y_index]; yy += (double) seg.data[y_index] * seg.data[y_index]; } p_cc->coeff[N1 - paras.Nmin] = p_status->cc_max = xy / sqrt (xx) / sqrt (yy); N0 = N1; for (n = N1 + 1; n <= N2; n++, j++) { xx += (double) seg.data[paras.Nmax - n] * seg.data[paras.Nmax - n]; yy += (double) seg.data[paras.Nmax + j] * seg.data[paras.Nmax + j]; for (k = 0, xy = 0.0; k < n; k++) xy += (double) seg.data[paras.Nmax - n + k] * seg.data[paras.Nmax + k]; p_cc->coeff[n - paras.Nmin] = xy / sqrt (xx) / sqrt (yy); if (p_cc->coeff[n - paras.Nmin] > p_status->cc_max) { p_status->cc_max = p_cc->coeff[n - paras.Nmin]; N0 = n; } } } /* compensate for finite resolution in estimating pitch */ if (N0 - 1 < paras.Nmin || N0 == N1) N_ = N0; else if (N0 + 1 > paras.Nmax || N0 == N2) N_ = N0 - 1; else if (p_cc->coeff[N0 - paras.Nmin] - p_cc->coeff[N0 - paras.Nmin - 1] < p_cc->coeff[N0 - paras.Nmin] - p_cc->coeff[N0 - paras.Nmin + 1]) N_ = N0 - 1; else N_ = N0; xx_N = yy_N = xy_N = y1y1_N = xy1_N = yy1_N = 0.0; for (j = 0; j < N_; j++) { x_index = paras.Nmax - N_ + j; y_index = paras.Nmax + j; xx_N += (double) seg.data[x_index] * seg.data[x_index]; yy_N += (double) seg.data[y_index] * seg.data[y_index]; xy_N += (double) seg.data[x_index] * seg.data[y_index]; y1y1_N += (double) seg.data[y_index + 1] * seg.data[y_index + 1]; xy1_N += (double) seg.data[x_index] * seg.data[y_index + 1]; yy1_N += (double) seg.data[y_index] * seg.data[y_index + 1]; } beta = (xy1_N * yy_N - xy_N * yy1_N) / (xy1_N * (yy_N - yy1_N) + xy_N * (y1y1_N - yy1_N)); if (beta < 0.0) { N_--; beta = 0.0; } else if (beta >= 1.0) { N_++; beta = 0.0; } else p_status->cc_max = ((1.0 - beta) * xy_N + beta * xy1_N) / sqrt (xx_N * ((1.0 - beta) * (1.0 - beta) * yy_N + 2.0 * beta * (1.0 - beta) * yy1_N + beta * beta * y1y1_N)); prev_pf = p_status->pitch_freq; p_status->pitch_freq = (double) (paras.sample_freq) / (double) (N_ + beta); p_status->v_uv = VOICED; free_list (&sig_pks_hd); return; } DetectPitch::DetectPitch() { m_pSrpdOp = new Srpd_Op; m_pSegment = new SEGMENT_; m_iSamplesFilledInSegment = 0; m_pCC = new CROSS_CORR_; m_pPdaStatus = new STATUS_; } DetectPitch::~DetectPitch() { SAFE_DELETE( m_pSrpdOp ); SAFE_DELETE( m_pSegment ); m_iSamplesFilledInSegment = 0; SAFE_DELETE( m_pCC ); SAFE_DELETE( m_pPdaStatus ); } void DetectPitch::Init(int iSampleFreq) { default_srpd_op(m_pSrpdOp); // default values m_pSrpdOp->sample_freq = iSampleFreq; initialise_structures (m_pSrpdOp, m_pSegment, m_pCC); for( int i=0; isize; ++i ) m_pCC->coeff[i] = 0.0; initialise_status (*m_pSrpdOp, m_pPdaStatus); // add a low-pass filter? } int DetectPitch::ReadOne(RageSoundReader_FileReader *sample) { if( read_next_wave_segment2 (sample, *m_pSrpdOp, m_pSegment) != 0 ) { super_resolution_pda (*m_pSrpdOp, *m_pSegment, m_pCC, m_pPdaStatus); printf( "track set: (of %d) is %f - voiced %d, held %d\n", m_pSegment->length, m_pPdaStatus->pitch_freq, m_pPdaStatus->v_uv, m_pPdaStatus->s_h ); return true; } end_structure_use (m_pSegment, m_pCC); return false; } int DetectPitch::ReadOne(short *pData, int iCount) { //printf("read: size %d shift %d length %d\n", m_pSegment->size, m_pSegment->shift, m_pSegment->length); for( int iDataReadCount = 0; iDataReadCount < iCount; ) { int iSamplesLeftInSegment = m_pSegment->size - m_iSamplesFilledInSegment; int iSamplesLeftInData = iCount - iDataReadCount; int iSamplesToCopy = min( iSamplesLeftInSegment, iSamplesLeftInData ); memcpy( m_pSegment->data + m_iSamplesFilledInSegment, pData + iDataReadCount, iSamplesToCopy*sizeof(short) ); m_iSamplesFilledInSegment += iSamplesToCopy; iDataReadCount += iSamplesToCopy; ASSERT( m_iSamplesFilledInSegment <= m_pSegment->size ); if( m_iSamplesFilledInSegment == m_pSegment->size ) // segment is full { super_resolution_pda (*m_pSrpdOp, *m_pSegment, m_pCC, m_pPdaStatus); //printf( "track set: (of %d) is %f - voiced %d, held %d\n", m_pSegment->length, m_pPdaStatus->pitch_freq, m_pPdaStatus->v_uv, m_pPdaStatus->s_h ); //printf( "v %d, h %d ", m_pPdaStatus->v_uv, m_pPdaStatus->s_h ); const int iNumBars = 30; for( int i=0; ipitch_freq < freq) ? "|" : "X" ); } //printf( "\n" ); int iNumSamplesToShift = m_pSegment->size - m_pSegment->shift; m_iSamplesFilledInSegment = iNumSamplesToShift; memcpy( m_pSegment->data, m_pSegment->data + m_pSegment->shift, iNumSamplesToShift*sizeof(short) ); } } return true; } void DetectPitch::GetStatus( MicrophoneStatus &out ) { out.fFreq = m_pPdaStatus->pitch_freq; out.fMaxFreq = m_pSrpdOp->max_pitch; out.bVoiced = m_pPdaStatus->v_uv == VOICED; } void DetectPitch::End() { end_structure_use (m_pSegment, m_pCC); } void srpd2() { RageFile file; file.Open( "speech-test.wav" ); RageSoundReader_FileReader *sample = new RageSoundReader_WAV; sample->Open( &file ); DetectPitch dp; dp.Init( SAMPLES_PER_SEC ); while( dp.ReadOne(sample) ) { } } /* * (c) 2007 Chris Danford * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, and/or sell copies of the Software, and to permit persons to * whom the Software is furnished to do so, provided that the above * copyright notice(s) and this permission notice appear in all copies of * the Software and that both the above copyright notice(s) and this * permission notice appear in supporting documentation. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */