diff --git a/index.html b/index.html index c125377..a5ccf05 100644 --- a/index.html +++ b/index.html @@ -16,6 +16,7 @@ Last Bit Percent: %
FFT Size: 2^
Smoothing Time Constant:
+ Max Samples on Graph:

Sent

@@ -28,6 +29,7 @@

Received


Samples Per Bit: 0
+ Sample Rate: N/A per second. diff --git a/index.js b/index.js index c65a97d..d897a71 100644 --- a/index.js +++ b/index.js @@ -10,14 +10,15 @@ var receivedDataTextarea; var sentDataTextArea; var receivedGraph; var receivedData = []; -var MAX_DATA_POINTS = 200; +var MAX_DATA_POINTS = 300; var MAX_DATA = 0; +var pauseTimeoutId; // 20 to 20,000 - human var FREQUENCY_TONE = 18000; var FREQUENCY_HIGH = 400; var FREQUENCY_LOW = 500; -var FREQUENCY_DURATION = 200; +var FREQUENCY_DURATION = 100; var FREQUENCY_THRESHOLD = 200; var FFT_POWER = 10; var LAST_BIT_PERCENT = 0.8; @@ -26,6 +27,7 @@ var frequencyOverTime = []; var bitStart = []; var samplesPerBit = []; var bitSampleCount = 0; +var PAUSE = false; function handleWindowLoad() { // grab dom elements @@ -41,6 +43,11 @@ function handleWindowLoad() { bitSampleCount = 0; samplesPerBit.length = 0; }); + document.getElementById('max-samples-on-graph').value= MAX_DATA_POINTS; + document.getElementById('max-samples-on-graph').addEventListener('input', (event) => { + MAX_DATA_POINTS = parseInt(event.target.value); + }) + document.getElementById('bit-duration-text').value = FREQUENCY_DURATION; document.getElementById('amplitude-threshold-text').value = FREQUENCY_THRESHOLD; document.getElementById('frequency-high-text').value = FREQUENCY_HIGH; document.getElementById('frequency-low-text').value = FREQUENCY_LOW; @@ -69,7 +76,7 @@ function handleWindowLoad() { SMOOTHING_TIME_CONSTANT = parseFloat(event.target.value); if(analyser) analyser.smoothingTimeConstant = SMOOTHING_TIME_CONSTANT; }); - + document.getElementById('audio-context-sample-rate').innerText = getAudioContext().sampleRate.toLocaleString(); // wire up events sendButton.addEventListener('click', handleSendButtonClick); isListeningCheckbox.addEventListener('click', handleListeningCheckbox); @@ -101,6 +108,11 @@ function sendBits(bits) { audioContext.currentTime + offset ); } + console.log('removing pause'); + if(PAUSE && isListeningCheckbox.checked) { + PAUSE = false; + requestAnimationFrame(analyzeAudio); + } oscillator.connect(audioContext.destination); oscillator.start(); window.setTimeout(function() { oscillator.stop(); }, duration); @@ -177,7 +189,9 @@ function received(value) { let bitStarted; let bitHighStrength = []; let bitLowStrength = []; +let lastBitIndex = 0; function analyzeAudio() { + if(PAUSE) return; if(!analyser) return; if(!microphoneNode) return; var audioContext = getAudioContext(); @@ -204,6 +218,7 @@ function analyzeAudio() { return frequencyData[i]; } const sum = (total, value) => total + value; + function evaluateBit(highBits, lowBits) { let highCount = highBits.reduce( (count, highAmplitude, i) => @@ -218,18 +233,22 @@ function evaluateBit(highBits, lowBits) { const now = performance.now(); if(high || low) { if(bitStarted) { - if(now - bitStarted >= FREQUENCY_DURATION) { + var totalDuration = now - bitStarted; + var bitIndex = Math.floor(totalDuration / FREQUENCY_DURATION) + // next bit? + if(lastBitIndex !== bitIndex) { + lastBitIndex = bitIndex; samplesPerBit.unshift(bitSampleCount) received(evaluateBit(bitHighStrength, bitLowStrength)); bitHighStrength.length = 0; bitLowStrength.length = 0; - bitStarted = now; bitStart[0] = true; bitSampleCount = 1; } else { bitSampleCount++; } } else { + lastBitIndex = 0; bitSampleCount = 1; bitStarted = now; bitStart[0] = true; @@ -240,15 +259,23 @@ function evaluateBit(highBits, lowBits) { bitLowStrength.push(amplitude(FREQUENCY_LOW)); } else { if(bitStarted) { - // was bit long enough? - const duration = now - bitStarted; + var bitIndex = Math.floor((bitStarted - now) / FREQUENCY_DURATION); + var startTime = bitStarted + (FREQUENCY_DURATION * bitIndex); + var duration = now - startTime; if(duration >= FREQUENCY_DURATION * LAST_BIT_PERCENT) { samplesPerBit.unshift(bitSampleCount) received(evaluateBit(bitHighStrength, bitLowStrength)); } + lastBitIndex = 0; bitStarted = undefined; bitStart[0] = true; received('\n'); + if(!pauseTimeoutId) { + pauseTimeoutId = window.setTimeout(() => { + PAUSE = true; + pauseTimeoutId = undefined; + }, FREQUENCY_DURATION * 2); + } } } if(samplesPerBit.length > MAX_DATA_POINTS) { @@ -299,16 +326,16 @@ function drawFrequency(ctx, hz, color) { function drawFrequencyData() { const ctx = receivedGraph.getContext('2d'); const { width, height } = receivedGraph; - ctx.clearRect(0, 0, width, height); ctx.fillStyle = 'black'; ctx.fillRect(0, 0, width, height); const thresholdY = (1 - (FREQUENCY_THRESHOLD/MAX_DATA)) * height; ctx.strokeStyle = 'grey'; + ctx.beginPath(); ctx.moveTo(0, thresholdY); ctx.lineTo(width, thresholdY); ctx.stroke(); - drawBitStart(ctx, 'grey'); + drawBitStart(ctx, 'green'); drawFrequency(ctx, FREQUENCY_HIGH, 'red'); drawFrequency(ctx, FREQUENCY_LOW, 'blue'); }