From e44ac6a37426fdd2b43d2720068accb0885c3072 Mon Sep 17 00:00:00 2001 From: Lewis Moten Date: Fri, 3 May 2024 15:38:55 -0400 Subject: [PATCH] prefix data stream with the number of bytes being sent. --- index.js | 189 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 144 insertions(+), 45 deletions(-) diff --git a/index.js b/index.js index 259973f..0d85da8 100644 --- a/index.js +++ b/index.js @@ -35,6 +35,9 @@ var PAUSE = false; var PAUSE_AFTER_END = true; var PACKET_SIZE_BITS = 10; +const packetBits = []; +let packetDataByteCount = -1; + function handleWindowLoad() { // grab dom elements sendButton = document.getElementById('send-button'); @@ -173,8 +176,6 @@ function sendBits(bits) { } else if(byteCount > (1 << PACKET_SIZE_BITS)) { logSent(`Can not transfer more than ${(1 << PACKET_SIZE_BITS)} bytes.`); return; - } else { - logSent(bits.join('')); } const packetLength = ((byteCount - 1) >>> 0) @@ -183,6 +184,7 @@ function sendBits(bits) { .split('') .map(Number); bits.unshift(...packetLength); + logSent(bits.join('')); var audioContext = getAudioContext(); const channels = getChannels(); @@ -245,18 +247,39 @@ function resumeGraph() { PAUSE = false; } } +function getInteger(start, end, samples) { + let value = 0; + let valueIndex = 0; + for(let i = 0; i < samples.length; i++) { + for(let j = 0; j < samples[i].pairs.length; j++) { + if(b >= start) { + value += getBit(samples[i].pairs[j]) << valueIndex++; + if(b >= end) return bits; + } + b++; + } + } + return bits; +} + function collectSample() { const time = performance.now(); const frequencies = new Uint8Array(analyser.frequencyBinCount); const length = audioContext.sampleRate / analyser.fftSize; + let processSegment = false; const { hasSignal: hadPriorSignal, - streamStarted: initialStreamStart = time, - streamEnded: priorStreamEnded, - bitIndex: priorBitIndex = -1 + streamStarted: initialStreamStart = -1, + streamEnded: priorStreamEnded = -1, + segmentIndex: priorSegmentIndex = -1 } = frequencyOverTime[0] ?? {} analyser.getByteFrequencyData(frequencies); - const data = { time, frequencies, length }; + const data = { + time, + frequencies, + length, + streamEnded: priorStreamEnded + }; let hasSignal = false; data.pairs = getChannels().map(([low, high], i) => { const lowAmp = frequencies[Math.round(low / length)]; @@ -277,29 +300,36 @@ function collectSample() { if(hadPriorSignal) { // continued bit stream data.streamStarted = initialStreamStart; + + // proposed end + data.streamEnded = priorStreamEnded; } else { // new bit stream data.streamStarted = time; + // clear last packet + packetBits.length = 0; + packetDataByteCount = 0; } + // number of bit in the stream - const bitIndex = data.bitIndex = Math.floor((time - initialStreamStart) / FREQUENCY_DURATION); - if(priorBitIndex !== bitIndex && priorBitIndex !== -1) { - processBitsReceived(); + const segmentIndex = data.segmentIndex = Math.floor((time - initialStreamStart) / FREQUENCY_DURATION); + if(priorSegmentIndex !== segmentIndex && priorSegmentIndex !== -1) { + processSegment = true; } } else { - data.bitIndex = -1; + data.segmentIndex = -1; if(hadPriorSignal) { // just stopped data.streamStarted = -1; - data.streamEnded = time; - // update all prior values with stream end - for(let i = 0; i < frequencyOverTime.length; i++) { - if(frequencyOverTime[i].streamStarted === initialStreamStart) { - frequencyOverTime[i].streamEnded = time; - } - } - processBitsReceived(); - received('\n'); + data.streamEnded = -1; + // update all prior values with stream end if they don't have one + frequencyOverTime + .filter(fov => fov.streamStarted === initialStreamStart) + .filter(fov => fov.streamEnded === -1) + .forEach(fov => { + fov.streamEnded === time + }); + processSegment = true; if(PAUSE_AFTER_END && !pauseTimeoutId) { pauseTimeoutId = window.setTimeout(() => { pauseTimeoutId = undefined; @@ -308,30 +338,46 @@ function collectSample() { } } else { // continued stopping (or never started) - data.streamEnded = priorStreamEnded; + data.streamEnded = -1; } } frequencyOverTime.unshift(data); + if(processSegment) processSegmentReceived(); truncateGraphData(); } -function processBitsReceived() { + +function evaluateBit(samples, segment, channel) { + const started = samples.find(s => s.streamStarted > 0).streamStarted; + const bitSamples = samples.filter(sample => { + return sample.time >= started + (segment * FREQUENCY_DURATION) && + sample.time < started + ((segment + 1) * FREQUENCY_DURATION) + }).map(samples => samples.pairs[channel]) + .reduce((bitSamples, { isHigh, isMissing }) => { + bitSamples.total++; + if(isHigh) bitSamples.highCount++; + }, {highCount: 0, total: 0}); + return bitSamples.highCount >= bitSamples.total / 2 ? 1 : 0; +} + +function processSegmentReceived() { const { - bitIndex, + segmentIndex, streamStarted, pairs: { length: channelCount } } = frequencyOverTime[0]; const bits = frequencyOverTime.filter(f => - f.bitIndex === bitIndex && + f.segmentIndex === segmentIndex && f.streamStarted === streamStarted ); const bitEnded = bits[0].time; - const bitStarted = streamStarted + (FREQUENCY_DURATION * bitIndex); + const bitStarted = streamStarted + (FREQUENCY_DURATION * segmentIndex); const bitDuration = bitEnded - bitStarted; - if(bitDuration < FREQUENCY_DURATION * LAST_BIT_PERCENT) { - return; - } + + // if(bitDuration < FREQUENCY_DURATION * LAST_BIT_PERCENT) { + // return; + // } const channels = new Array(channelCount).fill(0).map(() => ({isHigh: 0, isLow: 0, isMissing: 0})); @@ -342,11 +388,54 @@ function processBitsReceived() { else channels[i].isLow++; }) }); - const bitString = channels.map(({isHigh, isLow, isMissing}) => { + const bitValues = channels.map(({isHigh, isLow, isMissing}) => { if(isMissing > isHigh + isLow) return '.'; return isHigh > isLow ? '1' : '0'; - }).join(''); - received(bitString + '\n'); + }); + + packetBits.push(...bitValues); + + if(packetBits.length >= 10) { + // we can evaluate how many bytes are comming + const dataLengthIndex = Math.floor(10 / channelCount); + if(dataLengthIndex === segmentIndex) { + // we just got the bits we needed + packetDataByteCount = 1 + packetBits + .slice(0, PACKET_SIZE_BITS) + .reduce((value, bit) => (value << 1) | bit); + // let's get the end time + const totalBits = (packetDataByteCount * 8) + PACKET_SIZE_BITS; + const segments = Math.ceil(totalBits / channelCount); + const duration = segments * FREQUENCY_DURATION; + const streamEnded = streamStarted + duration; + console.log({ + tenBitNum: packetBits + .slice(0, PACKET_SIZE_BITS).join(''), + packetDataByteCount, + PACKET_SIZE_BITS, + totalBits, + segments, + streamStarted, + duration, + streamEnded + }); + // update everyones proposed end time + frequencyOverTime + .filter(fot => fot.streamStarted === streamStarted) + .forEach(fot => { + fot.streamEnded = streamEnded + }); + } + // remove phantom bits + const totalBits = (packetDataByteCount * 8) + PACKET_SIZE_BITS; + if(packetBits.length > totalBits) { + const excess = packetBits.length % totalBits; + packetBits.length = totalBits; + bitValues.length = bitValues.length - excess; + } + } + + received(bitValues.join('') + '\n'); } function resetGraphData() { frequencyOverTime.length = 0; @@ -386,6 +475,9 @@ function textToBits(text) { return bits.join('').split('').map(Number); } function handleSendButtonClick() { + receivedDataTextarea.value = ''; + sentDataTextArea.value = ''; + const text = document.getElementById('text-to-send').value; sendBits(textToBits(text)); } @@ -445,7 +537,7 @@ let lastBitStarted; let bitEnded; let bitHighStrength = []; let bitLowStrength = []; -let lastBitIndex = 0; +let lastSegmentIndex = 0; function canHear(hz, {frequencies, length}) { var i = Math.round(hz / length); @@ -457,14 +549,14 @@ function amplitude(hz, {frequencies, length}) { } const sum = (total, value) => total + value; -function evaluateBit(highBits, lowBits) { - let highCount = highBits.reduce( - (count, highAmplitude, i) => - count += highAmplitude > lowBits[i] ? 1 : 0 - , 0 - ); - return highCount >= (highBits.length / 2) ? '1' : '0'; -} +// function evaluateBit(highBits, lowBits) { +// let highCount = highBits.reduce( +// (count, highAmplitude, i) => +// count += highAmplitude > lowBits[i] ? 1 : 0 +// , 0 +// ); +// return highCount >= (highBits.length / 2) ? '1' : '0'; +// } function avgLabel(array) { const values = array.filter(v => v > 0); @@ -476,12 +568,19 @@ function drawBitDurationLines(ctx, color) { const newest = frequencyOverTime[0].time; const duration = FREQUENCY_DURATION * MAX_BITS_DISPLAYED_ON_GRAPH; - const streamTimes = frequencyOverTime.filter((v, i, a) => { - return v.streamStarted !== -1 && ( - i === 0 || - a[i-1].streamStarted !== v.streamStarted - ) - }); + const streamTimes = frequencyOverTime.filter(({ + streamStarted + }) => { + return streamStarted !== -1 + }).reduce((unique, { + streamStarted, + streamEnded = newest + }) => { + if(unique.every(u => u.streamStarted != streamStarted)) { + unique.push({streamStarted, streamEnded}) + } + return unique; + }, []); ctx.strokeStyle = color; streamTimes.forEach(({ streamStarted, streamEnded = newest}) => {