Skip to content

Commit 3dc0869

Browse files
committed
checkpoint message adjustments for websockets
1 parent a497d94 commit 3dc0869

File tree

3 files changed

+35
-36
lines changed

3 files changed

+35
-36
lines changed

main.py

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
# github: https://github.com/jsalsman/webrec
1010

1111
from flask import Flask, request, render_template, redirect, send_from_directory
12+
from flask_socketio import SocketIO
1213
import sox # needs command line sox and the pysox package
1314
from datetime import datetime # for audio file timestamps
1415
import os # to delete old audio files
@@ -18,6 +19,7 @@
1819
log = lambda message: stderr.write(message + '\n') # ...and connect this
1920

2021
app = Flask(__name__)
22+
socketio = SocketIO(app)
2123

2224
@app.route('/') # redirect from / to /record
2325
def index():

static/recording-processor.js

+28-23
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ class RecordingProcessor extends AudioWorkletProcessor {
88
constructor(options) {
99
super();
1010

11-
this.sampleRate = 0;
11+
this.sampleRate = 16000;
1212
this.maxRecordingFrames = 0;
13-
this.numberOfChannels = 0;
13+
this.numberOfChannels = 1;
1414

1515
if (options && options.processorOptions) {
1616
const {
@@ -24,11 +24,12 @@ class RecordingProcessor extends AudioWorkletProcessor {
2424
this.numberOfChannels = numberOfChannels;
2525
}
2626

27-
this._recordingBuffer = new Array(this.numberOfChannels)
28-
.fill(new Float32Array(this.maxRecordingFrames));
27+
// Initialize _recordingBuffer as a Uint8Array
28+
this._recordingBuffer = new Uint8Array(this.maxRecordingFrames * 2);
2929

3030
this.recordedFrames = 0;
3131
this.isRecording = false;
32+
this.lastSentFrame = 0;
3233

3334
// We will use a timer to gate our messages; this one will publish at 30hz
3435
this.framesSinceLastPublish = 0;
@@ -55,26 +56,24 @@ class RecordingProcessor extends AudioWorkletProcessor {
5556
}
5657

5758
process(inputs, outputs, params) {
58-
for (let input = 0; input < 1; input++) {
59-
for (let channel = 0; channel < this.numberOfChannels; channel++) {
60-
for (let sample = 0; sample < inputs[input][channel].length; sample++) {
61-
const currentSample = inputs[input][channel][sample];
62-
63-
// Copy data to recording buffer.
64-
if (this.isRecording) {
65-
this._recordingBuffer[channel][sample+this.recordedFrames] =
66-
currentSample;
67-
}
68-
69-
// Pass data directly to output, unchanged.
70-
outputs[input][channel][sample] = currentSample;
71-
72-
// Sum values for visualizer
73-
this.sampleSum += Math.abs(currentSample); // CHANGED to absolute values [0, 1]
74-
}
59+
// Assuming we are only interested in the first channel 0 // TODO: convert to mono properly
60+
let inputBuffer = inputs[0][0];
61+
for (let sample = 0; sample < inputBuffer.length; ++sample) {
62+
let currentSample = inputBuffer[sample];
63+
64+
if (this.isRecording) {
65+
// Copy data to recording buffer
66+
let signed16bits = Math.max(-32768,
67+
Math.min(32767, currentSample * 32768.0));
68+
let index = (sample + this.recordedFrames) * 2;
69+
this._recordingBuffer[index] = signed16bits & 255; // low byte, little endian
70+
this._recordingBuffer[index + 1] = (signed16bits >> 8) & 255; // high
7571
}
72+
73+
// Sum values for visualizer
74+
this.sampleSum += Math.abs(currentSample); // CHANGED to absolute values [0, 1]
7675
}
77-
76+
7877
const shouldPublish = this.framesSinceLastPublish >= this.publishInterval;
7978

8079
// Validate that recording hasn't reached its limit.
@@ -84,10 +83,16 @@ class RecordingProcessor extends AudioWorkletProcessor {
8483

8584
// Post a recording recording length update on the clock's schedule
8685
if (shouldPublish) {
86+
let bufferSlice = this._recordingBuffer.slice(
87+
this.lastSentFrame * 2, this.recordedFrames * 2);
88+
8789
this.port.postMessage({
88-
message: 'UPDATE_RECORDING_LENGTH',
90+
message: 'UPDATE_RECORDING',
8991
recordingLength: this.recordedFrames,
92+
bufferSlice: bufferSlice,
9093
});
94+
95+
this.lastSentFrame = this.recordedFrames;
9196
}
9297
} else {
9398
// Let the rest of the app know the limit was reached.

templates/record.html

+5-13
Original file line numberDiff line numberDiff line change
@@ -293,20 +293,10 @@ <h3>Apple Safari users: <a
293293
let length = event.data.recordingLength;
294294
console.log('Seconds Recorded: ' + length / 16000);
295295

296-
// Convert the floating point array buffer to audio/l16 PCM
297-
let buffer = event.data.buffer[0];
298-
let pcmData = new Uint8Array(length * 2);
299-
for (let index = 0; index < length; ++index) {
300-
let sample = buffer[index];
301-
sample = sample * 32768.0;
302-
sample = Math.max(-32768, Math.min(32767, sample));
303-
pcmData[index * 2] = sample & 255; // low byte, little endian
304-
pcmData[index * 2 + 1] = (sample >> 8) & 255; // high byte
305-
}
306-
307296
// Put the raw PCM data in a blob and upload it
308297
let formData = new FormData();
309-
let blob = new Blob([pcmData], { type: 'audio/l16' });
298+
let blob = new Blob([event.data.buffer.slice(0, length * 2)],
299+
{ type: 'audio/l16' });
310300
formData.append('audio', blob, 'audio.raw');
311301
fetch('/upload-audio', {
312302
method: 'POST',
@@ -328,11 +318,13 @@ <h3>Apple Safari users: <a
328318
stopRecording();
329319

330320
// Set the duration meter
331-
} else if (event.data.message === 'UPDATE_RECORDING_LENGTH') {
321+
} else if (event.data.message === 'UPDATE_RECORDING') {
332322
let seconds = event.data.recordingLength / 16000
333323
document.getElementById('full').value = seconds.toFixed(2);
334324
document.getElementById('secs').textContent =
335325
seconds.toFixed(1).padStart(4, '0');
326+
327+
// NEW: stream the latest chunk: event.data.bufferSlice
336328

337329
// Set the audio level meter
338330
} else if (event.data.message === 'UPDATE_VISUALIZERS') {

0 commit comments

Comments
 (0)