mirror of
https://github.com/lewismoten/data-over-audio.git
synced 2026-04-20 13:16:23 +08:00
display segment index and sample count for each segment
This commit is contained in:
101
index.js
101
index.js
@@ -15,7 +15,8 @@ var pauseTimeoutId;
|
|||||||
var sampleIntervalId;
|
var sampleIntervalId;
|
||||||
|
|
||||||
var TEXT_TO_SEND = "U";
|
var TEXT_TO_SEND = "U";
|
||||||
var MAX_BITS_DISPLAYED_ON_GRAPH = 115;
|
var RANDOM_COUNT = 64;
|
||||||
|
var MAX_BITS_DISPLAYED_ON_GRAPH = 58;
|
||||||
var SEGMENT_DURATION = 30;
|
var SEGMENT_DURATION = 30;
|
||||||
var AMPLITUDE_THRESHOLD_PERCENT = .75;
|
var AMPLITUDE_THRESHOLD_PERCENT = .75;
|
||||||
var AMPLITUDE_THRESHOLD = 160;
|
var AMPLITUDE_THRESHOLD = 160;
|
||||||
@@ -27,6 +28,7 @@ var FREQUENCY_RESOLUTION_MULTIPLIER = 2;
|
|||||||
var SMOOTHING_TIME_CONSTANT = 0;
|
var SMOOTHING_TIME_CONSTANT = 0;
|
||||||
var HAMMING_ERROR_CORRECTION = true;
|
var HAMMING_ERROR_CORRECTION = true;
|
||||||
|
|
||||||
|
var LAST_STREAM_STARTED;
|
||||||
var SAMPLE_DELAY_MS = 1;
|
var SAMPLE_DELAY_MS = 1;
|
||||||
var frequencyOverTime = [];
|
var frequencyOverTime = [];
|
||||||
var bitStart = [];
|
var bitStart = [];
|
||||||
@@ -46,7 +48,7 @@ let packetDataByteCount = -1;
|
|||||||
|
|
||||||
function handleWindowLoad() {
|
function handleWindowLoad() {
|
||||||
const printable = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890`-=~!@#$%^&*()_+[]\\{}|;':\",./<>?";
|
const printable = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890`-=~!@#$%^&*()_+[]\\{}|;':\",./<>?";
|
||||||
TEXT_TO_SEND = new Array(128).fill(0).map(() => printable[Math.floor(Math.random() * printable.length)]).join('');
|
TEXT_TO_SEND = new Array(RANDOM_COUNT).fill(0).map(() => printable[Math.floor(Math.random() * printable.length)]).join('');
|
||||||
|
|
||||||
// grab dom elements
|
// grab dom elements
|
||||||
sendButton = document.getElementById('send-button');
|
sendButton = document.getElementById('send-button');
|
||||||
@@ -209,7 +211,7 @@ function drawChannels() {
|
|||||||
const nyquistFrequency = audioContext.sampleRate / 2;
|
const nyquistFrequency = audioContext.sampleRate / 2;
|
||||||
const frequencySegments = Math.floor(nyquistFrequency / frequencyResolution);
|
const frequencySegments = Math.floor(nyquistFrequency / frequencyResolution);
|
||||||
|
|
||||||
for(let i = 0; i < channelCount; i++) {//xxx
|
for(let i = 0; i < channelCount; i++) {
|
||||||
const [low, high] = channels[i];
|
const [low, high] = channels[i];
|
||||||
let top = channelHeight * i;
|
let top = channelHeight * i;
|
||||||
ctx.fillStyle = 'black';
|
ctx.fillStyle = 'black';
|
||||||
@@ -365,10 +367,15 @@ function sendBits(bits) {
|
|||||||
const channel = i % channelCount;
|
const channel = i % channelCount;
|
||||||
const segment = Math.floor(i / channelCount);
|
const segment = Math.floor(i / channelCount);
|
||||||
var offset = ((segment * SEGMENT_DURATION)/1000);
|
var offset = ((segment * SEGMENT_DURATION)/1000);
|
||||||
|
var offset2 = (((segment+1) * SEGMENT_DURATION)/1000) - (1/100000);
|
||||||
oscillators[channel].frequency.setValueAtTime(
|
oscillators[channel].frequency.setValueAtTime(
|
||||||
channels[channel][isHigh ? 1 : 0],
|
channels[channel][isHigh ? 1 : 0],
|
||||||
currentTime + offset
|
currentTime + offset
|
||||||
);
|
);
|
||||||
|
oscillators[channel].frequency.setValueAtTime(
|
||||||
|
channels[channel][isHigh ? 1 : 0],
|
||||||
|
currentTime + offset2
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// start sending our signal
|
// start sending our signal
|
||||||
@@ -379,6 +386,7 @@ function sendBits(bits) {
|
|||||||
const channel = i % channelCount;
|
const channel = i % channelCount;
|
||||||
const segment = Math.floor(i / channelCount);
|
const segment = Math.floor(i / channelCount);
|
||||||
const offset = ((segment * SEGMENT_DURATION) / 1000);
|
const offset = ((segment * SEGMENT_DURATION) / 1000);
|
||||||
|
oscillators[channel].frequency.setValueAtTime(0, currentTime + offset);
|
||||||
oscillators[channel].stop(currentTime + offset);
|
oscillators[channel].stop(currentTime + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,6 +475,7 @@ function collectSample() {
|
|||||||
} else {
|
} else {
|
||||||
// new bit stream
|
// new bit stream
|
||||||
data.streamStarted = time;
|
data.streamStarted = time;
|
||||||
|
LAST_STREAM_STARTED = time;
|
||||||
// clear last packet
|
// clear last packet
|
||||||
packetReceivedBits.length = 0;
|
packetReceivedBits.length = 0;
|
||||||
packetDataByteCount = 0;
|
packetDataByteCount = 0;
|
||||||
@@ -554,9 +563,6 @@ function processSegmentReceived(streamStarted, segmentIndex) {
|
|||||||
if((sampleDuration / SEGMENT_DURATION) < LAST_SEGMENT_PERCENT) return;
|
if((sampleDuration / SEGMENT_DURATION) < LAST_SEGMENT_PERCENT) return;
|
||||||
|
|
||||||
const bitValues = GET_SEGMENT_BITS(streamStarted, segmentIndex);
|
const bitValues = GET_SEGMENT_BITS(streamStarted, segmentIndex);
|
||||||
|
|
||||||
|
|
||||||
console.log("%s Received: %s from %s samples", segmentIndex, bitValues.join(''), samples.length);
|
|
||||||
packetReceivedBits.push(...bitValues);
|
packetReceivedBits.push(...bitValues);
|
||||||
|
|
||||||
const encodingRatio = HAMMING_ERROR_CORRECTION ? 7/4 : 1;
|
const encodingRatio = HAMMING_ERROR_CORRECTION ? 7/4 : 1;
|
||||||
@@ -660,14 +666,21 @@ const textExpectorReducer = expected => (all, bit, i, bits) => {
|
|||||||
const char = String.fromCharCode(ascii);
|
const char = String.fromCharCode(ascii);
|
||||||
const charIndex = Math.floor((i - PACKET_SIZE_BITS) / 8);
|
const charIndex = Math.floor((i - PACKET_SIZE_BITS) / 8);
|
||||||
if(char !== expected[charIndex]) {
|
if(char !== expected[charIndex]) {
|
||||||
all += '<span class="bit-wrong">' + char + '</span>';
|
all += '<span class="bit-wrong">' + htmlEncode(printable(char)) + '</span>';
|
||||||
} else {
|
} else {
|
||||||
all += char;
|
all += htmlEncode(printable(char));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return all;
|
return all;
|
||||||
}
|
}
|
||||||
|
function printable(text) {
|
||||||
|
return text.replace(/[\x00-\x1f\x7f-\x9f]/g, '.');
|
||||||
|
}
|
||||||
|
function htmlEncode(text) {
|
||||||
|
const element = document.createElement('div');
|
||||||
|
element.textContent = text;
|
||||||
|
return element.innerHTML;
|
||||||
|
}
|
||||||
function resetGraphData() {
|
function resetGraphData() {
|
||||||
frequencyOverTime.length = 0;
|
frequencyOverTime.length = 0;
|
||||||
bitStart.length = 0;
|
bitStart.length = 0;
|
||||||
@@ -786,6 +799,75 @@ function avgLabel(array) {
|
|||||||
if(values.length === 0) return 'N/A';
|
if(values.length === 0) return 'N/A';
|
||||||
return (values.reduce((t, v) => t + v, 0) / values.length).toFixed(2)
|
return (values.reduce((t, v) => t + v, 0) / values.length).toFixed(2)
|
||||||
}
|
}
|
||||||
|
function drawSegmentIndexes(ctx) {
|
||||||
|
if(!LAST_STREAM_STARTED) return;
|
||||||
|
const { width, height } = receivedGraph;
|
||||||
|
const fot = frequencyOverTime.find(fot => fot.streamStarted === LAST_STREAM_STARTED);
|
||||||
|
const newest = frequencyOverTime[0].time;
|
||||||
|
const channelCount = frequencyOverTime[0].pairs.length;
|
||||||
|
let {
|
||||||
|
streamStarted,
|
||||||
|
streamEnded = newest
|
||||||
|
} = fot ?? {
|
||||||
|
streamStarted: LAST_STREAM_STARTED,
|
||||||
|
streamEnded: newest
|
||||||
|
};
|
||||||
|
if(streamEnded === -1) streamEnded = newest;
|
||||||
|
let segmentIndex = 0;
|
||||||
|
ctx.fontSize = 24;
|
||||||
|
|
||||||
|
// determine max segments to prevent infinite loop later
|
||||||
|
let maxBits = ((1 << PACKET_SIZE_BITS) * 8) + PACKET_SIZE_BITS;
|
||||||
|
if(HAMMING_ERROR_CORRECTION) maxBits *= 7/4;
|
||||||
|
let maxSegments = Math.ceil(maxBits / channelCount);
|
||||||
|
|
||||||
|
// loop through each index
|
||||||
|
while(true) {
|
||||||
|
let segmentStart = streamStarted + (segmentIndex * SEGMENT_DURATION);
|
||||||
|
// if(segmentStart > streamEnded) break; // stream ended
|
||||||
|
|
||||||
|
let segmentEnd = segmentStart + SEGMENT_DURATION;
|
||||||
|
// find where the index is on the graph
|
||||||
|
const rightX = getTimeX(segmentStart, newest);
|
||||||
|
const leftX = getTimeX(segmentEnd, newest);
|
||||||
|
const segmentWidth = rightX - leftX;
|
||||||
|
if(leftX > width) continue; // too far in past
|
||||||
|
if(rightX < 0) break; // in the future
|
||||||
|
|
||||||
|
// Draw segment index
|
||||||
|
let text = segmentIndex.toString();
|
||||||
|
let size = ctx.measureText(text);
|
||||||
|
let textX = leftX + (segmentWidth / 2) - (size.width / 2);
|
||||||
|
ctx.strokeStyle = 'black';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.textBaseline = 'bottom';
|
||||||
|
ctx.strokeText(text, textX, height);
|
||||||
|
ctx.fillStyle = segmentStart > streamEnded ? 'grey' : 'white';
|
||||||
|
ctx.fillText(text, textX, height);
|
||||||
|
|
||||||
|
// draw sample count
|
||||||
|
const sampleCount = frequencyOverTime
|
||||||
|
.filter(fot =>
|
||||||
|
fot.streamStarted === streamStarted &&
|
||||||
|
fot.segmentIndex === segmentIndex
|
||||||
|
)
|
||||||
|
.length;
|
||||||
|
|
||||||
|
text = sampleCount.toString();
|
||||||
|
size = ctx.measureText(text);
|
||||||
|
textX = leftX + (segmentWidth / 2) - (size.width / 2);
|
||||||
|
ctx.strokeStyle = 'black';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.textBaseline = 'top';
|
||||||
|
ctx.strokeText(text, textX, 5);
|
||||||
|
ctx.fillStyle = 'white';
|
||||||
|
ctx.fillText(text, textX, 5);
|
||||||
|
|
||||||
|
segmentIndex++;
|
||||||
|
// break out of potential infinite loop
|
||||||
|
if(segmentIndex >= maxSegments) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
function drawBitDurationLines(ctx, color) {
|
function drawBitDurationLines(ctx, color) {
|
||||||
const { width, height } = receivedGraph;
|
const { width, height } = receivedGraph;
|
||||||
const newest = frequencyOverTime[0].time;
|
const newest = frequencyOverTime[0].time;
|
||||||
@@ -1048,6 +1130,7 @@ function drawFrequencyData() {
|
|||||||
drawFrequencyLineGraph(ctx, high, `hsl(${hue}, 100%, 50%)`, 2, false);
|
drawFrequencyLineGraph(ctx, high, `hsl(${hue}, 100%, 50%)`, 2, false);
|
||||||
drawFrequencyLineGraph(ctx, low, `hsl(${hue}, 100%, 25%)`, 1, true);
|
drawFrequencyLineGraph(ctx, low, `hsl(${hue}, 100%, 25%)`, 1, true);
|
||||||
});
|
});
|
||||||
|
drawSegmentIndexes(ctx);
|
||||||
|
|
||||||
requestAnimationFrame(drawFrequencyData);
|
requestAnimationFrame(drawFrequencyData);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user