From 8da4553cfdd9a8db1b6735469e4cf31e183b7332 Mon Sep 17 00:00:00 2001 From: MrBearman64 Date: Mon, 14 Jun 2021 15:59:11 +0200 Subject: [PATCH] Replace old dependency using ScriptNode Processor with AudioWorklet to record microphone --- app/recorder.js | 16 ++++++++++++ app/voice.js | 63 ++++++++++++++++++++++++++++++++++++++++++++--- package.json | 3 ++- webpack.config.js | 5 ++++ 4 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 app/recorder.js diff --git a/app/recorder.js b/app/recorder.js new file mode 100644 index 0000000..354a0b7 --- /dev/null +++ b/app/recorder.js @@ -0,0 +1,16 @@ +class RecorderWorkletProcessor extends AudioWorkletProcessor { + process(inputs, outputs, parameters) { + if (this._number === undefined) { + this._number = 1 + } + + const timestamp = Date.now() + const buffer = new Uint8Array(inputs[0][0].buffer) + this.port.postMessage({ eventType: 'data', audioBuffer: buffer, timestamp: timestamp, number: this._number }) + this._number++ + + return true + } +} + +registerProcessor('recorder-worklet', RecorderWorkletProcessor) diff --git a/app/voice.js b/app/voice.js index 22bad32..939d2f7 100644 --- a/app/voice.js +++ b/app/voice.js @@ -5,6 +5,7 @@ import keyboardjs from 'keyboardjs' import vad from 'voice-activity-detection' import DropStream from 'drop-stream' import { WorkerBasedMumbleClient } from './worker-client' +import workletUrl from './recorder.js' class VoiceHandler extends Writable { constructor (client, settings) { @@ -163,14 +164,68 @@ export class VADVoiceHandler extends VoiceHandler { } var theUserMedia = null +var oldBufferNumber = 0; export function initVoice (onData) { return window.navigator.mediaDevices.getUserMedia({ audio: true }).then((userMedia) => { theUserMedia = userMedia - var micStream = new MicrophoneStream(userMedia, { objectMode: true, bufferSize: 1024 }) - micStream.on('data', data => { - onData(Buffer.from(data.getChannelData(0).buffer)) - }) + setTimeout(() => audioContext().audioWorklet.addModule(workletUrl).then(() => { + console.log("AudioWorklet loaded!") + const recorderNode = new window.AudioWorkletNode( + audioContext(), + 'recorder-worklet' + ) + const microphone = audioContext().createMediaStreamSource(userMedia) + microphone.connect(recorderNode) + + const delay_buffer = [] + const delay_upperlimit = 30 + recorderNode.port.onmessage = e => { + if (e.data.eventType === 'data') { + // Determine the delay between audio packet delivery (recorder.postMessage) and reception and + // stop forwarding audio packets to the voice pipeline, when the average delay is higher than + // delay_upperlimit. + // This prevents possible audio glitches induced by too high delays. + const timestamp = e.data.timestamp + const delay = Date.now() - timestamp + const newBufferNumber = e.data.number + var avg = 0 + + delay_buffer.push(delay) + if (delay_buffer.length < 10) { + console.debug("Not enough values!") + oldBufferNumber = newBufferNumber + return + } + if (delay_buffer.length > 10) { + delay_buffer.shift() + } + for (var i = 0; i < delay_buffer.length; i++) { + avg += delay_buffer[i] + } + avg /= delay_buffer.length + if (avg >= delay_upperlimit) { + console.log("Average delay too high! " + avg) + oldBufferNumber = newBufferNumber + return + } + + // Only forward audio packets with higher buffer number (=new audio packets) + // to the voice pipe line + if (oldBufferNumber >= newBufferNumber) { + console.error("Old buffer occured!", oldBufferNumber, newBufferNumber) + } else { + if (oldBufferNumber + 1 != newBufferNumber) { + console.log("Buffer numbers don't fit!", oldBufferNumber, newBufferNumber) + } + oldBufferNumber = newBufferNumber + const audioData = e.data.audioBuffer + onData(audioData) + } + } + } + console.log("AudioWorkletNode initialized!") + }), 500) return userMedia }) } diff --git a/package.json b/package.json index 2e6dde8..6c803ac 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,8 @@ "web-audio-buffer-queue": "^1.1.0", "webpack": "^4.44.2", "webpack-cli": "^3.3.12", - "worker-loader": "^2.0.0" + "worker-loader": "^2.0.0", + "worklet-loader": "^1.0.0" }, "optionalDependencies": {} } diff --git a/webpack.config.js b/webpack.config.js index 17c9000..809ccbd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -96,6 +96,11 @@ module.exports = { test: /worker\.js$/, use: { loader: 'worker-loader' } }, + { + test: /recorder\.js$/, + loader: 'worklet-loader', + options: {name:'[hash].audioworklet.js'} + }, { enforce: 'post', test: /mumble-streams\/lib\/data.js/,