Skip to content
This repository was archived by the owner on Mar 8, 2026. It is now read-only.

Commit e4f70b0

Browse files
Merge pull request #11 from ag2ai/voice-hooks
WebRTC Voice hooks
2 parents b9fa8b6 + cf5ea1b commit e4f70b0

File tree

3 files changed

+42
-81
lines changed

3 files changed

+42
-81
lines changed

src/mediaUtils.ts

Lines changed: 0 additions & 43 deletions
This file was deleted.

src/soundWorklet.ts

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,64 +8,59 @@ class ResamplerProcessor extends AudioWorkletProcessor {
88
this.ratio = this.inputSampleRate / this.outputSampleRate;
99
this.resampledBuffer = []; // Store resampled data to send to the main thread
1010
this.processCount = 0; //Counter.
11-
this.port.postMessage({ type: 'log', data: "constructor" });
11+
this.pcmData = new Int16Array(8196);
12+
this.pcmIndex = 0;
1213
}
1314
1415
process(inputs, outputs, parameters) {
1516
const input = inputs[0];
1617
const output = outputs[0];
1718
18-
this.port.postMessage({ type: 'log', data: "process" });
19-
2019
if (input.length === 0 || input[0].length === 0) {
2120
return true; // No input, nothing to do
2221
}
2322
2423
const inputChannel = input[0];
25-
const outputChannel = output[0];
26-
27-
// Pass the original input data to the output
28-
for (let i = 0; i < inputChannel.length; i++) {
29-
outputChannel[i] = inputChannel[i]; // Pass through original data
30-
}
3124
3225
// Resample the data
33-
const resampledData = this.resample(inputChannel);
34-
this.resampledBuffer = this.resampledBuffer.concat(Array.from(resampledData));
26+
this.resample(inputChannel);
3527
3628
this.processCount++; //Increment process count
3729
let messageRate = 100; //Message sent every 100 process calls.
3830
// Send the resampled data to the main thread (send in chunks)
39-
if (this.processCount % messageRate === 0 && this.resampledBuffer.length > 0) {
40-
//Limit what we send.
41-
let sendCount = 256;
42-
let send = this.resampledBuffer.slice(0, sendCount);
43-
44-
this.port.postMessage({ type: 'resampledData', data: send }); //Send only part of the resampledBuffer;
45-
46-
//Purge the buffer.
47-
this.resampledBuffer = this.resampledBuffer.slice(sendCount);
31+
if (this.processCount % messageRate === 0 && this.pcmIndex > 0) {
32+
// Convert Int16Array to a binary buffer (ArrayBuffer)
33+
const pcmBuffer = new ArrayBuffer(this.pcmIndex * 2); // 2 bytes per sample
34+
const pcmView = new DataView(pcmBuffer);
35+
for (let i = 0; i < this.pcmIndex; i++) {
36+
const pcmData_i = this.pcmData[i];
37+
if (pcmData_i) {
38+
pcmView.setInt16(i * 2, pcmData_i, true); // true means little-endian
39+
}
40+
}
41+
this.port.postMessage({ type: 'resampledData', data: pcmBuffer });
42+
this.pcmData = new Int16Array(8196);
43+
this.pcmIndex = 0;
4844
}
4945
5046
return true;
5147
}
5248
5349
resample(inputChannel) {
54-
const resampledData = [];
5550
let inputIndex = 0;
56-
57-
for (let outputIndex = 0; outputIndex < inputChannel.length / this.ratio; outputIndex++) {
51+
const howMany = Math.floor(inputChannel.length / this.ratio)
52+
let outputIndex;
53+
for (outputIndex = 0; outputIndex < howMany; outputIndex++) {
5854
const inputIndexFloat = outputIndex * this.ratio;
5955
inputIndex = Math.floor(inputIndexFloat);
6056
6157
if (inputIndex < inputChannel.length) {
62-
resampledData.push(inputChannel[inputIndex]); // Replace with interpolation
58+
this.pcmData[this.pcmIndex+outputIndex] = Math.max(-32768, Math.min(32767, inputChannel[inputIndex] * 32767));
6359
} else {
6460
break; //No more data, break out of loop
6561
}
6662
}
67-
68-
return resampledData;
63+
this.pcmIndex += outputIndex;
6964
}
7065
}
7166

src/webRTC.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,17 @@ export class WebRTC {
1919
private microphone?: MediaStreamTrack;
2020
private ws: WebSocket | null;
2121
private pc: RTCPeerConnection | null;
22+
private connected: boolean;
2223
public onDisconnect: () => void;
2324

2425
constructor(ag2SocketUrl: string, microphone?: MediaStreamTrack) {
2526
this.ag2SocketUrl = ag2SocketUrl;
2627
this.microphone = microphone;
2728
this.ws = null;
2829
this.pc = null;
30+
this.connected = false;
2931
this.onDisconnect = () => {
32+
this.connected = false;
3033
console.log('WebRTC disconnected');
3134
};
3235
}
@@ -159,13 +162,8 @@ export class WebRTC {
159162
});
160163

161164
const audioContext = new window.AudioContext();
162-
163-
// 2. Create a MediaStreamSource
164165
const source = audioContext.createMediaStreamSource(ms);
165-
166-
// 3. Get the sampling rate from the AudioContext
167166
const sampleRate = audioContext.sampleRate;
168-
169167
console.log('Sampling Rate:', sampleRate);
170168
console.log('ResamplerSrc', ResamplerProcessorSrc);
171169
await audioContext.audioWorklet.addModule(ResamplerProcessorSrc);
@@ -178,18 +176,28 @@ export class WebRTC {
178176
},
179177
},
180178
);
181-
source.connect(resamplerNode).connect(audioContext.destination);
179+
source.connect(resamplerNode);
182180
resamplerNode.port.onmessage = (event) => {
183-
console.log('Received message from resampler node', event);
181+
//console.log('Received message from resampler node', event);
184182
if (event.data.type === 'resampledData') {
185-
const resampledData = event.data.data; //Get resampledData
186-
// Now you have access to the resampled data in your main thread.
187-
// You can do whatever you need with it (e.g., visualize it, record it, etc.).
188-
console.log('Received resampled data:', resampledData);
183+
const audioData = event.data.data; //Get resampledData
184+
console.log('Received resampled data len:', audioData.length);
185+
186+
const byteArray = new Uint8Array(audioData); // Create a Uint8Array view
187+
const bufferString = String.fromCharCode(...byteArray); // convert each byte of the buffer to a character
188+
const audioBase64String = btoa(bufferString); // Apply base64
189+
190+
const audioMessage = {
191+
type: 'input_audio_buffer.delta',
192+
delta: btoa(audioBase64String),
193+
};
194+
if (this.connected && this.ws) {
195+
this.ws.send(JSON.stringify(audioMessage));
196+
}
189197
}
190198
};
191199

192-
const microphone = ms.getTracks()[0];
200+
const microphone = ms.getTracks()[0]?.clone();
193201
if (!microphone) {
194202
throw new Error('No microphone found');
195203
}
@@ -238,5 +246,6 @@ export class WebRTC {
238246
};
239247
await completed;
240248
console.log('WebRTC fully operational');
249+
this.connected = true;
241250
}
242251
}

0 commit comments

Comments
 (0)