1 /***************************************************************************
2 * Copyright 1991, 1992, 1993, 1994, 1995, 1996, 2001, 2002 *
3 * David R. Hill, Leonard Manzara, Craig Schock *
4 * *
5 * This program is free software: you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation, either version 3 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17 ***************************************************************************/
18 // 2014-09
19 // This file was copied from Gnuspeech and modified by Marcelo Y. Matuda.
20
21 /******************************************************************************
22 *
23 * Program: tube
24 *
25 * Description: Software (non-real-time) implementation of the Tube
26 * Resonance Model for speech production.
27 *
28 * Author: Leonard Manzara
29 *
30 * Date: July 5th, 1994
31 *
32 ******************************************************************************/
33
34 #include "Tube.h"
35
36 #include <cstdio>
37 #include <cstdlib>
38 #include <cstring>
39 #include <cmath>
40 #include <fstream>
41 #include <sstream>
42 #include <string>
43 #include <utility> /* move */
44
45 #include "Exception.h"
46 #include "Log.h"
47 #include "Text.h"
48 #include "WAVEFileWriter.h"
49
50 /* COMPILE SO THAT INTERPOLATION NOT DONE FOR SOME CONTROL RATE PARAMETERS */
51 //#define MATCH_DSP 1
52
53 #define INPUT_VECTOR_RESERVE 128
54 #define OUTPUT_VECTOR_RESERVE 1024
55
56 #define GLOTTAL_SOURCE_PULSE 0
57 #define GLOTTAL_SOURCE_SINE 1
58
59 /* PITCH VARIABLES */
60 #define PITCH_BASE 220.0
61 #define PITCH_OFFSET 3 /* MIDDLE C = 0 */
62 //#define LOG_FACTOR 3.32193
63
64 /* RANGE OF ALL VOLUME CONTROLS */
65 #define VOL_MAX 60
66
67 /* SCALING CONSTANT FOR INPUT TO VOCAL TRACT & THROAT (MATCHES DSP) */
68 //#define VT_SCALE 0.03125 /* 2^(-5) */
69 // this is a temporary fix only, to try to match dsp synthesizer
70 #define VT_SCALE 0.125 /* 2^(-3) */
71
72 /* FINAL OUTPUT SCALING, SO THAT .SND FILES APPROX. MATCH DSP OUTPUT */
73 #define OUTPUT_SCALE 0.95
74
75 /* BI-DIRECTIONAL TRANSMISSION LINE POINTERS */
76 #define TOP 0
77 #define BOTTOM 1
78
79 //#define OUTPUT_SRATE_LOW 22050.0
80 //#define OUTPUT_SRATE_HIGH 44100.0
81
82
83
84 namespace GS {
85 namespace TRM {
86
Tube()87 Tube::Tube()
88 {
89 reset();
90
91 inputData_.reserve(INPUT_VECTOR_RESERVE);
92 outputData_.reserve(OUTPUT_VECTOR_RESERVE);
93 }
94
~Tube()95 Tube::~Tube()
96 {
97 }
98
99 void
reset()100 Tube::reset()
101 {
102 outputRate_ = 0.0;
103 controlRate_ = 0.0;
104 volume_ = 0.0;
105 channels_ = 0;
106 balance_ = 0.0;
107 waveform_ = 0;
108 tp_ = 0.0;
109 tnMin_ = 0.0;
110 tnMax_ = 0.0;
111 breathiness_ = 0.0;
112 length_ = 0.0;
113 temperature_ = 0.0;
114 lossFactor_ = 0.0;
115 apertureRadius_ = 0.0;
116 mouthCoef_ = 0.0;
117 noseCoef_ = 0.0;
118 memset(noseRadius_, 0, sizeof(double) * TOTAL_NASAL_SECTIONS);
119 throatCutoff_ = 0.0;
120 throatVol_ = 0.0;
121 modulation_ = 0;
122 mixOffset_ = 0.0;
123 controlPeriod_ = 0;
124 sampleRate_ = 0;
125 actualTubeLength_ = 0.0;
126 memset(&oropharynx_[0][0][0], 0, sizeof(double) * TOTAL_SECTIONS * 2 * 2);
127 memset(oropharynxCoeff_, 0, sizeof(double) * TOTAL_COEFFICIENTS);
128 memset(&nasal_[0][0][0], 0, sizeof(double) * TOTAL_NASAL_SECTIONS * 2 * 2);
129 memset(nasalCoeff_, 0, sizeof(double) * TOTAL_NASAL_COEFFICIENTS);
130 memset(alpha_, 0, sizeof(double) * TOTAL_ALPHA_COEFFICIENTS);
131 currentPtr_ = 1;
132 prevPtr_ = 0;
133 memset(fricationTap_, 0, sizeof(double) * TOTAL_FRIC_COEFFICIENTS);
134 dampingFactor_ = 0.0;
135 crossmixFactor_ = 0.0;
136 breathinessFactor_ = 0.0;
137 prevGlotAmplitude_ = -1.0;
138 inputData_.resize(0);
139 memset(¤tData_, 0, sizeof(CurrentData));
140 memset(&singleInput_, 0, sizeof(InputData));
141 outputDataPos_ = 0;
142 outputData_.resize(0);
143
144 if (srConv_) srConv_->reset();
145 if (mouthRadiationFilter_) mouthRadiationFilter_->reset();
146 if (mouthReflectionFilter_) mouthReflectionFilter_->reset();
147 if (nasalRadiationFilter_) nasalRadiationFilter_->reset();
148 if (nasalReflectionFilter_) nasalReflectionFilter_->reset();
149 if (throat_) throat_->reset();
150 if (glottalSource_) glottalSource_->reset();
151 if (bandpassFilter_) bandpassFilter_->reset();
152 if (noiseFilter_) noiseFilter_->reset();
153 if (noiseSource_) noiseSource_->reset();
154 if (inputFilters_) inputFilters_->reset();
155 }
156
157 void
synthesizeToFile(std::istream & inputStream,const char * outputFile)158 Tube::synthesizeToFile(std::istream& inputStream, const char* outputFile)
159 {
160 if (!outputData_.empty()) {
161 reset();
162 }
163 parseInputStream(inputStream);
164 initializeSynthesizer();
165 #if 0
166 if (Log::debugEnabled) {
167 printInfo(inputFile);
168 }
169 #endif
170 synthesizeForInputSequence();
171 writeOutputToFile(outputFile);
172 }
173
174 void
synthesizeToBuffer(std::istream & inputStream,std::vector<float> & outputBuffer)175 Tube::synthesizeToBuffer(std::istream& inputStream, std::vector<float>& outputBuffer)
176 {
177 if (!outputData_.empty()) {
178 reset();
179 }
180 parseInputStream(inputStream);
181 initializeSynthesizer();
182 synthesizeForInputSequence();
183 writeOutputToBuffer(outputBuffer);
184 }
185
186 /******************************************************************************
187 *
188 * function: printInfo
189 *
190 * purpose: Prints pertinent variables to standard output.
191 *
192 ******************************************************************************/
193 void
printInfo(const char * inputFile)194 Tube::printInfo(const char* inputFile)
195 {
196 /* PRINT INPUT FILE NAME */
197 printf("input file:\t\t%s\n\n", inputFile);
198
199 /* ECHO INPUT PARAMETERS */
200 printf("outputRate:\t\t%.1f Hz\n", outputRate_);
201 printf("controlRate:\t\t%.2f Hz\n\n", controlRate_);
202
203 printf("volume:\t\t\t%.2f dB\n", volume_);
204 printf("channels:\t\t%-d\n", channels_);
205 printf("balance:\t\t%+1.2f\n\n", balance_);
206
207 printf("waveform:\t\t");
208 if (waveform_ == GLOTTAL_SOURCE_PULSE) {
209 printf("pulse\n");
210 } else if (waveform_ == GLOTTAL_SOURCE_SINE) {
211 printf("sine\n");
212 }
213 printf("tp:\t\t\t%.2f%%\n", tp_);
214 printf("tnMin:\t\t\t%.2f%%\n", tnMin_);
215 printf("tnMax:\t\t\t%.2f%%\n", tnMax_);
216 printf("breathiness:\t\t%.2f%%\n\n", breathiness_);
217
218 printf("nominal tube length:\t%.2f cm\n", length_);
219 printf("temperature:\t\t%.2f degrees C\n", temperature_);
220 printf("lossFactor:\t\t%.2f%%\n\n", lossFactor_);
221
222 printf("apertureRadius:\t\t%.2f cm\n", apertureRadius_);
223 printf("mouthCoef:\t\t%.1f Hz\n", mouthCoef_);
224 printf("noseCoef:\t\t%.1f Hz\n\n", noseCoef_);
225
226 for (int i = 1; i < TOTAL_NASAL_SECTIONS; i++) {
227 printf("n%-d:\t\t\t%.2f cm\n", i, noseRadius_[i]);
228 }
229
230 printf("\nthroatCutoff:\t\t%.1f Hz\n", throatCutoff_);
231 printf("throatVol:\t\t%.2f dB\n\n", throatVol_);
232
233 printf("modulation:\t\t");
234 if (modulation_) {
235 printf("on\n");
236 } else {
237 printf("off\n");
238 }
239 printf("mixOffset:\t\t%.2f dB\n\n", mixOffset_);
240
241 /* PRINT OUT DERIVED VALUES */
242 printf("\nactual tube length:\t%.4f cm\n", actualTubeLength_);
243 printf("internal sample rate:\t%-d Hz\n", sampleRate_);
244 printf("control period:\t\t%-d samples (%.4f seconds)\n\n",
245 controlPeriod_, (float) controlPeriod_ / (float) sampleRate_);
246
247 #if 0
248 /* PRINT OUT WAVE TABLE VALUES */
249 printf("\n");
250 for (int i = 0; i < TABLE_LENGTH; i++)
251 printf("table[%-d] = %.4f\n", i, wavetable[i]);
252 #endif
253
254 /* ECHO TABLE VALUES */
255 printf("\n%-ld control rate input tables:\n\n", inputData_.size() - 1);
256
257 /* HEADER */
258 printf("glPitch");
259 printf("\tglotVol");
260 printf("\taspVol");
261 printf("\tfricVol");
262 printf("\tfricPos");
263 printf("\tfricCF");
264 printf("\tfricBW");
265 for (int i = 1; i <= TOTAL_REGIONS; i++) {
266 printf("\tr%-d", i);
267 }
268 printf("\tvelum\n");
269
270 /* ACTUAL VALUES */
271 for (int i = 0; i < static_cast<int>(inputData_.size()) - 1; ++i) {
272 printf("%.2f" , inputData_[i]->glotPitch);
273 printf("\t%.2f", inputData_[i]->glotVol);
274 printf("\t%.2f", inputData_[i]->aspVol);
275 printf("\t%.2f", inputData_[i]->fricVol);
276 printf("\t%.2f", inputData_[i]->fricPos);
277 printf("\t%.2f", inputData_[i]->fricCF);
278 printf("\t%.2f", inputData_[i]->fricBW);
279 for (int j = 0; j < TOTAL_REGIONS; ++j) {
280 printf("\t%.2f", inputData_[i]->radius[j]);
281 }
282 printf("\t%.2f\n", inputData_[i]->velum);
283 }
284 printf("\n");
285 }
286
287 /******************************************************************************
288 *
289 * function: parseInputStream
290 *
291 * purpose: Parses the input stream and assigns values to global
292 * variables.
293 *
294 ******************************************************************************/
295 void
parseInputStream(std::istream & in)296 Tube::parseInputStream(std::istream& in)
297 {
298 std::string line;
299
300 /* GET THE OUTPUT SAMPLE RATE */
301 if (!std::getline(in, line)) {
302 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read output sample rate.");
303 } else {
304 outputRate_ = Text::parseString<float>(line);
305 }
306
307 /* GET THE INPUT CONTROL RATE */
308 if (!std::getline(in, line)) {
309 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read input control rate.");
310 } else {
311 controlRate_ = Text::parseString<float>(line);
312 }
313
314 /* GET THE MASTER VOLUME */
315 if (!std::getline(in, line)) {
316 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read master volume.");
317 } else {
318 volume_ = Text::parseString<double>(line);
319 }
320
321 /* GET THE NUMBER OF SOUND OUTPUT CHANNELS */
322 if (!std::getline(in, line)) {
323 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read number of sound output channels.");
324 } else {
325 channels_ = Text::parseString<int>(line);
326 }
327
328 /* GET THE STEREO BALANCE */
329 if (!std::getline(in, line)) {
330 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read stereo balance.");
331 } else {
332 balance_ = Text::parseString<double>(line);
333 }
334
335 /* GET THE GLOTTAL SOURCE WAVEFORM TYPE */
336 if (!std::getline(in, line)) {
337 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read glottal source waveform type.");
338 } else {
339 waveform_ = Text::parseString<int>(line);
340 }
341
342 /* GET THE GLOTTAL PULSE RISE TIME (tp) */
343 if (!std::getline(in, line)) {
344 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read glottal pulse rise time (tp).");
345 } else {
346 tp_ = Text::parseString<double>(line);
347 }
348
349 /* GET THE GLOTTAL PULSE FALL TIME MINIMUM (tnMin) */
350 if (!std::getline(in, line)) {
351 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read glottal pulse fall time minimum (tnMin).");
352 } else {
353 tnMin_ = Text::parseString<double>(line);
354 }
355
356 /* GET THE GLOTTAL PULSE FALL TIME MAXIMUM (tnMax) */
357 if (!std::getline(in, line)) {
358 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read glottal pulse fall time maximum (tnMax).");
359 } else {
360 tnMax_ = Text::parseString<double>(line);
361 }
362
363 /* GET THE GLOTTAL SOURCE BREATHINESS */
364 if (!std::getline(in, line)) {
365 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read glottal source breathiness.");
366 } else {
367 breathiness_ = Text::parseString<double>(line);
368 }
369
370 /* GET THE NOMINAL TUBE LENGTH */
371 if (!std::getline(in, line)) {
372 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read nominal tube length.");
373 } else {
374 length_ = Text::parseString<double>(line);
375 }
376
377 /* GET THE TUBE TEMPERATURE */
378 if (!std::getline(in, line)) {
379 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read tube temperature.");
380 } else {
381 temperature_ = Text::parseString<double>(line);
382 }
383
384 /* GET THE JUNCTION LOSS FACTOR */
385 if (!std::getline(in, line)) {
386 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read junction loss factor.");
387 } else {
388 lossFactor_ = Text::parseString<double>(line);
389 }
390
391 /* GET THE APERTURE SCALING RADIUS */
392 if (!std::getline(in, line)) {
393 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read aperture scaling radius.");
394 } else {
395 apertureRadius_ = Text::parseString<double>(line);
396 }
397
398 /* GET THE MOUTH APERTURE COEFFICIENT */
399 if (!std::getline(in, line)) {
400 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read mouth aperture coefficient.");
401 } else {
402 mouthCoef_ = Text::parseString<double>(line);
403 }
404
405 /* GET THE NOSE APERTURE COEFFICIENT */
406 if (!std::getline(in, line)) {
407 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read nose aperture coefficient.");
408 } else {
409 noseCoef_ = Text::parseString<double>(line);
410 }
411
412 /* GET THE NOSE RADII */
413 noseRadius_[0] = 0.0;
414 for (int i = 1; i < TOTAL_NASAL_SECTIONS; i++) {
415 if (!std::getline(in, line)) {
416 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read nose radius " << i << '.');
417 } else {
418 noseRadius_[i] = std::max(Text::parseString<double>(line), GS_TRM_TUBE_MIN_RADIUS);
419 }
420 }
421
422 /* GET THE THROAT LOWPASS FREQUENCY CUTOFF */
423 if (!std::getline(in, line)) {
424 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read throat lowpass filter cutoff.");
425 } else {
426 throatCutoff_ = Text::parseString<double>(line);
427 }
428
429 /* GET THE THROAT VOLUME */
430 if (!std::getline(in, line)) {
431 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read throat volume.");
432 } else {
433 throatVol_ = Text::parseString<double>(line);
434 }
435
436 /* GET THE PULSE MODULATION OF NOISE FLAG */
437 if (!std::getline(in, line)) {
438 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read pulse modulation of noise flag.");
439 } else {
440 modulation_ = Text::parseString<int>(line);
441 }
442
443 /* GET THE NOISE CROSSMIX OFFSET */
444 if (!std::getline(in, line)) {
445 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read noise crossmix offset.");
446 } else {
447 mixOffset_ = Text::parseString<double>(line);
448 }
449
450 /* GET THE INPUT TABLE VALUES */
451 unsigned int paramNumber = 0;
452 while (std::getline(in, line)) {
453 std::istringstream lineStream(line);
454 std::unique_ptr<InputData> data(new InputData());
455
456 /* GET EACH PARAMETER */
457 lineStream >>
458 data->glotPitch >>
459 data->glotVol >>
460 data->aspVol >>
461 data->fricVol >>
462 data->fricPos >>
463 data->fricCF >>
464 data->fricBW;
465 for (int i = 0; i < TOTAL_REGIONS; i++) {
466 double radius;
467 lineStream >> radius;
468 data->radius[i] = std::max(radius, GS_TRM_TUBE_MIN_RADIUS);
469 }
470 lineStream >> data->velum;
471
472 if (!lineStream) {
473 THROW_EXCEPTION(TRMException, "Error in tube input parsing: Could not read parameters (number " << paramNumber << ").");
474 }
475
476 inputData_.push_back(std::move(data));
477 ++paramNumber;
478 }
479
480 /* DOUBLE UP THE LAST INPUT TABLE, TO HELP INTERPOLATION CALCULATIONS */
481 if (!inputData_.empty()) {
482 std::unique_ptr<InputData> lastData(new InputData());
483 *lastData = *inputData_.back();
484 inputData_.push_back(std::move(lastData));
485 }
486 }
487
488 /******************************************************************************
489 *
490 * function: speedOfSound
491 *
492 * purpose: Returns the speed of sound according to the value of
493 * the temperature (in Celsius degrees).
494 *
495 ******************************************************************************/
496 double
speedOfSound(double temperature)497 Tube::speedOfSound(double temperature)
498 {
499 return 331.4 + (0.6 * temperature);
500 }
501
502 /******************************************************************************
503 *
504 * function: initializeSynthesizer
505 *
506 * purpose: Initializes all variables so that the synthesis can
507 * be run.
508 *
509 ******************************************************************************/
510 void
initializeSynthesizer()511 Tube::initializeSynthesizer()
512 {
513 double nyquist;
514
515 /* CALCULATE THE SAMPLE RATE, BASED ON NOMINAL TUBE LENGTH AND SPEED OF SOUND */
516 if (length_ > 0.0) {
517 double c = speedOfSound(temperature_);
518 controlPeriod_ = static_cast<int>(rint((c * TOTAL_SECTIONS * 100.0) / (length_ * controlRate_)));
519 sampleRate_ = static_cast<int>(controlRate_ * controlPeriod_);
520 actualTubeLength_ = (c * TOTAL_SECTIONS * 100.0) / sampleRate_;
521 nyquist = sampleRate_ / 2.0;
522 } else {
523 THROW_EXCEPTION(TRMException, "Illegal tube length.\n");
524 }
525
526 /* CALCULATE THE BREATHINESS FACTOR */
527 breathinessFactor_ = breathiness_ / 100.0;
528
529 /* CALCULATE CROSSMIX FACTOR */
530 crossmixFactor_ = 1.0 / amplitude(mixOffset_);
531
532 /* CALCULATE THE DAMPING FACTOR */
533 dampingFactor_ = (1.0 - (lossFactor_ / 100.0));
534
535 /* INITIALIZE THE WAVE TABLE */
536 glottalSource_.reset(new WavetableGlottalSource(
537 waveform_ == GLOTTAL_SOURCE_PULSE ?
538 WavetableGlottalSource::TYPE_PULSE :
539 WavetableGlottalSource::TYPE_SINE,
540 sampleRate_,
541 tp_, tnMin_, tnMax_));
542
543 /* INITIALIZE REFLECTION AND RADIATION FILTER COEFFICIENTS FOR MOUTH */
544 double mouthApertureCoeff = (nyquist - mouthCoef_) / nyquist;
545 mouthRadiationFilter_.reset(new RadiationFilter(mouthApertureCoeff));
546 mouthReflectionFilter_.reset(new ReflectionFilter(mouthApertureCoeff));
547
548 /* INITIALIZE REFLECTION AND RADIATION FILTER COEFFICIENTS FOR NOSE */
549 double nasalApertureCoeff = (nyquist - noseCoef_) / nyquist;
550 nasalRadiationFilter_.reset(new RadiationFilter(nasalApertureCoeff));
551 nasalReflectionFilter_.reset(new ReflectionFilter(nasalApertureCoeff));
552
553 /* INITIALIZE NASAL CAVITY FIXED SCATTERING COEFFICIENTS */
554 initializeNasalCavity();
555
556 /* INITIALIZE THE THROAT LOWPASS FILTER */
557 throat_.reset(new Throat(sampleRate_, throatCutoff_, amplitude(throatVol_)));
558
559 /* INITIALIZE THE SAMPLE RATE CONVERSION ROUTINES */
560 srConv_.reset(new SampleRateConverter(sampleRate_, outputRate_, outputData_));
561
562 /* INITIALIZE THE OUTPUT VECTOR */
563 outputData_.clear();
564
565 bandpassFilter_.reset(new BandpassFilter());
566 noiseFilter_.reset(new NoiseFilter());
567 noiseSource_.reset(new NoiseSource());
568 }
569
570 void
initializeInputFilters(double period)571 Tube::initializeInputFilters(double period)
572 {
573 inputFilters_.reset(new InputFilters(sampleRate_, period));
574 }
575
576 /******************************************************************************
577 *
578 * function: synthesize
579 *
580 * purpose: Performs the actual synthesis of sound samples.
581 *
582 ******************************************************************************/
583 void
synthesizeForInputSequence()584 Tube::synthesizeForInputSequence()
585 {
586 /* CONTROL RATE LOOP */
587 for (int i = 1, size = inputData_.size(); i < size; i++) {
588 /* SET CONTROL RATE PARAMETERS FROM INPUT TABLES */
589 setControlRateParameters(i);
590
591 /* SAMPLE RATE LOOP */
592 for (int j = 0; j < controlPeriod_; j++) {
593 synthesize();
594
595 /* DO SAMPLE RATE INTERPOLATION OF CONTROL PARAMETERS */
596 sampleRateInterpolation();
597 }
598 }
599 }
600
601 void
synthesizeForSingleInput(int numIterations)602 Tube::synthesizeForSingleInput(int numIterations)
603 {
604 if (!inputFilters_) {
605 THROW_EXCEPTION(InvalidStateException, "Input filters have not been initialized.");
606 }
607
608 for (int i = 0; i < numIterations; ++i) {
609
610 currentData_.glotPitch = inputFilters_->glotPitchFilter.filter(singleInput_.glotPitch);
611 currentData_.glotVol = inputFilters_->glotVolFilter.filter( singleInput_.glotVol);
612 currentData_.aspVol = inputFilters_->aspVolFilter.filter( singleInput_.aspVol);
613 currentData_.fricVol = inputFilters_->fricVolFilter.filter( singleInput_.fricVol);
614 currentData_.fricPos = inputFilters_->fricPosFilter.filter( singleInput_.fricPos);
615 currentData_.fricCF = inputFilters_->fricCFFilter.filter( singleInput_.fricCF);
616 currentData_.fricBW = inputFilters_->fricBWFilter.filter( singleInput_.fricBW);
617 currentData_.radius[0] = inputFilters_->radius0Filter.filter( singleInput_.radius[0]);
618 currentData_.radius[1] = inputFilters_->radius1Filter.filter( singleInput_.radius[1]);
619 currentData_.radius[2] = inputFilters_->radius2Filter.filter( singleInput_.radius[2]);
620 currentData_.radius[3] = inputFilters_->radius3Filter.filter( singleInput_.radius[3]);
621 currentData_.radius[4] = inputFilters_->radius4Filter.filter( singleInput_.radius[4]);
622 currentData_.radius[5] = inputFilters_->radius5Filter.filter( singleInput_.radius[5]);
623 currentData_.radius[6] = inputFilters_->radius6Filter.filter( singleInput_.radius[6]);
624 currentData_.radius[7] = inputFilters_->radius7Filter.filter( singleInput_.radius[7]);
625 currentData_.velum = inputFilters_->velumFilter.filter( singleInput_.velum);
626
627 synthesize();
628 }
629 }
630
631 void
synthesize()632 Tube::synthesize()
633 {
634 /* CONVERT PARAMETERS HERE */
635 double f0 = frequency(currentData_.glotPitch);
636 double ax = amplitude(currentData_.glotVol);
637 double ah1 = amplitude(currentData_.aspVol);
638 calculateTubeCoefficients();
639 setFricationTaps();
640 bandpassFilter_->update(sampleRate_, currentData_.fricBW, currentData_.fricCF);
641
642 /* DO SYNTHESIS HERE */
643 /* CREATE LOW-PASS FILTERED NOISE */
644 double lpNoise = noiseFilter_->filter(noiseSource_->getSample());
645
646 /* UPDATE THE SHAPE OF THE GLOTTAL PULSE, IF NECESSARY */
647 if (waveform_ == GLOTTAL_SOURCE_PULSE) {
648 if (ax != prevGlotAmplitude_) {
649 glottalSource_->updateWavetable(ax);
650 }
651 }
652
653 /* CREATE GLOTTAL PULSE (OR SINE TONE) */
654 double pulse = glottalSource_->getSample(f0);
655
656 /* CREATE PULSED NOISE */
657 double pulsedNoise = lpNoise * pulse;
658
659 /* CREATE NOISY GLOTTAL PULSE */
660 pulse = ax * ((pulse * (1.0 - breathinessFactor_)) +
661 (pulsedNoise * breathinessFactor_));
662
663 double signal;
664 /* CROSS-MIX PURE NOISE WITH PULSED NOISE */
665 if (modulation_) {
666 double crossmix = ax * crossmixFactor_;
667 crossmix = (crossmix < 1.0) ? crossmix : 1.0;
668 signal = (pulsedNoise * crossmix) +
669 (lpNoise * (1.0 - crossmix));
670 } else {
671 signal = lpNoise;
672 }
673
674 /* PUT SIGNAL THROUGH VOCAL TRACT */
675 signal = vocalTract(((pulse + (ah1 * signal)) * VT_SCALE),
676 bandpassFilter_->filter(signal));
677
678 /* PUT PULSE THROUGH THROAT */
679 signal += throat_->process(pulse * VT_SCALE);
680
681 /* OUTPUT SAMPLE HERE */
682 srConv_->dataFill(signal);
683
684 prevGlotAmplitude_ = ax;
685 }
686
687 /******************************************************************************
688 *
689 * function: setControlRateParameters
690 *
691 * purpose: Calculates the current table values, and their
692 * associated sample-to-sample delta values.
693 *
694 ******************************************************************************/
695 void
setControlRateParameters(int pos)696 Tube::setControlRateParameters(int pos)
697 {
698 double controlFreq = 1.0 / controlPeriod_;
699
700 /* GLOTTAL PITCH */
701 currentData_.glotPitch = inputData_[pos - 1]->glotPitch;
702 currentData_.glotPitchDelta = (inputData_[pos]->glotPitch - currentData_.glotPitch) * controlFreq;
703
704 /* GLOTTAL VOLUME */
705 currentData_.glotVol = inputData_[pos - 1]->glotVol;
706 currentData_.glotVolDelta = (inputData_[pos]->glotVol - currentData_.glotVol) * controlFreq;
707
708 /* ASPIRATION VOLUME */
709 currentData_.aspVol = inputData_[pos - 1]->aspVol;
710 #if MATCH_DSP
711 currentData_.aspVolDelta = 0.0;
712 #else
713 currentData_.aspVolDelta = (inputData_[pos]->aspVol - currentData_.aspVol) * controlFreq;
714 #endif
715
716 /* FRICATION VOLUME */
717 currentData_.fricVol = inputData_[pos - 1]->fricVol;
718 #if MATCH_DSP
719 currentData_.fricVolDelta = 0.0;
720 #else
721 currentData_.fricVolDelta = (inputData_[pos]->fricVol - currentData_.fricVol) * controlFreq;
722 #endif
723
724 /* FRICATION POSITION */
725 currentData_.fricPos = inputData_[pos - 1]->fricPos;
726 #if MATCH_DSP
727 currentData_.fricPosDelta = 0.0;
728 #else
729 currentData_.fricPosDelta = (inputData_[pos]->fricPos - currentData_.fricPos) * controlFreq;
730 #endif
731
732 /* FRICATION CENTER FREQUENCY */
733 currentData_.fricCF = inputData_[pos - 1]->fricCF;
734 #if MATCH_DSP
735 currentData_.fricCFDelta = 0.0;
736 #else
737 currentData_.fricCFDelta = (inputData_[pos]->fricCF - currentData_.fricCF) * controlFreq;
738 #endif
739
740 /* FRICATION BANDWIDTH */
741 currentData_.fricBW = inputData_[pos - 1]->fricBW;
742 #if MATCH_DSP
743 currentData_.fricBWDelta = 0.0;
744 #else
745 currentData_.fricBWDelta = (inputData_[pos]->fricBW - currentData_.fricBW) * controlFreq;
746 #endif
747
748 /* TUBE REGION RADII */
749 for (int i = 0; i < TOTAL_REGIONS; i++) {
750 currentData_.radius[i] = inputData_[pos - 1]->radius[i];
751 currentData_.radiusDelta[i] = (inputData_[pos]->radius[i] - currentData_.radius[i]) * controlFreq;
752 }
753
754 /* VELUM RADIUS */
755 currentData_.velum = inputData_[pos - 1]->velum;
756 currentData_.velumDelta = (inputData_[pos]->velum - currentData_.velum) * controlFreq;
757 }
758
759 /******************************************************************************
760 *
761 * function: sampleRateInterpolation
762 *
763 * purpose: Interpolates table values at the sample rate.
764 *
765 ******************************************************************************/
766 void
sampleRateInterpolation()767 Tube::sampleRateInterpolation()
768 {
769 currentData_.glotPitch += currentData_.glotPitchDelta;
770 currentData_.glotVol += currentData_.glotVolDelta;
771 currentData_.aspVol += currentData_.aspVolDelta;
772 currentData_.fricVol += currentData_.fricVolDelta;
773 currentData_.fricPos += currentData_.fricPosDelta;
774 currentData_.fricCF += currentData_.fricCFDelta;
775 currentData_.fricBW += currentData_.fricBWDelta;
776 for (int i = 0; i < TOTAL_REGIONS; i++) {
777 currentData_.radius[i] += currentData_.radiusDelta[i];
778 }
779 currentData_.velum += currentData_.velumDelta;
780 }
781
782 /******************************************************************************
783 *
784 * function: initializeNasalCavity
785 *
786 * purpose: Calculates the scattering coefficients for the fixed
787 * sections of the nasal cavity.
788 *
789 ******************************************************************************/
790 void
initializeNasalCavity()791 Tube::initializeNasalCavity()
792 {
793 double radA2, radB2;
794
795 /* CALCULATE COEFFICIENTS FOR INTERNAL FIXED SECTIONS OF NASAL CAVITY */
796 for (int i = N2, j = NC2; i < N6; i++, j++) {
797 radA2 = noseRadius_[i] * noseRadius_[i];
798 radB2 = noseRadius_[i + 1] * noseRadius_[i + 1];
799 nasalCoeff_[j] = (radA2 - radB2) / (radA2 + radB2);
800 }
801
802 /* CALCULATE THE FIXED COEFFICIENT FOR THE NOSE APERTURE */
803 radA2 = noseRadius_[N6] * noseRadius_[N6];
804 radB2 = apertureRadius_ * apertureRadius_;
805 nasalCoeff_[NC6] = (radA2 - radB2) / (radA2 + radB2);
806 }
807
808
809 /******************************************************************************
810 *
811 * function: calculateTubeCoefficients
812 *
813 * purpose: Calculates the scattering coefficients for the vocal
814 * ract according to the current radii. Also calculates
815 * the coefficients for the reflection/radiation filter
816 * pair for the mouth and nose.
817 *
818 ******************************************************************************/
819 void
calculateTubeCoefficients()820 Tube::calculateTubeCoefficients()
821 {
822 double radA2, radB2, r0_2, r1_2, r2_2, sum;
823
824 /* CALCULATE COEFFICIENTS FOR THE OROPHARYNX */
825 for (int i = 0; i < (TOTAL_REGIONS - 1); i++) {
826 radA2 = currentData_.radius[i] * currentData_.radius[i];
827 radB2 = currentData_.radius[i + 1] * currentData_.radius[i + 1];
828 oropharynxCoeff_[i] = (radA2 - radB2) / (radA2 + radB2);
829 }
830
831 /* CALCULATE THE COEFFICIENT FOR THE MOUTH APERTURE */
832 radA2 = currentData_.radius[R8] * currentData_.radius[R8];
833 radB2 = apertureRadius_ * apertureRadius_;
834 oropharynxCoeff_[C8] = (radA2 - radB2) / (radA2 + radB2);
835
836 /* CALCULATE ALPHA COEFFICIENTS FOR 3-WAY JUNCTION */
837 /* NOTE: SINCE JUNCTION IS IN MIDDLE OF REGION 4, r0_2 = r1_2 */
838 r0_2 = r1_2 = currentData_.radius[R4] * currentData_.radius[R4];
839 r2_2 = currentData_.velum * currentData_.velum;
840 sum = 2.0 / (r0_2 + r1_2 + r2_2);
841 alpha_[LEFT] = sum * r0_2;
842 alpha_[RIGHT] = sum * r1_2;
843 alpha_[UPPER] = sum * r2_2;
844
845 /* AND 1ST NASAL PASSAGE COEFFICIENT */
846 radA2 = currentData_.velum * currentData_.velum;
847 radB2 = noseRadius_[N2] * noseRadius_[N2];
848 nasalCoeff_[NC1] = (radA2 - radB2) / (radA2 + radB2);
849 }
850
851 /******************************************************************************
852 *
853 * function: setFricationTaps
854 *
855 * purpose: Sets the frication taps according to the current
856 * position and amplitude of frication.
857 *
858 ******************************************************************************/
859 void
setFricationTaps()860 Tube::setFricationTaps()
861 {
862 int integerPart;
863 double complement, remainder;
864 double fricationAmplitude = amplitude(currentData_.fricVol);
865
866 /* CALCULATE POSITION REMAINDER AND COMPLEMENT */
867 integerPart = (int) currentData_.fricPos;
868 complement = currentData_.fricPos - (double) integerPart;
869 remainder = 1.0 - complement;
870
871 /* SET THE FRICATION TAPS */
872 for (int i = FC1; i < TOTAL_FRIC_COEFFICIENTS; i++) {
873 if (i == integerPart) {
874 fricationTap_[i] = remainder * fricationAmplitude;
875 if ((i + 1) < TOTAL_FRIC_COEFFICIENTS) {
876 fricationTap_[++i] = complement * fricationAmplitude;
877 }
878 } else {
879 fricationTap_[i] = 0.0;
880 }
881 }
882
883 #if 0
884 /* PRINT OUT */
885 printf("fricationTaps: ");
886 for (i = FC1; i < TOTAL_FRIC_COEFFICIENTS; i++)
887 printf("%.6f ", fricationTap[i]);
888 printf("\n");
889 #endif
890 }
891
892 /******************************************************************************
893 *
894 * function: vocalTract
895 *
896 * purpose: Updates the pressure wave throughout the vocal tract,
897 * and returns the summed output of the oral and nasal
898 * cavities. Also injects frication appropriately.
899 *
900 ******************************************************************************/
901 double
vocalTract(double input,double frication)902 Tube::vocalTract(double input, double frication)
903 {
904 int i, j, k;
905 double delta, output, junctionPressure;
906
907 /* INCREMENT CURRENT AND PREVIOUS POINTERS */
908 if (++currentPtr_ > 1) {
909 currentPtr_ = 0;
910 }
911 if (++prevPtr_ > 1) {
912 prevPtr_ = 0;
913 }
914
915 /* UPDATE OROPHARYNX */
916 /* INPUT TO TOP OF TUBE */
917 oropharynx_[S1][TOP][currentPtr_] =
918 (oropharynx_[S1][BOTTOM][prevPtr_] * dampingFactor_) + input;
919
920 /* CALCULATE THE SCATTERING JUNCTIONS FOR S1-S2 */
921 delta = oropharynxCoeff_[C1] *
922 (oropharynx_[S1][TOP][prevPtr_] - oropharynx_[S2][BOTTOM][prevPtr_]);
923 oropharynx_[S2][TOP][currentPtr_] =
924 (oropharynx_[S1][TOP][prevPtr_] + delta) * dampingFactor_;
925 oropharynx_[S1][BOTTOM][currentPtr_] =
926 (oropharynx_[S2][BOTTOM][prevPtr_] + delta) * dampingFactor_;
927
928 /* CALCULATE THE SCATTERING JUNCTIONS FOR S2-S3 AND S3-S4 */
929 for (i = S2, j = C2, k = FC1; i < S4; i++, j++, k++) {
930 delta = oropharynxCoeff_[j] *
931 (oropharynx_[i][TOP][prevPtr_] - oropharynx_[i + 1][BOTTOM][prevPtr_]);
932 oropharynx_[i + 1][TOP][currentPtr_] =
933 ((oropharynx_[i][TOP][prevPtr_] + delta) * dampingFactor_) +
934 (fricationTap_[k] * frication);
935 oropharynx_[i][BOTTOM][currentPtr_] =
936 (oropharynx_[i + 1][BOTTOM][prevPtr_] + delta) * dampingFactor_;
937 }
938
939 /* UPDATE 3-WAY JUNCTION BETWEEN THE MIDDLE OF R4 AND NASAL CAVITY */
940 junctionPressure = (alpha_[LEFT] * oropharynx_[S4][TOP][prevPtr_])+
941 (alpha_[RIGHT] * oropharynx_[S5][BOTTOM][prevPtr_]) +
942 (alpha_[UPPER] * nasal_[VELUM][BOTTOM][prevPtr_]);
943 oropharynx_[S4][BOTTOM][currentPtr_] =
944 (junctionPressure - oropharynx_[S4][TOP][prevPtr_]) * dampingFactor_;
945 oropharynx_[S5][TOP][currentPtr_] =
946 ((junctionPressure - oropharynx_[S5][BOTTOM][prevPtr_]) * dampingFactor_)
947 + (fricationTap_[FC3] * frication);
948 nasal_[VELUM][TOP][currentPtr_] =
949 (junctionPressure - nasal_[VELUM][BOTTOM][prevPtr_]) * dampingFactor_;
950
951 /* CALCULATE JUNCTION BETWEEN R4 AND R5 (S5-S6) */
952 delta = oropharynxCoeff_[C4] *
953 (oropharynx_[S5][TOP][prevPtr_] - oropharynx_[S6][BOTTOM][prevPtr_]);
954 oropharynx_[S6][TOP][currentPtr_] =
955 ((oropharynx_[S5][TOP][prevPtr_] + delta) * dampingFactor_) +
956 (fricationTap_[FC4] * frication);
957 oropharynx_[S5][BOTTOM][currentPtr_] =
958 (oropharynx_[S6][BOTTOM][prevPtr_] + delta) * dampingFactor_;
959
960 /* CALCULATE JUNCTION INSIDE R5 (S6-S7) (PURE DELAY WITH DAMPING) */
961 oropharynx_[S7][TOP][currentPtr_] =
962 (oropharynx_[S6][TOP][prevPtr_] * dampingFactor_) +
963 (fricationTap_[FC5] * frication);
964 oropharynx_[S6][BOTTOM][currentPtr_] =
965 oropharynx_[S7][BOTTOM][prevPtr_] * dampingFactor_;
966
967 /* CALCULATE LAST 3 INTERNAL JUNCTIONS (S7-S8, S8-S9, S9-S10) */
968 for (i = S7, j = C5, k = FC6; i < S10; i++, j++, k++) {
969 delta = oropharynxCoeff_[j] *
970 (oropharynx_[i][TOP][prevPtr_] - oropharynx_[i + 1][BOTTOM][prevPtr_]);
971 oropharynx_[i + 1][TOP][currentPtr_] =
972 ((oropharynx_[i][TOP][prevPtr_] + delta) * dampingFactor_) +
973 (fricationTap_[k] * frication);
974 oropharynx_[i][BOTTOM][currentPtr_] =
975 (oropharynx_[i + 1][BOTTOM][prevPtr_] + delta) * dampingFactor_;
976 }
977
978 /* REFLECTED SIGNAL AT MOUTH GOES THROUGH A LOWPASS FILTER */
979 oropharynx_[S10][BOTTOM][currentPtr_] = dampingFactor_ *
980 mouthReflectionFilter_->filter(oropharynxCoeff_[C8] *
981 oropharynx_[S10][TOP][prevPtr_]);
982
983 /* OUTPUT FROM MOUTH GOES THROUGH A HIGHPASS FILTER */
984 output = mouthRadiationFilter_->filter((1.0 + oropharynxCoeff_[C8]) *
985 oropharynx_[S10][TOP][prevPtr_]);
986
987 /* UPDATE NASAL CAVITY */
988 for (i = VELUM, j = NC1; i < N6; i++, j++) {
989 delta = nasalCoeff_[j] *
990 (nasal_[i][TOP][prevPtr_] - nasal_[i + 1][BOTTOM][prevPtr_]);
991 nasal_[i+1][TOP][currentPtr_] =
992 (nasal_[i][TOP][prevPtr_] + delta) * dampingFactor_;
993 nasal_[i][BOTTOM][currentPtr_] =
994 (nasal_[i + 1][BOTTOM][prevPtr_] + delta) * dampingFactor_;
995 }
996
997 /* REFLECTED SIGNAL AT NOSE GOES THROUGH A LOWPASS FILTER */
998 nasal_[N6][BOTTOM][currentPtr_] = dampingFactor_ *
999 nasalReflectionFilter_->filter(nasalCoeff_[NC6] * nasal_[N6][TOP][prevPtr_]);
1000
1001 /* OUTPUT FROM NOSE GOES THROUGH A HIGHPASS FILTER */
1002 output += nasalRadiationFilter_->filter((1.0 + nasalCoeff_[NC6]) *
1003 nasal_[N6][TOP][prevPtr_]);
1004 /* RETURN SUMMED OUTPUT FROM MOUTH AND NOSE */
1005 return output;
1006 }
1007
1008 /******************************************************************************
1009 *
1010 * function: writeOutputToFile
1011 *
1012 * purpose: Scales the samples stored in the temporary file, and
1013 * writes them to the output file, with the appropriate
1014 * header. Also does master volume scaling, and stereo
1015 * balance scaling, if 2 channels of output.
1016 *
1017 ******************************************************************************/
1018 void
writeOutputToFile(const char * outputFile)1019 Tube::writeOutputToFile(const char* outputFile)
1020 {
1021 /* BE SURE TO FLUSH SRC BUFFER */
1022 srConv_->flushBuffer();
1023
1024 LOG_DEBUG("\nNumber of samples: " << srConv_->numberSamples() <<
1025 "\nMaximum sample value: " << srConv_->maximumSampleValue());
1026
1027 WAVEFileWriter fileWriter(outputFile, channels_, srConv_->numberSamples(), outputRate_);
1028
1029 if (channels_ == 1) {
1030 float scale = calculateMonoScale();
1031 for (unsigned int i = 0, end = srConv_->numberSamples(); i < end; ++i) {
1032 fileWriter.writeSample(outputData_[i] * scale);
1033 }
1034 } else {
1035 float leftScale, rightScale;
1036 calculateStereoScale(leftScale, rightScale);
1037 for (unsigned int i = 0, end = srConv_->numberSamples(); i < end; ++i) {
1038 fileWriter.writeStereoSamples(outputData_[i] * leftScale, outputData_[i] * rightScale);
1039 }
1040 }
1041 }
1042
1043 void
writeOutputToBuffer(std::vector<float> & outputBuffer)1044 Tube::writeOutputToBuffer(std::vector<float>& outputBuffer)
1045 {
1046 /* BE SURE TO FLUSH SRC BUFFER */
1047 srConv_->flushBuffer();
1048
1049 LOG_DEBUG("\nNumber of samples: " << srConv_->numberSamples() <<
1050 "\nMaximum sample value: " << srConv_->maximumSampleValue());
1051
1052 outputBuffer.resize(srConv_->numberSamples() * channels_);
1053
1054 if (channels_ == 1) {
1055 float scale = calculateMonoScale();
1056 for (unsigned int i = 0, end = srConv_->numberSamples(); i < end; ++i) {
1057 outputBuffer[i] = outputData_[i] * scale;
1058 }
1059 } else {
1060 float leftScale, rightScale;
1061 calculateStereoScale(leftScale, rightScale);
1062 for (unsigned int i = 0, end = srConv_->numberSamples(); i < end; ++i) {
1063 unsigned int baseIndex = i * 2;
1064 outputBuffer[baseIndex ] = outputData_[i] * leftScale;
1065 outputBuffer[baseIndex + 1] = outputData_[i] * rightScale;
1066 }
1067 }
1068 }
1069
1070 float
calculateMonoScale()1071 Tube::calculateMonoScale()
1072 {
1073 float scale = static_cast<float>((OUTPUT_SCALE / srConv_->maximumSampleValue()) * amplitude(volume_));
1074 LOG_DEBUG("\nScale: " << scale << '\n');
1075 return scale;
1076 }
1077
1078 void
calculateStereoScale(float & leftScale,float & rightScale)1079 Tube::calculateStereoScale(float& leftScale, float& rightScale)
1080 {
1081 leftScale = static_cast<float>(-((balance_ / 2.0) - 0.5));
1082 rightScale = static_cast<float>(((balance_ / 2.0) + 0.5));
1083 float newMax = static_cast<float>(srConv_->maximumSampleValue() * (balance_ > 0.0 ? rightScale : leftScale));
1084 float scale = static_cast<float>((OUTPUT_SCALE / newMax) * amplitude(volume_));
1085 leftScale *= scale;
1086 rightScale *= scale;
1087 LOG_DEBUG("\nLeft scale: " << leftScale << " Right scale: " << rightScale << '\n');
1088 }
1089
1090 /******************************************************************************
1091 *
1092 * function: amplitude
1093 *
1094 * purpose: Converts dB value to amplitude value.
1095 *
1096 ******************************************************************************/
1097 double
amplitude(double decibelLevel)1098 Tube::amplitude(double decibelLevel)
1099 {
1100 /* CONVERT 0-60 RANGE TO -60-0 RANGE */
1101 decibelLevel -= VOL_MAX;
1102
1103 /* IF -60 OR LESS, RETURN AMPLITUDE OF 0 */
1104 if (decibelLevel <= (-VOL_MAX)) {
1105 return 0.0;
1106 }
1107
1108 /* IF 0 OR GREATER, RETURN AMPLITUDE OF 1 */
1109 if (decibelLevel >= 0.0) {
1110 return 1.0;
1111 }
1112
1113 /* ELSE RETURN INVERSE LOG VALUE */
1114 return pow(10.0, decibelLevel / 20.0);
1115 }
1116
1117 /******************************************************************************
1118 *
1119 * function: frequency
1120 *
1121 * purpose: Converts a given pitch (0 = middle C) to the
1122 * corresponding frequency.
1123 *
1124 ******************************************************************************/
1125 double
frequency(double pitch)1126 Tube::frequency(double pitch)
1127 {
1128 return PITCH_BASE * pow(2.0, (pitch + PITCH_OFFSET) / 12.0);
1129 }
1130
1131 void
loadSingleInput(const VocalTractModelParameterValue pv)1132 Tube::loadSingleInput(const VocalTractModelParameterValue pv)
1133 {
1134 switch (pv.index) {
1135 case PARAM_GLOT_PITCH:
1136 singleInput_.glotPitch = pv.value;
1137 break;
1138 case PARAM_GLOT_VOL:
1139 singleInput_.glotVol = pv.value;
1140 break;
1141 case PARAM_ASP_VOL:
1142 singleInput_.aspVol = pv.value;
1143 break;
1144 case PARAM_FRIC_VOL:
1145 singleInput_.fricVol = pv.value;
1146 break;
1147 case PARAM_FRIC_POS:
1148 singleInput_.fricPos = pv.value;
1149 break;
1150 case PARAM_FRIC_CF:
1151 singleInput_.fricCF = pv.value;
1152 break;
1153 case PARAM_FRIC_BW:
1154 singleInput_.fricBW = pv.value;
1155 break;
1156 case PARAM_R1:
1157 singleInput_.radius[0] = std::max(pv.value, GS_TRM_TUBE_MIN_RADIUS);
1158 break;
1159 case PARAM_R2:
1160 singleInput_.radius[1] = std::max(pv.value, GS_TRM_TUBE_MIN_RADIUS);
1161 break;
1162 case PARAM_R3:
1163 singleInput_.radius[2] = std::max(pv.value, GS_TRM_TUBE_MIN_RADIUS);
1164 break;
1165 case PARAM_R4:
1166 singleInput_.radius[3] = std::max(pv.value, GS_TRM_TUBE_MIN_RADIUS);
1167 break;
1168 case PARAM_R5:
1169 singleInput_.radius[4] = std::max(pv.value, GS_TRM_TUBE_MIN_RADIUS);
1170 break;
1171 case PARAM_R6:
1172 singleInput_.radius[5] = std::max(pv.value, GS_TRM_TUBE_MIN_RADIUS);
1173 break;
1174 case PARAM_R7:
1175 singleInput_.radius[6] = std::max(pv.value, GS_TRM_TUBE_MIN_RADIUS);
1176 break;
1177 case PARAM_R8:
1178 singleInput_.radius[7] = std::max(pv.value, GS_TRM_TUBE_MIN_RADIUS);
1179 break;
1180 case PARAM_VELUM:
1181 singleInput_.velum = pv.value;
1182 break;
1183 default:
1184 THROW_EXCEPTION(TRMException, "Invalid parameter index: " << pv.index << '.');
1185 }
1186 }
1187
1188 } /* namespace TRM */
1189 } /* namespace GS */
1190