/*
	animated sine sweep, 2x upsampling, with user choice of anti-alias filter

	copyright 2010 Nigel Redmon
*/

document.observe('dom:loaded', function(){
	// update globals (in case we got here by the "back" button)
	irType = document.params.irType.value;
	showIR = document.params.showIR.checked;

	//startSim();
	doFreq(32, 0);
});

function doFreq(freq, step) {
	isRunning = true;
	var sigLen = 1024;
	var sigLenPadded = sigLen + 300;	// make some extra, to allow for sinc interpolation
	var signal = [sigLenPadded];		// original signal
	var signal2 = [sigLen * 2];			// 2x oversample result
	// sine
	for (var idx = 0; idx < sigLenPadded; idx++) {
		signal[idx] = 0;
		var numHarmonics = 1;
		var val = 0;
		for (var harm = 1; harm <= numHarmonics; harm++)
			val += Math.sin(Math.PI * 2 * freq * harm * idx / sigLen) / harm;
		signal[idx] = val;
	}

	// select filter
	switch (irType) {
		default:
		case "none":
			var coefs = [ 1.0 ];			// no interpolation
			break;
		case "zoh":
			var coefs = [ 1.0, 1.0 ];		// zero-order hold
			break;
		case "linear":
			var coefs = [ 0.5, 1.0, 0.5 ];	// linear
			break;
		case "sinc 1":
			var coefs = [ -0.00004032787096706525,0,0.0007920116403661329,0,-0.0040181863877435135,0,0.013232838626812462,0,-0.03441026074262933,0,0.07862928971185519,0,-0.17894257012285258,0,0.6247646537513802,0.9999851027875566,0.6247646537513802,0,-0.17894257012285258,0,0.07862928971185519,0,-0.03441026074262933,0,0.013232838626812462,0,-0.0040181863877435135,0,0.0007920116403661329,0,-0.00004032787096706525 ];
			break;
		case "sinc 2":
			var coefs = [-0.000223155103289621,0.0003247356502056869,0.002380083574542235,0.0027438534027862437,-0.0037062150882656553,-0.01330554064114003,-0.009350354982381237,0.018030953203899643,0.04387884723366638,0.019523380919769573,-0.06308669540223802,-0.1208131753539901,-0.029268372751344085,0.24355509271425682,0.5593570762152679,0.6999189728165084,0.5593570762152679,0.24355509271425682,-0.029268372751344085,-0.1208131753539901,-0.06308669540223802,0.019523380919769573,0.04387884723366638,0.018030953203899643,-0.009350354982381237,-0.01330554064114003,-0.0037062150882656553,0.0027438534027862437,0.002380083574542235,0.0003247356502056869,-0.000223155103289621 ];
			break;
		case "sinc 3":
			var coefs = [ 0.000013982738421036122,0.00003417348318904143,-0.00003764100101012808,-0.0001527896644241927,-0.000015895815917497444,0.000370938970103814,0.0003088857515219192,-0.0005643369112154606,-0.0010055286039766232,0.0003882378973523975,0.002082789124162701,0.0006793822397940184,-0.0030932614643256304,-0.00308104296445819,0.0030388323261787502,0.006724708875382101,-0.0005483107467958783,-0.010504114036064527,-0.005533903255275947,0.012091846641057785,0.015243191772241356,-0.008288444866135194,-0.02659484993886801,-0.003989779910778763,0.03510475757898855,0.026329950381553464,-0.033954239808363346,-0.0575922965125618,0.014370412875668459,0.0934573220760928,0.03608228157111943,-0.1271481305821821,-0.14995942055286846,0.15124582145939883,0.6144968487333189,0.8399992442793526,0.6144968487333189,0.15124582145939883,-0.14995942055286846,-0.1271481305821821,0.03608228157111943,0.0934573220760928,0.014370412875668459,-0.0575922965125618,-0.033954239808363346,0.026329950381553464,0.03510475757898855,-0.003989779910778763,-0.02659484993886801,-0.008288444866135194,0.015243191772241356,0.012091846641057785,-0.005533903255275947,-0.010504114036064527,-0.0005483107467958783,0.006724708875382101,0.0030388323261787502,-0.00308104296445819,-0.0030932614643256304,0.0006793822397940184,0.002082789124162701,0.0003882378973523975,-0.0010055286039766232,-0.0005643369112154606,0.0003088857515219192,0.000370938970103814,-0.000015895815917497444,-0.0001527896644241927,-0.00003764100101012808,0.00003417348318904143,0.000013982738421036122 ];
			break;
	}

	var coefsLen = coefs.length;
	var sigBaseIdx = 0;
	var outIdx = 0;
	for (var idx = 0; idx < sigLen; idx++) {
		coefsIdx = coefsLen;
		sigIdx = sigBaseIdx++;
		var acc0 = 0;
		var acc1 = 0;
		while (coefsIdx) {
			acc1 += signal[sigIdx] * coefs[--coefsIdx];
			if (coefsIdx)
				acc0 += signal[sigIdx++] * coefs[--coefsIdx];
		}
		signal2[outIdx++] = acc0;
		signal2[outIdx++] = acc1;
	}

	// plot time domain
	var signalPlot = [];
	for (var idx = 0; idx < sigLen/4; idx++)
		signalPlot.push([idx, signal2[idx]]);
	Flotr.draw($('container1-20101211'), [ signalPlot ], { yaxis: { max: 1.0, min: -1.0 } });

	// plot frequency domain and impulse response
	var fftSize = 1024;
	var mag = getMagDB(signal2, fftSize);
	var magPlot = [];
	for (var idx = 0, len = mag.length - 1; idx <= len; idx++)
		magPlot.push([ idx / len * 2, mag[idx] - 54.18 ]);

	if (showIR) {
		var impMag = getMagDB(coefs, fftSize);
		var impMagPlot = [];
		for (idx = 0, len = impMag.length - 1; idx <= len; idx++)
			impMagPlot.push([ idx / len * 2, impMag[idx] - 6.03 ]);	// half for 2x (and enough slop to fit under 0 plot top)

		Flotr.draw($('container2-20101211'), [ { data: impMagPlot/*, label: 'filter impulse response'*/, lines: { lineWidth: 2 }, shadowsize: 0 }, { data: magPlot/*, label: 'sine sweep'*/} ], { legend: {position: 'sw'}, xaxis: { noTicks: 4, max: 2.0 }, yaxis: { max: 0, min: -150 }, colors: [ '#C0D800', '#00A8F0' ] });
	}
	else
		Flotr.draw($('container2-20101211'), [ { data: magPlot/*, label: 'sine sweep'*/} ], { legend: {position: 'sw'}, xaxis: { noTicks: 4, max: 2.0 }, yaxis: { max: 1, min: -150 } } );

	// next
	if ((step > 0) && (freq < ((sigLen/2) - step)))
		setTimeout(function () { doFreq(freq + step, step) }, 40);
	else
		isRunning = false;
}


var irType = "sinc 2";
function doIRTypeSelect() {
	irType = document.params.irType.value;
	startSim();
}

var showIR = true;
function doShowCheckbox() {
	showIR = document.params.showIR.checked;
	startSim();
}

var isRunning = false;
function startSim() {
	if (!isRunning)
		doFreq(4, 4);
}

