// Vowel Recognizer and CPU Heater, // by PRC, March/April 2008 // updated Jan 2014 BPF b[14]; Gain g[14]; OnePole p[14]; ["Ahh","Ehh","Eee","Ohh","Ooo","..."] @=> string vnames[]; MAUI_View myWinder; myWinder.size(350.0,225.0); MAUI_LED disp[14]; // Our subband display MAUI_LED which[6]; // Our neareast neighbor display MAUI_Button exit,emit,erase,train[16]; exit.size(80,60); // This cleans up and exits exit.name("Exit"); exit.position(270,120); emit.size(80,60); // This prints the current state values emit.name("Emit"); emit.position(220,120); erase.size(350.0,120.0); // This is just for pretty erase.toggleType(); erase.state(1); MAUI_Slider myGain; myGain.name("Input gain"); myGain.range(0.0,10.0); myGain.value(4.0); myGain.position(0,180); myGain.size(320,60); myWinder.addElement(exit); myWinder.addElement(emit); myWinder.addElement(erase); myWinder.addElement(myGain); float st[6][14]; // Trained (and trainable) "states" [0.3,0.5,0.5,0.6,0.7,1.0,1.0,0.7,0.6,0.4,0.3,0.2,0.2,0.1] @=> st[0]; [0.2,0.4,0.4,0.6,0.8,0.6,0.4,0.2,0.2,0.3,0.3,0.2,0.1,0.1] @=> st[1]; [0.3,0.6,0.4,0.3,0.1,0.1,0.0,0.0,0.0,0.1,0.1,0.1,0.2,0.2] @=> st[2]; [0.5,0.7,0.7,1.0,0.8,0.7,0.5,0.4,0.2,0.2,0.1,0.1,0.0,0.0] @=> st[3]; [0.6,0.9,0.8,0.9,0.6,0.5,0.4,0.2,0.1,0.1,0.1,0.0,0.0,0.0] @=> st[4]; [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0] @=> st[5]; adc => Gain inp; 4.0 => inp.gain; for (0 => int i;i < 14;1 +=> i) { inp=> b[i] => g[i] => p[i] => blackhole; b[i] => g[i]; // make a 3 => g[i].op; // squarer 0.9995 => p[i].pole; // then smoothing 4.0 => b[i].Q; 200.0*Math.pow(1.2599,i) => b[i].freq; disp[i].position(15+20*i,50); myWinder.addElement(disp[i]); } for (0 => int j;j < 6;1 +=> j) { train[j].name(vnames[j]); train[j].size(70,60); train[j].position(35*j,135); which[j].position(3+35*j,100); myWinder.addElement(train[j]); myWinder.addElement(which[j]); } myWinder.name("Vowel Recognizer"); myWinder.display(); float bestdist,dist,temp,logp[14]; int best; 1 => int running; while (running) { 0.100 :: second => now; for (0 => int i;i < 14;1 +=> i) { 0.5 * (Math.log10(p[i].last() + 0.01) + 2.0) => logp[i]; disp[i].position(15+20*i,50-50*logp[i]); if (logp[i]>0.6) disp[i].light(); else disp[i].unlight(); myWinder.addElement(disp[i]); } 1000000.0 => bestdist; for (0 => int j; j < 6; 1 +=> j) { if (train[j].state()==1) { for (0 => int i;i < 14;1 +=> i) logp[i] => st[j][i]; <<< vnames[j], " trained" >>>; } else { 0.0 => dist; for (0 => int i;i < 14;1 +=> i) { logp[i]-st[j][i] => temp; dist + (temp*temp) => dist; } if (dist < bestdist) { dist => bestdist; j => best; } } which[j].unlight(); } which[best].light(); 1 - exit.state() => running; if (emit.state()==1) { // Save our "training data" for (0 => int j;j < 6;1 +=> j) { <<< st[j][0],st[j][1],st[j][2],st[j][3],st[j][4],st[j][5],st[j][6], st[j][7],st[j][8],st[j][9],st[j][10],st[j][11],st[j][12],st[j][13] >>>; } } myGain.value() => inp.gain; } myWinder.destroy();