Spaces:
Running
Running
| /* Smalltalk from Squeak4.5 with VMMaker 4.13.6 translated as JS source on 3 November 2014 1:52:21 pm */ | |
| /* Automatically generated by | |
| JSPluginCodeGenerator VMMakerJS-bf.15 uuid: fd4e10f2-3773-4e80-8bb5-c4b471a014e5 | |
| from | |
| KlattSynthesizerPlugin VMMaker-bf.353 uuid: 8ae25e7e-8d2c-451e-8277-598b30e9c002 | |
| */ | |
| (function Klatt() { | |
| ; | |
| var VM_PROXY_MAJOR = 1; | |
| var VM_PROXY_MINOR = 11; | |
| /*** Functions ***/ | |
| function CLASSOF(obj) { return typeof obj === "number" ? interpreterProxy.classSmallInteger() : obj.sqClass } | |
| function SIZEOF(obj) { return obj.pointers ? obj.pointers.length : obj.words ? obj.words.length : obj.bytes ? obj.bytes.length : 0 } | |
| function BYTESIZEOF(obj) { return obj.bytes ? obj.bytes.length : obj.words ? obj.words.length * 4 : 0 } | |
| function DIV(a, b) { return Math.floor(a / b) | 0; } // integer division | |
| function MOD(a, b) { return a - DIV(a, b) * b | 0; } // signed modulus | |
| function SHL(a, b) { return b > 31 ? 0 : a << b; } // fix JS shift | |
| function SHR(a, b) { return b > 31 ? 0 : a >>> b; } // fix JS shift | |
| function SHIFT(a, b) { return b < 0 ? (b < -31 ? 0 : a >>> (0-b) ) : (b > 31 ? 0 : a << b); } | |
| /*** Constants ***/ | |
| var A1v = 46; | |
| var A2f = 34; | |
| var A2v = 47; | |
| var A3f = 35; | |
| var A3v = 48; | |
| var A4f = 36; | |
| var A4v = 49; | |
| var A5f = 37; | |
| var A6f = 38; | |
| var Anv = 45; | |
| var Aspiration = 9; | |
| var Atv = 50; | |
| var B1 = 13; | |
| var B2 = 17; | |
| var B2f = 40; | |
| var B3 = 19; | |
| var B3f = 41; | |
| var B4 = 21; | |
| var B4f = 42; | |
| var B5 = 23; | |
| var B5f = 43; | |
| var B6 = 25; | |
| var B6f = 44; | |
| var Bnp = 27; | |
| var Bnz = 29; | |
| var Btp = 31; | |
| var Btz = 33; | |
| var Bypass = 39; | |
| var Diplophonia = 4; | |
| var Epsilon = 0.0001; | |
| var F0 = 0; | |
| var F1 = 12; | |
| var F2 = 16; | |
| var F3 = 18; | |
| var F4 = 20; | |
| var F5 = 22; | |
| var F6 = 24; | |
| var Flutter = 1; | |
| var Fnp = 26; | |
| var Fnz = 28; | |
| var Friction = 10; | |
| var Ftp = 30; | |
| var Ftz = 32; | |
| var Gain = 51; | |
| var Jitter = 2; | |
| var PI = 3.141592653589793; | |
| var R1c = 12; | |
| var R1vp = 3; | |
| var R2c = 13; | |
| var R2fp = 7; | |
| var R2vp = 4; | |
| var R3c = 14; | |
| var R3fp = 8; | |
| var R3vp = 5; | |
| var R4c = 15; | |
| var R4fp = 9; | |
| var R4vp = 6; | |
| var R5c = 16; | |
| var R5fp = 10; | |
| var R6c = 17; | |
| var R6fp = 11; | |
| var R7c = 18; | |
| var R8c = 19; | |
| var Ra = 7; | |
| var Rk = 8; | |
| var Rnpc = 20; | |
| var Rnpp = 1; | |
| var Rnz = 21; | |
| var Ro = 6; | |
| var Rout = 24; | |
| var Rtpc = 22; | |
| var Rtpp = 2; | |
| var Rtz = 23; | |
| var Shimmer = 3; | |
| var Turbulence = 11; | |
| var Voicing = 5; | |
| /*** Variables ***/ | |
| var a1 = 0; | |
| var a2 = 0; | |
| var b1 = 0; | |
| var c1 = 0; | |
| var cascade = 0; | |
| var frame = null; | |
| var glast = 0; | |
| var interpreterProxy = null; | |
| var moduleName = "Klatt 3 November 2014 (e)"; | |
| var nlast = 0; | |
| var nmod = 0; | |
| var nopen = 0; | |
| var nper = 0; | |
| var periodCount = 0; | |
| var pitch = 0; | |
| var resonators = null; | |
| var samplesCount = 0; | |
| var samplesPerFrame = 0; | |
| var samplingRate = 0; | |
| var seed = 0; | |
| var t0 = 0; | |
| var vlast = 0; | |
| var x1 = 0; | |
| var x2 = 0; | |
| /* Add diplophonia (bicyclic voice). Change voicing amplitude. */ | |
| function addAmplitudeDiplophonia() { | |
| if ((MOD(periodCount, 2)) !== 0) { | |
| /* x1 must be <= 0 */ | |
| x1 = x1 * (1.0 - frame[Diplophonia]); | |
| if (x1 > 0) { | |
| x1 = 0; | |
| } | |
| } | |
| } | |
| /* Add F0 flutter, as specified in: | |
| 'Analysis, synthesis and perception of voice quality variations among | |
| female and male talkers' D.H. Klatt and L.C. Klatt JASA 87(2) February 1990. | |
| Flutter is added by applying a quasi-random element constructed from three | |
| slowly varying sine waves. */ | |
| function addFlutter() { | |
| var asin; | |
| var bsin; | |
| var csin; | |
| var deltaF0; | |
| var timeCount; | |
| timeCount = samplesCount / samplingRate; | |
| asin = Math.sin(((2.0 * PI) * 12.7) * timeCount); | |
| bsin = Math.sin(((2.0 * PI) * 7.1) * timeCount); | |
| csin = Math.sin(((2.0 * PI) * 4.7) * timeCount); | |
| deltaF0 = (((frame[Flutter] * 2.0) * frame[F0]) / 100.0) * ((asin + bsin) + csin); | |
| pitch += deltaF0; | |
| } | |
| /* Add diplophonia (bicyclic voice). Change F0. */ | |
| function addFrequencyDiplophonia() { | |
| if ((MOD(periodCount, 2)) === 0) { | |
| pitch += (frame[Diplophonia] * frame[F0]) * (1.0 - frame[Ro]); | |
| } else { | |
| pitch -= (frame[Diplophonia] * frame[F0]) * (1.0 - frame[Ro]); | |
| } | |
| } | |
| /* Add jitter (random F0 perturbation). */ | |
| function addJitter() { | |
| pitch += (((nextRandom() - 32767) * frame[Jitter]) / 32768.0) * frame[F0]; | |
| } | |
| /* Add shimmer (random voicing amplitude perturbation). */ | |
| function addShimmer() { | |
| /* x1 must be <= 0 */ | |
| x1 += (((nextRandom() - 32767) * frame[Shimmer]) / 32768.0) * x1; | |
| if (x1 > 0) { | |
| x1 = 0; | |
| } | |
| } | |
| /* Set up an anti-resonator */ | |
| function antiResonatorfrequencybandwidth(index, freq, bw) { | |
| var a; | |
| var arg; | |
| var b; | |
| var c; | |
| var r; | |
| arg = ((0.0 - PI) / samplingRate) * bw; | |
| r = Math.exp(arg); | |
| c = 0.0 - (r * r); | |
| arg = ((PI * 2.0) / samplingRate) * freq; | |
| b = (r * Math.cos(arg)) * 2.0; | |
| a = (1.0 - b) - c; | |
| a = 1.0 / a; | |
| b = (0.0 - b) * a; | |
| c = (0.0 - c) * a; | |
| resonatorAput(index, a); | |
| resonatorBput(index, b); | |
| resonatorCput(index, c); | |
| } | |
| function antiResonatorvalue(index, aFloat) { | |
| var answer; | |
| var p1; | |
| answer = ((resonatorA(index) * aFloat) + (resonatorB(index) * ((p1 = resonatorP1(index))))) + (resonatorC(index) * resonatorP2(index)); | |
| resonatorP2put(index, p1); | |
| resonatorP1put(index, aFloat); | |
| return answer; | |
| } | |
| /* Cascade vocal tract, excited by laryngeal sources. | |
| Nasal antiresonator, nasal resonator, tracheal antirresonator, | |
| tracheal resonator, then formants F8, F7, F6, F5, F4, F3, F2, F1. */ | |
| function cascadeBranch(source) { | |
| var out; | |
| if (!(cascade > 0)) { | |
| return 0.0; | |
| } | |
| out = antiResonatorvalue(Rnz, source); | |
| out = resonatorvalue(Rnpc, out); | |
| out = antiResonatorvalue(Rtz, out); | |
| /* Do not use unless sample rate >= 16000 */ | |
| out = resonatorvalue(Rtpc, out); | |
| if (cascade >= 8) { | |
| out = resonatorvalue(R8c, out); | |
| } | |
| if (cascade >= 7) { | |
| out = resonatorvalue(R7c, out); | |
| } | |
| if (cascade >= 6) { | |
| out = resonatorvalue(R6c, out); | |
| } | |
| if (cascade >= 5) { | |
| out = resonatorvalue(R5c, out); | |
| } | |
| if (cascade >= 4) { | |
| out = resonatorvalue(R4c, out); | |
| } | |
| if (cascade >= 3) { | |
| out = resonatorvalue(R3c, out); | |
| } | |
| if (cascade >= 2) { | |
| out = resonatorvalue(R2c, out); | |
| } | |
| if (cascade >= 1) { | |
| out = resonatorvalue(R1c, out); | |
| } | |
| return out; | |
| } | |
| /* Return the first indexable word of oop which is assumed to be variableWordSubclass */ | |
| function checkedFloatPtrOf(oop) { | |
| interpreterProxy.success(interpreterProxy.isWords(oop)); | |
| if (interpreterProxy.failed()) { | |
| return 0; | |
| } | |
| return oop.wordsAsFloat32Array(); | |
| } | |
| /* Return the first indexable word of oop which is assumed to be variableWordSubclass */ | |
| function checkedShortPtrOf(oop) { | |
| interpreterProxy.success(interpreterProxy.isWords(oop)); | |
| if (interpreterProxy.failed()) { | |
| return 0; | |
| } | |
| return oop.wordsAsInt16Array(); | |
| } | |
| /* Note: This is hardcoded so it can be run from Squeak. | |
| The module name is used for validating a module *after* | |
| it is loaded to check if it does really contain the module | |
| we're thinking it contains. This is important! */ | |
| function getModuleName() { | |
| return moduleName; | |
| } | |
| function glottalSource() { | |
| var x0; | |
| if (t0 === 0) { | |
| return 0; | |
| } | |
| if (nper < nopen) { | |
| x0 = (a1 * x1) + (a2 * x2); | |
| x2 = x1; | |
| x1 = x0; | |
| } else { | |
| x0 = (b1 * x1) - c1; | |
| x1 = x0; | |
| } | |
| if (nper >= t0) { | |
| nper = 0; | |
| pitchSynchronousReset(); | |
| } | |
| ++nper; | |
| return x0; | |
| } | |
| function halt() { | |
| ; | |
| } | |
| function linearFromdB(aNumber) { | |
| return Math.pow(2.0,((aNumber - 87.0) / 6.0)) * 32.767; | |
| } | |
| function loadFrom(klattOop) { | |
| var oop; | |
| interpreterProxy.success(SIZEOF(klattOop) === 22); | |
| if (interpreterProxy.failed()) { | |
| return false; | |
| } | |
| oop = interpreterProxy.fetchPointerofObject(0, klattOop); | |
| resonators = checkedFloatPtrOf(oop); | |
| pitch = interpreterProxy.fetchFloatofObject(2, klattOop); | |
| t0 = interpreterProxy.fetchIntegerofObject(3, klattOop); | |
| nper = interpreterProxy.fetchIntegerofObject(4, klattOop); | |
| nopen = interpreterProxy.fetchIntegerofObject(5, klattOop); | |
| nmod = interpreterProxy.fetchIntegerofObject(6, klattOop); | |
| a1 = interpreterProxy.fetchFloatofObject(7, klattOop); | |
| a2 = interpreterProxy.fetchFloatofObject(8, klattOop); | |
| x1 = interpreterProxy.fetchFloatofObject(9, klattOop); | |
| x2 = interpreterProxy.fetchFloatofObject(10, klattOop); | |
| b1 = interpreterProxy.fetchFloatofObject(11, klattOop); | |
| c1 = interpreterProxy.fetchFloatofObject(12, klattOop); | |
| glast = interpreterProxy.fetchFloatofObject(13, klattOop); | |
| vlast = interpreterProxy.fetchFloatofObject(14, klattOop); | |
| nlast = interpreterProxy.fetchFloatofObject(15, klattOop); | |
| periodCount = interpreterProxy.fetchIntegerofObject(16, klattOop); | |
| samplesCount = interpreterProxy.fetchIntegerofObject(17, klattOop); | |
| seed = interpreterProxy.fetchIntegerofObject(18, klattOop); | |
| cascade = interpreterProxy.fetchIntegerofObject(19, klattOop); | |
| samplesPerFrame = interpreterProxy.fetchIntegerofObject(20, klattOop); | |
| samplingRate = interpreterProxy.fetchIntegerofObject(21, klattOop); | |
| return interpreterProxy.failed() === false; | |
| } | |
| /* Answer a random number between 0 and 65535. */ | |
| function nextRandom() { | |
| seed = ((seed * 1309) + 13849) & 65535; | |
| return seed; | |
| } | |
| function normalizeGlottalPulse() { | |
| var ingore; | |
| var s0; | |
| var s1; | |
| var s2; | |
| s0 = 0.0; | |
| s1 = x1; | |
| s2 = x2; | |
| for (ingore = 1; ingore <= nopen; ingore++) { | |
| s0 = (a1 * s1) + (a2 * s2); | |
| s2 = s1; | |
| s1 = s0; | |
| } | |
| if (s0 !== 0.0) { | |
| x1 = (x1 / s0) * 10000.0; | |
| } | |
| } | |
| /* Friction-excited parallel vocal tract formants F6, F5, F4, F3, F2, | |
| outputs added with alternating sign. Sound source for other | |
| parallel resonators is friction plus first difference of | |
| voicing waveform. */ | |
| function parallelFrictionBranch(source) { | |
| return (((resonatorvalue(R2fp, source) - resonatorvalue(R3fp, source)) + resonatorvalue(R4fp, source)) - resonatorvalue(R5fp, source)) + resonatorvalue(R6fp, source); | |
| } | |
| /* Voice-excited parallel vocal tract F1, F2, F3, F4, FNP and FTP. */ | |
| function parallelVoicedBranch(source) { | |
| return ((((resonatorvalue(R1vp, source) + resonatorvalue(R2vp, source)) + resonatorvalue(R3vp, source)) + resonatorvalue(R4vp, source)) + resonatorvalue(Rnpp, source)) + resonatorvalue(Rtpp, source); | |
| } | |
| function pitchSynchronousReset() { | |
| if (frame[F0] > 0) { | |
| voicedPitchSynchronousReset(); | |
| periodCount = MOD((periodCount + 1), 65535); | |
| } else { | |
| t0 = 1; | |
| nmod = t0; | |
| } | |
| } | |
| function primitiveSynthesizeFrameIntoStartingAt() { | |
| var aKlattFrame; | |
| var buffer; | |
| var bufferOop; | |
| var rcvr; | |
| var startIndex; | |
| aKlattFrame = checkedFloatPtrOf(interpreterProxy.stackValue(2)); | |
| buffer = checkedShortPtrOf((bufferOop = interpreterProxy.stackValue(1))); | |
| startIndex = interpreterProxy.stackIntegerValue(0); | |
| if (interpreterProxy.failed()) { | |
| return null; | |
| } | |
| rcvr = interpreterProxy.stackObjectValue(3); | |
| if (!loadFrom(rcvr)) { | |
| return null; | |
| } | |
| interpreterProxy.success((SIZEOF(bufferOop) * 2) >= samplesPerFrame); | |
| if (interpreterProxy.failed()) { | |
| return null; | |
| } | |
| synthesizeFrameintostartingAt(aKlattFrame, buffer, startIndex); | |
| if (!saveTo(rcvr)) { | |
| return null; | |
| } | |
| interpreterProxy.pop(3); | |
| } | |
| function quphicosphisinphirphid(u, phi, cosphi, sinphi, rphid) { | |
| var expuphi; | |
| expuphi = Math.exp(u * phi); | |
| return (expuphi * ((((rphid * ((u * u) + 1.0)) + u) * sinphi) - cosphi)) + 1.0; | |
| } | |
| /* Convert formant frequencies and bandwidth into | |
| resonator difference equation coefficients. */ | |
| function resonatorfrequencybandwidth(index, freq, bw) { | |
| var a; | |
| var arg; | |
| var b; | |
| var c; | |
| var r; | |
| arg = ((0.0 - PI) / samplingRate) * bw; | |
| r = Math.exp(arg); | |
| c = 0.0 - (r * r); | |
| arg = ((PI * 2.0) / samplingRate) * freq; | |
| b = (r * Math.cos(arg)) * 2.0; | |
| a = (1.0 - b) - c; | |
| resonatorAput(index, a); | |
| resonatorBput(index, b); | |
| resonatorCput(index, c); | |
| } | |
| /* Convert formant frequencies and bandwidth into | |
| resonator difference equation coefficients. */ | |
| function resonatorfrequencybandwidthgain(index, freq, bw, gain) { | |
| resonatorfrequencybandwidth(index, freq, bw); | |
| resonatorAput(index, resonatorA(index) * gain); | |
| } | |
| function resonatorvalue(index, aFloat) { | |
| var answer; | |
| var p1; | |
| /* (p1 between: -100000 and: 100000) ifFalse: [self halt]. | |
| (answer between: -100000 and: 100000) ifFalse: [self halt]. */ | |
| answer = ((resonatorA(index) * aFloat) + (resonatorB(index) * ((p1 = resonatorP1(index))))) + (resonatorC(index) * resonatorP2(index)); | |
| resonatorP2put(index, p1); | |
| resonatorP1put(index, answer); | |
| return answer; | |
| } | |
| function resonatorA(index) { | |
| return resonators[(index * 5) - 5]; | |
| } | |
| function resonatorAput(index, aFloat) { | |
| resonators[(index * 5) - 5] = aFloat; | |
| } | |
| function resonatorB(index) { | |
| return resonators[(index * 5) - 4]; | |
| } | |
| function resonatorBput(index, aFloat) { | |
| resonators[(index * 5) - 4] = aFloat; | |
| } | |
| function resonatorC(index) { | |
| return resonators[(index * 5) - 3]; | |
| } | |
| function resonatorCput(index, aFloat) { | |
| resonators[(index * 5) - 3] = aFloat; | |
| } | |
| function resonatorP1(index) { | |
| return resonators[(index * 5) - 2]; | |
| } | |
| function resonatorP1put(index, aFloat) { | |
| resonators[(index * 5) - 2] = aFloat; | |
| } | |
| function resonatorP2(index) { | |
| return resonators[(index * 5) - 1]; | |
| } | |
| function resonatorP2put(index, aFloat) { | |
| resonators[(index * 5) - 1] = aFloat; | |
| } | |
| function rorark(roNumber, raNumber, rkNumber) { | |
| var cosphi; | |
| var d; | |
| var gamma; | |
| var gammapwr; | |
| var phi; | |
| var r; | |
| var ra; | |
| var rho; | |
| var rk; | |
| var ro; | |
| var rphid; | |
| var sinphi; | |
| var te; | |
| var theta; | |
| var u; | |
| te = ((t0 * roNumber)|0); | |
| ro = te / t0; | |
| rk = rkNumber; | |
| ra = raNumber; | |
| if (ra <= 0.0) { | |
| d = 1.0; | |
| } else { | |
| r = (1.0 - ro) / ra; | |
| d = 1.0 - (r / (Math.exp(r) - 1.0)); | |
| } | |
| phi = PI * (rk + 1.0); | |
| cosphi = Math.cos(phi); | |
| sinphi = Math.sin(phi); | |
| rphid = ((ra / ro) * phi) * d; | |
| u = zeroQphicosphisinphirphid(phi, cosphi, sinphi, rphid); | |
| theta = phi / te; | |
| rho = Math.exp(u * theta); | |
| a1 = (2.0 * Math.cos(theta)) * rho; | |
| a2 = 0.0 - (rho * rho); | |
| x2 = 0.0; | |
| x1 = rho * Math.sin(theta); | |
| gamma = Math.exp(-1.0 / (ra * t0)); | |
| gammapwr = Math.pow(gamma,(t0 - te)); | |
| b1 = gamma; | |
| c1 = ((1.0 - gamma) * gammapwr) / (1.0 - gammapwr); | |
| normalizeGlottalPulse(); | |
| } | |
| function saveTo(origKlattOop) { | |
| var a1Oop; | |
| var a2Oop; | |
| var b1Oop; | |
| var c1Oop; | |
| var glastOop; | |
| var klattOop; | |
| var nlastOop; | |
| var pitchOop; | |
| var vlastOop; | |
| var x1Oop; | |
| var x2Oop; | |
| interpreterProxy.pushRemappableOop(origKlattOop); | |
| interpreterProxy.pushRemappableOop(interpreterProxy.floatObjectOf(pitch)); | |
| interpreterProxy.pushRemappableOop(interpreterProxy.floatObjectOf(a1)); | |
| interpreterProxy.pushRemappableOop(interpreterProxy.floatObjectOf(a2)); | |
| interpreterProxy.pushRemappableOop(interpreterProxy.floatObjectOf(x1)); | |
| interpreterProxy.pushRemappableOop(interpreterProxy.floatObjectOf(x2)); | |
| interpreterProxy.pushRemappableOop(interpreterProxy.floatObjectOf(b1)); | |
| interpreterProxy.pushRemappableOop(interpreterProxy.floatObjectOf(c1)); | |
| interpreterProxy.pushRemappableOop(interpreterProxy.floatObjectOf(glast)); | |
| interpreterProxy.pushRemappableOop(interpreterProxy.floatObjectOf(vlast)); | |
| nlastOop = interpreterProxy.floatObjectOf(nlast); | |
| vlastOop = interpreterProxy.popRemappableOop(); | |
| glastOop = interpreterProxy.popRemappableOop(); | |
| c1Oop = interpreterProxy.popRemappableOop(); | |
| b1Oop = interpreterProxy.popRemappableOop(); | |
| x2Oop = interpreterProxy.popRemappableOop(); | |
| x1Oop = interpreterProxy.popRemappableOop(); | |
| a2Oop = interpreterProxy.popRemappableOop(); | |
| a1Oop = interpreterProxy.popRemappableOop(); | |
| pitchOop = interpreterProxy.popRemappableOop(); | |
| klattOop = interpreterProxy.popRemappableOop(); | |
| if (interpreterProxy.failed()) { | |
| return false; | |
| } | |
| interpreterProxy.storePointerofObjectwithValue(2, klattOop, pitchOop); | |
| interpreterProxy.storeIntegerofObjectwithValue(3, klattOop, t0); | |
| interpreterProxy.storeIntegerofObjectwithValue(4, klattOop, nper); | |
| interpreterProxy.storeIntegerofObjectwithValue(5, klattOop, nopen); | |
| interpreterProxy.storeIntegerofObjectwithValue(6, klattOop, nmod); | |
| interpreterProxy.storePointerofObjectwithValue(7, klattOop, a1Oop); | |
| interpreterProxy.storePointerofObjectwithValue(8, klattOop, a2Oop); | |
| interpreterProxy.storePointerofObjectwithValue(9, klattOop, x1Oop); | |
| interpreterProxy.storePointerofObjectwithValue(10, klattOop, x2Oop); | |
| interpreterProxy.storePointerofObjectwithValue(11, klattOop, b1Oop); | |
| interpreterProxy.storePointerofObjectwithValue(12, klattOop, c1Oop); | |
| interpreterProxy.storePointerofObjectwithValue(13, klattOop, glastOop); | |
| interpreterProxy.storePointerofObjectwithValue(14, klattOop, vlastOop); | |
| interpreterProxy.storePointerofObjectwithValue(15, klattOop, nlastOop); | |
| interpreterProxy.storeIntegerofObjectwithValue(16, klattOop, periodCount); | |
| interpreterProxy.storeIntegerofObjectwithValue(17, klattOop, samplesCount); | |
| interpreterProxy.storeIntegerofObjectwithValue(18, klattOop, seed); | |
| return interpreterProxy.failed() === false; | |
| } | |
| function setCurrentFrame(aKlattFrame) { | |
| var ampF1V; | |
| var ampF2F; | |
| var ampF2V; | |
| var ampF3F; | |
| var ampF3V; | |
| var ampF4F; | |
| var ampF4V; | |
| var ampF5F; | |
| var ampF6F; | |
| var ampFNV; | |
| var ampFTV; | |
| /* Fudge factors... */ | |
| frame = aKlattFrame; | |
| /* -4.44 dB */ | |
| ampFNV = linearFromdB(frame[Anv]) * 0.6; | |
| /* -4.44 dB */ | |
| ampFTV = linearFromdB(frame[Atv]) * 0.6; | |
| /* -7.96 dB */ | |
| ampF1V = linearFromdB(frame[A1v]) * 0.4; | |
| /* -16.5 dB */ | |
| ampF2V = linearFromdB(frame[A2v]) * 0.15; | |
| /* -24.4 dB */ | |
| ampF3V = linearFromdB(frame[A3v]) * 0.06; | |
| /* -28.0 dB */ | |
| ampF4V = linearFromdB(frame[A4v]) * 0.04; | |
| /* -16.5 dB */ | |
| ampF2F = linearFromdB(frame[A2f]) * 0.15; | |
| /* -24.4 dB */ | |
| ampF3F = linearFromdB(frame[A3f]) * 0.06; | |
| /* -28.0 dB */ | |
| ampF4F = linearFromdB(frame[A4f]) * 0.04; | |
| /* -33.2 dB */ | |
| ampF5F = linearFromdB(frame[A5f]) * 0.022; | |
| /* -30.5 dB */ | |
| /* Set coefficients of variable cascade resonators */ | |
| ampF6F = linearFromdB(frame[A6f]) * 0.03; | |
| if (cascade >= 8) { | |
| if (samplingRate >= 16000) { | |
| /* Inside Nyquist rate? */ | |
| resonatorfrequencybandwidth(R8c, 7500, 600); | |
| } else { | |
| cascade = 6; | |
| } | |
| } | |
| if (cascade >= 7) { | |
| if (samplingRate >= 16000) { | |
| /* Inside Nyquist rate? */ | |
| resonatorfrequencybandwidth(R7c, 6500, 500); | |
| } else { | |
| cascade = 6; | |
| } | |
| } | |
| if (cascade >= 6) { | |
| resonatorfrequencybandwidth(R6c, frame[F6], frame[B6]); | |
| } | |
| if (cascade >= 5) { | |
| resonatorfrequencybandwidth(R5c, frame[F5], frame[B5]); | |
| } | |
| resonatorfrequencybandwidth(R4c, frame[F4], frame[B4]); | |
| resonatorfrequencybandwidth(R3c, frame[F3], frame[B3]); | |
| resonatorfrequencybandwidth(R2c, frame[F2], frame[B2]); | |
| resonatorfrequencybandwidth(R1c, frame[F1], frame[B1]); | |
| resonatorfrequencybandwidth(Rnpc, frame[Fnp], frame[Bnp]); | |
| resonatorfrequencybandwidth(Rtpc, frame[Ftp], frame[Btp]); | |
| antiResonatorfrequencybandwidth(Rnz, frame[Fnz], frame[Bnz]); | |
| antiResonatorfrequencybandwidth(Rtz, frame[Ftz], frame[Btz]); | |
| resonatorfrequencybandwidthgain(Rnpp, frame[Fnp], frame[Bnp], ampFNV); | |
| resonatorfrequencybandwidthgain(Rtpp, frame[Ftp], frame[Btp], ampFTV); | |
| resonatorfrequencybandwidthgain(R1vp, frame[F1], frame[B1], ampF1V); | |
| resonatorfrequencybandwidthgain(R2vp, frame[F2], frame[B2], ampF2V); | |
| resonatorfrequencybandwidthgain(R3vp, frame[F3], frame[B3], ampF3V); | |
| resonatorfrequencybandwidthgain(R4vp, frame[F4], frame[B4], ampF4V); | |
| resonatorfrequencybandwidthgain(R2fp, frame[F2], frame[B2f], ampF2F); | |
| resonatorfrequencybandwidthgain(R3fp, frame[F3], frame[B3f], ampF3F); | |
| resonatorfrequencybandwidthgain(R4fp, frame[F4], frame[B4f], ampF4F); | |
| resonatorfrequencybandwidthgain(R5fp, frame[F5], frame[B5f], ampF5F); | |
| resonatorfrequencybandwidthgain(R6fp, frame[F6], frame[B6f], ampF6F); | |
| } | |
| /* Note: This is coded so that is can be run from Squeak. */ | |
| function setInterpreter(anInterpreter) { | |
| var ok; | |
| interpreterProxy = anInterpreter; | |
| ok = interpreterProxy.majorVersion() == VM_PROXY_MAJOR; | |
| if (ok === false) { | |
| return false; | |
| } | |
| ok = interpreterProxy.minorVersion() >= VM_PROXY_MINOR; | |
| return ok; | |
| } | |
| function synthesizeFrameintostartingAt(aKlattFrame, buffer, startIndex) { | |
| var ampGain; | |
| var aspiration; | |
| var aspirationNoise; | |
| var bypass; | |
| var friction; | |
| var frictionNoise; | |
| var gain; | |
| var glotout; | |
| var index; | |
| var noise; | |
| var out; | |
| var parGlotout; | |
| var parVoicing; | |
| var source; | |
| var temp; | |
| var top; | |
| var turbulence; | |
| var voice; | |
| var voicing; | |
| setCurrentFrame(aKlattFrame); | |
| if (pitch > 0) { | |
| voicing = linearFromdB(frame[Voicing] - 7); | |
| parVoicing = linearFromdB(frame[Voicing]); | |
| turbulence = linearFromdB(frame[Turbulence]) * 0.1; | |
| } else { | |
| voicing = (parVoicing = (turbulence = 0.0)); | |
| } | |
| friction = linearFromdB(frame[Friction]) * 0.25; | |
| aspiration = linearFromdB(frame[Aspiration]) * 0.05; | |
| /* -26.0 dB */ | |
| /* Flod overall gain into output resonator (low-pass filter) */ | |
| bypass = linearFromdB(frame[Bypass]) * 0.05; | |
| gain = frame[Gain] - 3; | |
| if (gain <= 0) { | |
| gain = 57; | |
| } | |
| ampGain = linearFromdB(gain); | |
| resonatorfrequencybandwidthgain(Rout, 0, samplingRate, ampGain); | |
| noise = nlast; | |
| index = startIndex; | |
| top = (samplesPerFrame + startIndex) - 1; | |
| while (index <= top) { | |
| /* Get low-passed random number for aspiration and friction noise */ | |
| /* radom number between -8196.0 and 8196.0 */ | |
| /* Tilt down noise spectrum by soft low-pass filter having | |
| a pole near the origin in the z-plane. */ | |
| noise = (nextRandom() - 32768) / 4.0; | |
| noise += 0.75 * nlast; | |
| /* Amplitude modulate noise (reduce noise amplitude during second | |
| half of glottal period) if voicing simultaneously present. */ | |
| nlast = noise; | |
| if (nper > nmod) { | |
| noise = noise * 0.5; | |
| } | |
| /* Compute voicing waveform. */ | |
| frictionNoise = friction * noise; | |
| voice = glottalSource(); | |
| /* Add turbulence during glottal open phase. | |
| Use random rather than noise because noise is low-passed. */ | |
| vlast = voice; | |
| if (nper < nopen) { | |
| voice += (turbulence * (nextRandom() - 32768)) / 4.0; | |
| } | |
| glotout = voicing * voice; | |
| /* Compute aspiration amplitude and add to voicing source. */ | |
| parGlotout = parVoicing * voice; | |
| aspirationNoise = aspiration * noise; | |
| glotout += aspirationNoise; | |
| /* Cascade vocal tract, excited by laryngeal sources. | |
| Nasal antiresonator, nasal resonator, trachearl antirresonator, | |
| tracheal resonator, then formants F8, F7, F6, F5, F4, F3, F2, F1. */ | |
| parGlotout += aspirationNoise; | |
| /* Voice-excited parallel vocal tract F1, F2, F3, F4, FNP and FTP. */ | |
| out = cascadeBranch(glotout); | |
| /* Source is voicing plus aspiration. */ | |
| source = parGlotout; | |
| /* Friction-excited parallel vocal tract formants F6, F5, F4, F3, F2, | |
| outputs added with alternating sign. Sound source for other | |
| parallel resonators is friction plus first difference of | |
| voicing waveform. */ | |
| out += parallelVoicedBranch(source); | |
| source = (frictionNoise + parGlotout) - glast; | |
| glast = parGlotout; | |
| /* Apply bypas and output low-pass filter */ | |
| out = parallelFrictionBranch(source) - out; | |
| out = (bypass * source) - out; | |
| out = resonatorvalue(Rout, out); | |
| temp = ((out * ampGain)|0); | |
| if (temp < -32768) { | |
| temp = -32768; | |
| } | |
| if (temp > 32767) { | |
| temp = 32767; | |
| } | |
| buffer[index - 1] = temp; | |
| ++index; | |
| ++samplesCount; | |
| } | |
| } | |
| /* Set the pitch. */ | |
| function voicedPitchSynchronousReset() { | |
| /* Add flutter and jitter (F0 perturbations). */ | |
| pitch = frame[F0]; | |
| addFlutter(); | |
| addJitter(); | |
| addFrequencyDiplophonia(); | |
| if (pitch < 0) { | |
| pitch = 0; | |
| } | |
| /* Duration of period before amplitude modulation. */ | |
| t0 = ((samplingRate / pitch)|0); | |
| nmod = t0; | |
| if (frame[Voicing] > 0) { | |
| nmod = nmod >> 1; | |
| } | |
| /* Set the LF glottal pulse model parameters. */ | |
| nopen = ((t0 * frame[Ro])|0); | |
| rorark(frame[Ro], frame[Ra], frame[Rk]); | |
| addShimmer(); | |
| addAmplitudeDiplophonia(); | |
| } | |
| function zeroQphicosphisinphirphid(phi, cosphi, sinphi, rphid) { | |
| var qa; | |
| var qb; | |
| var qc; | |
| var qzero; | |
| var ua; | |
| var ub; | |
| var uc; | |
| qzero = quphicosphisinphirphid(0, phi, cosphi, sinphi, rphid); | |
| if (qzero > 0) { | |
| ua = 0; | |
| ub = 1; | |
| qa = qzero; | |
| qb = quphicosphisinphirphid(ub, phi, cosphi, sinphi, rphid); | |
| while (qb > 0) { | |
| ua = ub; | |
| qa = qb; | |
| ub = ub * 2; | |
| qb = quphicosphisinphirphid(ub, phi, cosphi, sinphi, rphid); | |
| } | |
| } else { | |
| ua = -1; | |
| ub = 0; | |
| qa = quphicosphisinphirphid(ua, phi, cosphi, sinphi, rphid); | |
| qb = qzero; | |
| while (qa < 0) { | |
| ub = ua; | |
| qb = qa; | |
| ua = ua * 2; | |
| qa = quphicosphisinphirphid(ua, phi, cosphi, sinphi, rphid); | |
| } | |
| } | |
| while ((ub - ua) > Epsilon) { | |
| uc = (ub + ua) / 2.0; | |
| qc = quphicosphisinphirphid(uc, phi, cosphi, sinphi, rphid); | |
| if (qc > 0) { | |
| ua = uc; | |
| qa = qc; | |
| } else { | |
| ub = uc; | |
| qb = qc; | |
| } | |
| } | |
| return (ub + ua) / 2.0; | |
| } | |
| function registerPlugin() { | |
| if (typeof Squeak === "object" && Squeak.registerExternalModule) { | |
| Squeak.registerExternalModule("Klatt", { | |
| setInterpreter: setInterpreter, | |
| primitiveSynthesizeFrameIntoStartingAt: primitiveSynthesizeFrameIntoStartingAt, | |
| getModuleName: getModuleName, | |
| }); | |
| } else self.setTimeout(registerPlugin, 100); | |
| } | |
| registerPlugin(); | |
| })(); // Register module/plugin | |