Files
itgmania212121/src/PitchDetectionTestUtil.cpp
T

3386 lines
80 KiB
C++
Raw Normal View History

2011-03-17 01:47:30 -04:00
/*************************************************************************/
/* */
/* 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<class K, class V>
V &EST_TKVL<K, V>::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<EST_String,EST_String>::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<class K, class V>
int EST_TKVL<K, V>::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<K,V> item;
item.k = rkey;
item.v = rval;
list.append(item);
return 1;
}
int EST_TKVL<EST_String,EST_String>::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<K,V> item;
item.k = rkey;
item.v = rval;
list.append(item);
return 1;
}
template<class K, class V>
const int EST_TKVL<K, V>::present(const K &rkey) const
{
if (find_pair_key(rkey) == 0)
return 0;
else
return 1;
}
const int EST_TKVL<EST_String, EST_String>::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;i<dummy.n();i++)
dummy[i] = 0.0;
int e=slowIFFT(filt,dummy);
if (e != 0)
{
cerr << "Failed to design filter because FFT failed" << endl;
return EST_FVector(0);
}
EST_FVector reduced_filt(filter_order);
int mid = filter_order/2;
reduced_filt[mid] = filt(0);
for(i=1; i<=mid ;i++)
{
// Hann window for zero ripple
float window = 0.5 + 0.5 * cos(PI*(float)i / (float)mid);
reduced_filt[mid+i] = filt(i) * window;
reduced_filt[mid-i] = filt(i) * window;
}
return reduced_filt;
}
EST_FVector design_high_or_low_pass_FIR_filter(int sample_rate,
int cutoff_freq, int order,
float gain1, float gain2)
{
// change to bandpass filter .....
if (sample_rate <= 0){
cerr << "Can't design a FIR filter for a sampling rate of "
<< sample_rate << endl;
return EST_FVector(0);
}
int i;
int N=10; // good minimum size
int fft_size = (int)pow(2.0, N);
while(fft_size < order*4){ // rule of thumb !?
N++;
fft_size = (int)pow(2.0, N);
}
// freq response is from 0 to sampling freq and therefore
// must be symmetrical about 1/2 sampling freq
EST_FVector freq_resp(fft_size);
int normalised_cutoff = (fft_size * cutoff_freq)/sample_rate;
for(i=0;i<normalised_cutoff;i++){
freq_resp[i] = gain1;
freq_resp[fft_size-i-1] = gain1;
}
for(;i<fft_size/2;i++){
freq_resp[i] = gain2;
freq_resp[fft_size-i-1] = gain2;
}
return design_FIR_filter(freq_resp, order);
}
EST_FVector design_lowpass_FIR_filter(int sample_rate, int freq, int order)
{
return design_high_or_low_pass_FIR_filter(sample_rate,
freq, order, 1.0, 0.0);
}
struct Ms_Op { /* median smoother operations */
int smooth_double;
int apply_hanning;
int extrapolate;
int first_median;
int second_median;
int window_length;
int interp;
float breaker;
};
struct Ms_Op *default_ms_op(struct Ms_Op *ms)
{
ms->smooth_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_String, EST_Val>;
}
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<EST_String, EST_Val>;
*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<KEY, VAL>; \
template class EST_TKVI<KEY, VAL>; \
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<KEY, VAL> 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<KEY, VAL> KVI_ ## TAG ## _t; \
typedef EST_TKVL<KEY, VAL> 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<KEY, VAL> KVI_ ## TAG ## _t; \
typedef EST_TKVL<KEY, VAL> 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<KEY, VAL> KVI_ ## TAG ## _t; \
typedef EST_TKVL<KEY, VAL> 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 <class K, class V>
EST_Litem *EST_TKVL<K, V>::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 <class K, class V>
int EST_TKVL<K, V>::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<class K, class V>
const V &EST_TKVL<K, V>::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<class T>
EST_TMatrix<T>::~EST_TMatrix()
{
p_num_rows = 0;
p_row_step=0;
}
template<class T>
EST_TVector<T>::~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<class T>
EST_TVector<T>::EST_TVector()
{
default_vals();
}
template<class T>
EST_TMatrix<T>::EST_TMatrix()
{
default_vals();
}
EST_Featured::EST_Featured(void)
{
init_features();
}
template<class T> EST_TSimpleVector<T>::EST_TSimpleVector(const EST_TSimpleVector<T> &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<class T>
void EST_TSimpleMatrix<T>::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; j<new_cols; j++)
for(i=copy_r; i<new_rows; i++)
this->a_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; i<copy_r; i++)
for(j=copy_c; j<new_cols; j++)
this->a_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; j<new_cols; j++)
for(i=copy_r; i<new_rows; i++)
this->a_no_check(i,j) = *this->def_val;
}
}
else
EST_TMatrix<T>::resize(new_rows, new_cols, 1);
}
else
EST_TMatrix<T>::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 "<<bytes<<"\n";
}
#if defined(__CHUNK_USE_WALLOC__)
void *it = walloc(char, size+bytes);
#else
void *it = new char[size + bytes];
#endif
// cerr<<"allocated "<<bytes+size<<" byte for chunk\n";
((EST_Chunk *)it) -> 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<class K, class V> EST_TKVL<K, V> &EST_TKVL<K, V>::operator =
(const EST_TKVL<K, V> &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<length; i++)
data[i] = SWAPSHORT(data[i]);
}
void schar_to_short(const unsigned char *chars,short *data,int length)
{
/* Convert 8 bit data to shorts SIGNED CHAR */
int i;
for (i=0; i<length; i++)
data[i] = (((unsigned char)chars[i]))*256;
}
template<class ENUM, class VAL, class INFO>
INFO &EST_TValuedEnumI<ENUM,VAL,INFO>::info (ENUM token) const
{
int i;
for(i=0; i<this->ndefinitions; 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<class ENUM, class VAL, class INFO>
ENUM EST_TValuedEnumI<ENUM,VAL,INFO>::token (VAL value) const
{
int i,j;
for(i=0; i<this->ndefinitions; i++)
for(j=0; j<NAMED_ENUM_MAX_SYNONYMS && this->definitions[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<class ENUM, class VAL, class INFO>
VAL EST_TValuedEnumI<ENUM,VAL,INFO>::value (ENUM token, int n) const
{
int i;
for(i=0; i<this->ndefinitions; 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<class ENUM, class VAL, class INFO>
int EST_TValuedEnumI<ENUM,VAL,INFO>::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<len;j++)
p[j] = fill;
p[len]='\0';
}
else
memory=NULL;
}
}
void EST_Featured::clear_features()
{
if (p_features)
{
delete p_features;
p_features=NULL;
}
init_features();
}
void EST_Featured::init_features()
{
p_features=NULL;
}
template<class T>
void EST_TVector<T>::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<copy_c; i++)
a_no_check(i)
= old_vals[vcell_pos(i,
old_column_step)];
}
else
copy_c = old_cols;
for(i=copy_c; i<new_cols; i++)
a_no_check(i) = *def_val;
}
if (old_vals && old_vals != p_memory && !p_sub_matrix)
delete [] (old_vals-old_offset);
}
template<class T>
void EST_TMatrix<T>::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; i<copy_r; i++)
for(j=copy_c; j<new_cols; j++)
a_no_check(i,j) = *this->def_val;
for(i=copy_r; i<new_rows; i++)
for(j=0; j<new_cols; j++)
a_no_check(i,j) = *this->def_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<class T> void EST_TSimpleVector<T>::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<length; i++)
{
data[i] = (((int)chars[i])-128)*256;
}
}
static short st_ulaw_to_short( unsigned char ulawbyte )
{
static int exp_lut[8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 };
int sign, exponent, mantissa;
short sample;
ulawbyte = ~ ulawbyte;
sign = ( ulawbyte & 0x80 );
exponent = ( ulawbyte >> 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<length; i++)
data[i] = st_ulaw_to_short(ulaw[i]); /* ulaw convert */
}
static int stdio_fread(void *buff,int size,int nitems,FILE *fp)
{
// So it can find the stdio one rather than the TokenStream one
return fread(buff,size,nitems,fp);
}
int EST_TokenStream::fread(void *buff, int size, int nitems)
{
// switching into binary mode for current position
int items_read;
// so we can continue to read afterwards
if (peeked_tokp)
{
exit(1);//cerr << "ERROR " << pos_description()
// << " peeked into binary data" << endl;
return 0;
}
peeked_charp = FALSE;
peeked_tokp = FALSE;
switch (type)
{
case tst_none:
exit(1);//cerr << "EST_TokenStream unset" << endl;
return 0;
break;
case tst_file:
items_read = stdio_fread(buff,(size_t)size,(size_t)nitems,fp);
p_filepos += items_read*size;
return items_read;
case tst_pipe:
exit(1);//cerr << "EST_TokenStream fread pipe not yet supported" << endl;
return 0;
break;
case tst_istream:
exit(1);//cerr << "EST_TokenStream fread istream not yet supported" << endl;
return 0;
case tst_string:
if ((buffer_length-pos)/size < nitems)
items_read = (buffer_length-pos)/size;
else
items_read = nitems;
memcpy(buff,&buffer[pos],items_read*size);
pos += items_read*size;
return items_read;
default:
exit(1);//cerr << "EST_TokenStream: unknown type" << endl;
return EOF;
}
return 0; // can't get here
}
template<class T>
void EST_TMatrix<T>::set_memory(T *buffer, int offset,
int rows, int columns,
int free_when_destroyed)
{
EST_TVector<T>::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<class T>
EST_TVector<T> &EST_TVector<T>::operator=(const EST_TVector<T> &in)
{
copy(in);
return *this;
}
template<class T>
EST_TMatrix<T> &EST_TMatrix<T>::operator=(const EST_TMatrix<T> &in)
{
copy(in);
return *this;
}
template<class T> EST_TSimpleVector<T> &EST_TSimpleVector<T>::operator=(const EST_TSimpleVector<T> &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<class T> EST_TSimpleMatrix<T> &EST_TSimpleMatrix<T>::operator=(const EST_TSimpleMatrix<T> &in)
{
copy(in);
return *this;
}
template<class T>
void EST_TSimpleMatrix<T>::copy(const EST_TSimpleMatrix<T> &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<class T>
EST_TVector<T>::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<class T> void EST_TList<T>::free_item(EST_UItem *item)
{ EST_TItem<T>::release((EST_TItem<T> *)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<class T> EST_TItem<T> *EST_TItem<T>::make(const T &val)
{
EST_TItem<T> *it=NULL;
if (s_free!=NULL)
{
void *mem = s_free;
s_free=(EST_TItem<T> *)s_free->n;
s_nfree--;
// Create an item in the retrieved memory.
it=new (mem) EST_TItem<T>(val);
}
else
it = new EST_TItem<T>(val);
return it;
}
template<class ENUM, class VAL, class INFO>
ENUM EST_TValuedEnumI<ENUM,VAL,INFO>::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<class T>
void EST_TVector<T>::default_vals()
{
p_num_columns = 0;
p_offset=0;
p_column_step=0;
p_memory = NULL;
p_sub_matrix=FALSE;
}
template<class T>
void EST_TMatrix<T>::default_vals()
{
EST_TVector<T>::default_vals();
p_num_rows = 0;
p_row_step=0;
}
template<class T>
void EST_TMatrix<T>::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<class T>
void EST_TMatrix<T>::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<class T> void EST_TSimpleVector<T>::copy(const EST_TSimpleVector<T> &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<T> *)this)->copy(a);
}
template<class T>
void EST_TVector<T>::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<class T>
void EST_TVector<T>::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<class T>
void EST_TSimpleMatrix<T>::copy_data(const EST_TSimpleMatrix<T> &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<class T> void EST_TItem<T>::release(EST_TItem<T> *it)
{
if (s_nfree < s_maxFree)
{
// Destroy the value in case it holds resources.
it->EST_TItem<T>::~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<class T> EST_TList<T> &EST_TList<T>::operator=(const EST_TList<T> &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<class T>
void EST_TMatrix<T>::copy(const EST_TMatrix<T> &a)
{
resize(a.num_rows(), a.num_columns(), 0);
copy_data(a);
}
template<class T>
void EST_TVector<T>::copy(const EST_TVector<T> &a)
{
resize(a.n(), FALSE);
copy_data(a);
}
template<class T> void EST_TList<T>::copy_items(const EST_TList<T> &l)
{
EST_UItem *p;
for (p = l.head(); p; p = next(p))
append(l.item(p));
}
template<class T>
void EST_TMatrix<T>::copy_data(const EST_TMatrix<T> &a)
{
set_values(a.p_memory,
a.p_row_step, a.p_column_step,
0, a.num_rows(),
0, a.num_columns());
}
template<class T>
void EST_TVector<T>::copy_data(const EST_TVector<T> &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<class T>
void EST_TVector<T>::set_values(const T *data,
int step,
int start_c,
int num_c)
{
for(int i=0, c=start_c, p=0; i<num_c; i++, c++, p+=step)
a_no_check(c) = data[p];
}
template<class ENUM, class VAL, class INFO>
void EST_TValuedEnumI<ENUM,VAL,INFO>::initialise(const void *vdefs)
{
int n=0;
typedef EST_TValuedEnumDefinition<ENUM,VAL,INFO> 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<class ENUM, class VAL, class INFO>
EST_TValuedEnumI<ENUM,VAL,INFO>::~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 &paras, 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 &paras, 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; i<iSize; ++i )
{
if( i < iNumRead )
{
float f = pfData[i];
short d = ConvertFloatTo16Bit(f);
p_seg->data[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 &paras, 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; i<m_pCC->size; ++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; i<iNumBars; i++ )
{
//const int freq = SCALE( i, 0, iNumBars, 1, 400 );
//printf( (m_pPdaStatus->pitch_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.
*/