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(&currentData_, 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