pause on end of data, resume on send

This commit is contained in:
Lewis Moten
2024-05-02 00:28:37 -04:00
parent e902b236cb
commit a47074eeca
2 changed files with 38 additions and 9 deletions

View File

@@ -16,6 +16,7 @@
Last Bit Percent: <input id="last-bit-percent" type="number" min="0" max="100" value="90">%<br> Last Bit Percent: <input id="last-bit-percent" type="number" min="0" max="100" value="90">%<br>
FFT Size: 2^<input id="fft-size-power-text" type="number" min="5" max="15" value="90"><br> FFT Size: 2^<input id="fft-size-power-text" type="number" min="5" max="15" value="90"><br>
Smoothing Time Constant: <input id="smoothing-time-constant-text" type="number" min="0.00" max="1.00" step="0.01" value="0.00"><br> Smoothing Time Constant: <input id="smoothing-time-constant-text" type="number" min="0.00" max="1.00" step="0.01" value="0.00"><br>
Max Samples on Graph: <input id="max-samples-on-graph" type="number" min="100" max="2000"><br>
<input type="text" id="text-to-send"> <input type="text" id="text-to-send">
<button id="send-button">Send</button> <button id="send-button">Send</button>
<h2>Sent</h2> <h2>Sent</h2>
@@ -28,6 +29,7 @@
<h2>Received</h2> <h2>Received</h2>
<textarea id="received-data" rows="10" cols="40"></textarea><br /> <textarea id="received-data" rows="10" cols="40"></textarea><br />
Samples Per Bit: <span id="samples-per-bit">0</span><br> Samples Per Bit: <span id="samples-per-bit">0</span><br>
Sample Rate: <span id="audio-context-sample-rate">N/A</span> per second.
</div> </div>
</div> </div>
<canvas id="received-graph" width="800" height="100"></canvas> <canvas id="received-graph" width="800" height="100"></canvas>

View File

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