import flash.media.Sound;
/*
babblebot Copyright (C) 2008 Alex McLean
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
The formant filter is by alex@smartelectronix.com, taken from
http://www.musicdsp.org/ . Thanks!
*/
class Synth {
static var samplesPerCall : Int = 2048;
static var delay : Array = new Array();
static var delayPos : Int = 0;
static var gain:Float;
static var prev:Float;
static var input : Array = new Array();
static var letterQueue : Array = new Array();
static var wordQueue : Array = new Array();
static var wordPos = 0;
static var maxDelaySz : Int = 512;
static var delaySz : Int = Math.floor(maxDelaySz / 2);
static var switcheroo : Float = 0;
static var vowels : Hash = new Hash();
static var consonants : Hash = new Hash();
static var samples : Int = 0;
static var letterSpeed : Int = 1000;
static var wordSpeed : Int = 11000;
static var coeff : Array>;
static var formant_history : Array = new Array();
static var vowelnum = 0;
static var jsCnx;
static function initLetters() {
consonants.set("b", 0);
consonants.set("c", 0.04);
consonants.set("d", 0.09);
consonants.set("f", 0.14);
consonants.set("g", 0.19);
consonants.set("h", 0.23);
consonants.set("j", 0.28);
consonants.set("k", 0.33);
consonants.set("l", 0.38);
consonants.set("m", 0.42);
consonants.set("n", 0.47);
consonants.set("p", 0.52);
consonants.set("q", 0.57);
consonants.set("r", 0.61);
consonants.set("s", 0.66);
consonants.set("t", 0.71);
consonants.set("v", 0.76);
consonants.set("w", 0.80);
consonants.set("x", 0.85);
consonants.set("y", 0.9);
consonants.set("z", 0.95);
vowels.set("a", 0.4);
vowels.set("e", 0.8);
vowels.set("i", 0.5);
vowels.set("o", 0.9);
vowels.set("u", 0.7);
}
static function main() {
//trace(flash.system.Capabilities.version);
initLetters();
initCoeff();
var i;
prev = 0;
gain = 0.5;
var ctx = new haxe.remoting.Context();
ctx.addObject("Synth",Synth);
jsCnx = haxe.remoting.ExternalConnection.jsConnect("default",ctx);
var buffer = new Sound();
for (i in 0 ... maxDelaySz) {
delay.push(0.0);
}
buffer.addEventListener("sampleData", onSamplesCallback);
buffer.play();
}
static function play(dur : Null) {
for (i in 0 ... dur) {
input.push((Math.random() - 0.5) * gain);
}
}
static function setDelaySz(sz : Null) {
if (sz > 1) {
sz = 1;
}
if (sz < 0) {
sz = 0;
}
var foo = Math.floor(sz * maxDelaySz);
if (foo < delaySz) {
for (i in foo ... delaySz) {
delay[i] = 0.0;
}
}
delaySz = foo;
}
static function setSwitcheroo(i : Float) {
if (i > 1) {
i = 1;
}
else if (i < 0) {
i = 0;
}
switcheroo = i;
}
static function playWord(n: Int) {
var w = wordQueue[n].toLowerCase();
jsCnx.JsClient.playingWord.call([n, wordQueue]);
for (i in 0 ... w.length) {
var c : String = w.charAt(i);
letterQueue.push(c);
}
}
static function playSentence(s : String) {
var r : EReg = ~/([a-zA-Z]+|-)/g;
wordQueue = new Array();
while (r.match(s)) {
wordQueue.push(r.matched(1));
}
}
static function playNextLetter() {
var c = letterQueue.shift();
if (c == '-') {
// a rest - do nothing
}
else if (consonants.exists(c)) {
setSwitcheroo(consonants.get(c));
play(50);
}
else if (vowels.exists(c)) {
setDelaySz(vowels.get(c));
if (c == 'a') {
vowelnum = 0;
}
else if (c == 'e') {
vowelnum = 1;
}
else if (c == 'i') {
vowelnum = 2;
}
else if (c == 'o') {
vowelnum = 3;
}
else if (c == 'u') {
vowelnum = 4;
}
}
else {
// unknown glyph
}
}
static function onSamplesCallback(event) {
for (i in 0 ... (samplesPerCall)) {
var sample = delay[delayPos];
var tmp = prev;
prev = sample;
sample = (tmp + sample) * 0.5;
var output = formant_filter(sample);
event.data.writeFloat(output); // left
event.data.writeFloat(output); // right
sample = sample * 0.99;
if (input.length > 0) {
sample += input.shift();
}
if (switcheroo > 0 && (Math.random() < switcheroo)) {
sample = 0 - sample;
}
delay[delayPos] = sample;
if (++delayPos >= delaySz) {
delayPos = 0;
}
if (samples % letterSpeed == 0 && letterQueue.length > 0) {
playNextLetter();
}
if (samples % wordSpeed == 0 && wordQueue.length > 0) {
playWord(wordPos % wordQueue.length);
wordPos++;
}
samples++;
}
}
static function initCoeff() {
coeff = new Array>();
coeff[0] = [3.11044e-06, 8.943665402, -36.83889529, 92.01697887,
-154.337906, 181.6233289, -151.8651235, 89.09614114,
-35.10298511, 8.388101016, -0.923313471
];
coeff[1] = [4.36215e-06, 8.90438318, -36.55179099, 91.05750846,
-152.422234, 179.1170248, -149.6496211, 87.78352223,
-34.60687431, 8.282228154, -0.914150747
];
coeff[2] = [3.33819e-06, 8.893102966, -36.49532826, 90.96543286,
-152.4545478, 179.4835618, -150.315433, 88.43409371,
-34.98612086, 8.407803364, -0.932568035
];
coeff[3] = [1.13572e-06, 8.994734087, -37.2084849, 93.22900521,
-156.6929844, 184.596544, -154.3755513, 90.49663749,
-35.58964535, 8.478996281, -0.929252233
];
coeff[4] = [4.09431e-07, 8.997322763, -37.20218544, 93.11385476,
-156.2530937, 183.7080141, -153.2631681,
89.59539726, -35.12454591, 8.338655623,
-0.910251753
];
for (i in 0 ... 11) {
formant_history.push(0.0);
}
}
static function formant_filter(s : Float) {
var res : Float;
res = ( coeff[vowelnum][0] * s +
coeff[vowelnum][1] * formant_history[0] +
coeff[vowelnum][2] * formant_history[1] +
coeff[vowelnum][3] * formant_history[2] +
coeff[vowelnum][4] * formant_history[3] +
coeff[vowelnum][5] * formant_history[4] +
coeff[vowelnum][6] * formant_history[5] +
coeff[vowelnum][7] * formant_history[6] +
coeff[vowelnum][8] * formant_history[7] +
coeff[vowelnum][9] * formant_history[8] +
coeff[vowelnum][10] * formant_history[9]
);
formant_history[9] = formant_history[8];
formant_history[8] = formant_history[7];
formant_history[7] = formant_history[6];
formant_history[6] = formant_history[5];
formant_history[5] = formant_history[4];
formant_history[4] = formant_history[3];
formant_history[3] = formant_history[2];
formant_history[2] = formant_history[1];
formant_history[1] = formant_history[0];
formant_history[0] = res;
return res;
}
}