Bibliography
C/C++ Code for VoicTract Class
VocTract.h
/******************************************************************************
* Institution: Stanford University
* Project: Sonification
* Author: Ryan Cassidy (05157787)
* Date: Summer 2003
******************************************************************************/
/*! \class VocTract
* \brief STK class to implement modified Cook tract model. All references to
* PRC's thesis below denote the thesis by former CCRMAlite Perry R. Cook.
*
* VERSION CONTROL INFORMATION:
*
* $RCSfile: VocTract.cpp,v $
*
* $Author: rjc $
*
* $Date: 2003/10/05 05:30:23 $
*
* $Locker: $
*
* $Log: VocTract.cpp,v $
* Revision 1.2 2003/10/05 05:30:23 rjc
* Added ability to read shapes from file.
*
* Revision 1.1 2003/09/29 21:50:33 rjc
* Initial revision
*
*
******************************************************************************/
#include "VocTract.h"
#include "Stk.h"
#include "ShpFile.h"
#include <cassert>
#include <string>
using namespace std;
#define DEFAULT_SECTION_LENGTH ((MY_FLOAT) 1.0)
#define DEFAULT_SECTION_RADIUS ((MY_FLOAT) 1.0)
const MY_FLOAT VocTract::_default_section_radii[] = {0.928177, 1.37569,
1.37569, 0.679558,
0.629834, 0.646409,
0.568966, 0.662983};
const ShapeRadii VocTract::_shp_radii[] =
{
{"aah", {0.821402, 0.783922, 1.09815 , 0.993759, 0.817757, 1.19078 , 1.31497 ,
1.07057 }},
{"eee", {0.928177, 1.37569 , 1.37569 , 0.679558, 0.629834, 0.646409, 0.568966,
0.662983}}
};
// Above are the default values for the vowel 'eee' as obtained by Perry Cook.
VocTract::VocTract(int num_sections /* = DEFAULT_NUM_SECTIONS */)
:
_num_sections(num_sections),
_pos_delay(new DelayA[_num_sections]),
_neg_delay(new DelayA[_num_sections]),
_lip_refl(0.0),
_last_lip_in(0.0),
_last_out(0.0),
_lip_refl_gain(-0.45),
_glot_refl_gain(0.7),
_tract_minus(0.0),
_k(new MY_FLOAT[_num_sections-1]),
_radii(new MY_FLOAT[_num_sections]),
_lengths(new MY_FLOAT[_num_sections])
{
// Set tract section lengths to default value.
for (int i=0; i<_num_sections; i++)
{
this->setSectionLength(i, DEFAULT_SECTION_LENGTH);
}
// If the number of tract sections is equal to the value which we have a
// default for, initialize the tract radii accordingly.
if (_num_sections == sizeof(_default_section_radii)/sizeof(MY_FLOAT))
{
this->setRadii(_default_section_radii, _num_sections);
}
else
{
for (int i=0; i<_num_sections; i++)
{
this->setSectionRadius(i, DEFAULT_SECTION_RADIUS);
}
}
}
VocTract::~VocTract()
{
delete [] _pos_delay;
delete [] _neg_delay;
delete [] _k;
delete [] _radii;
delete [] _lengths;
}
/******************************************************************************
* Functions to handle clocking in and out of samples to the vocal tract.
******************************************************************************/
MY_FLOAT VocTract::tick(MY_FLOAT sample)
{
// The variable _lip_refl will be used by the upcoming call to tractTick().
_lip_refl = (_last_lip_in + _pos_delay[_num_sections-1].nextOut())
* _lip_refl_gain;
MY_FLOAT temp = _last_lip_in;
_last_out = temp +
(_last_lip_in=this->tractTick(sample +
_neg_delay[0].nextOut()*_glot_refl_gain));
return _last_out;
}
// In this function, we implement the delay line section of the discrete-time
// tract model shown in Fig. 1.5 of PRC's thesis.
MY_FLOAT VocTract::tractTick(MY_FLOAT sample)
{
// First handle positive rail in Fig. 1.5 of PRC's thesis.
MY_FLOAT pos_out = _pos_delay[0].tick(sample);
for (int i=1; i<_num_sections; i++)
{
// Pass through Kelly-Lochbaum junction.
MY_FLOAT neg_in = _neg_delay[i].nextOut();
MY_FLOAT pos_in = (-_k[i-1])*neg_in + (1+_k[i-1])*pos_out;
// Clock through positive delay.
pos_out = _pos_delay[i].tick(pos_in);
}
// Now clock samples through negative rail of Fig. 1.5 of PRC's thesis.
MY_FLOAT neg_out = _neg_delay[_num_sections-1].tick(_lip_refl);
for (int i=_num_sections-2; i>=0; i--)
{
// Pass through Kelly-Lochbaum junction.
MY_FLOAT pos_in = _pos_delay[i].lastOut();
MY_FLOAT neg_in = (_k[i])*pos_in + (1-_k[i])*neg_out;
// Clock through positive delay.
neg_out = _neg_delay[i].tick(neg_in);
}
_tract_minus = neg_out;
return pos_out;
}
/******************************************************************************
* Functions to get and set radii of vocal tract sections.
******************************************************************************/
void VocTract::setSectionRadius(int index, MY_FLOAT radius)
{
assert(index >= 0 && index < _num_sections);
_radii[index] = radius;
// Update junction coefficient(s) _k since radii have changed.
// First update the junction coefficient in front of the section.
if ((index) < (_num_sections-1))
{
_k[index] = (_radii[index+1] - _radii[index]) /
(_radii[index+1] + _radii[index]);
}
// Second update the junction coefficient behind the section.
if (index > 0)
{
_k[index-1] = (_radii[index] - _radii[index-1]) /
(_radii[index] + _radii[index-1]);
}
}
void VocTract::setRadii(const MY_FLOAT *radii, int num_sections)
{
assert(num_sections == _num_sections);
for (int i=0; i<num_sections; i++)
{
this->setSectionRadius(i, radii[i]);
}
}
const MY_FLOAT *VocTract::getRadii() const
{
return _radii;
}
/******************************************************************************
* Functions to get and set lengths of vocal tract sections.
******************************************************************************/
void VocTract::setSectionLength(int index, MY_FLOAT length)
{
assert(index >= 0 and (index) < _num_sections);
_lengths[index] = length;
_pos_delay[index].setDelay(length);
_neg_delay[index].setDelay(length);
}
void VocTract::setLengths(const MY_FLOAT *lengths, int num_sections)
{
assert(num_sections == _num_sections);
for (int i=0; i<num_sections; i++)
{
this->setSectionLength(i, lengths[i]);
}
}
const MY_FLOAT *VocTract::getLengths() const
{
return _lengths;
}
int VocTract::setShape(const char *name, ShapeDataSource sds
/* = SHP_FILE */)
{
bool found_match = false;
MY_FLOAT *temp_radii;
ShpFile *sf;
switch (sds)
{
case STRUCT:
for (unsigned int i=0; i < sizeof(VocTract::_shp_radii)/sizeof(ShapeRadii);
i++)
{
if (string(name) == string(VocTract::_shp_radii[i].name))
{
found_match = true;
// Found a match.
this->setRadii(VocTract::_shp_radii[i].radii, DEFAULT_NUM_SECTIONS);
}
}
if (!found_match)
{
return -1;
}
else
{
return 0;
}
break;
case SHP_FILE:
sf = new ShpFile(("shapes/" + string(name) + ".shp").c_str());
if (!(*sf))
{
return -1;
}
if ((*sf).numSections() != this->getNumSections())
{
return -1;
}
assert((*sf).numSections() == DEFAULT_NUM_SECTIONS);
temp_radii = new MY_FLOAT [this->getNumSections()];
if (!((*sf).getRadii())) return -1;
memcpy(temp_radii, (*sf).getRadii(),
this->getNumSections()*sizeof(MY_FLOAT));
this->setRadii(temp_radii, this->getNumSections());
delete [] temp_radii;
delete sf;
break;
default:
assert(false);
break;
}
return 0;
}
/******************************************************************************
* Functions to control the injection of noise into the vocal tract.
******************************************************************************/
Bibliography
C/C++ Code for VoicTract Class
VocTract.h
``Audio Speech Research Note'',
Ryan J. Cassidy,
published electronically by author, July 2003.
Download PDF version (audio_speech.pdf)
Download compressed PostScript version (audio_speech.ps.gz)
Copyright © 2003-11-28 by Ryan J. Cassidy.
Please email errata, comments, and suggestions to Ryan J. Cassidy <ryanc@ieee.org>
Stanford University