in 9-realtime/openai-realtime-console-example/src/lib/wavtools/lib/wav_recorder.js [301:381]
async begin(deviceId) {
if (this.processor) {
throw new Error(
`Already connected: please call .end() to start a new session`,
);
}
if (
!navigator.mediaDevices ||
!('getUserMedia' in navigator.mediaDevices)
) {
throw new Error('Could not request user media');
}
try {
const config = { audio: true };
if (deviceId) {
config.audio = { deviceId: { exact: deviceId } };
}
this.stream = await navigator.mediaDevices.getUserMedia(config);
} catch (err) {
throw new Error('Could not start media stream');
}
const context = new AudioContext({ sampleRate: this.sampleRate });
const source = context.createMediaStreamSource(this.stream);
// Load and execute the module script.
try {
await context.audioWorklet.addModule(this.scriptSrc);
} catch (e) {
console.error(e);
throw new Error(`Could not add audioWorklet module: ${this.scriptSrc}`);
}
const processor = new AudioWorkletNode(context, 'audio_processor');
processor.port.onmessage = (e) => {
const { event, id, data } = e.data;
if (event === 'receipt') {
this.eventReceipts[id] = data;
} else if (event === 'chunk') {
if (this._chunkProcessorSize) {
const buffer = this._chunkProcessorBuffer;
this._chunkProcessorBuffer = {
raw: WavPacker.mergeBuffers(buffer.raw, data.raw),
mono: WavPacker.mergeBuffers(buffer.mono, data.mono),
};
if (
this._chunkProcessorBuffer.mono.byteLength >=
this._chunkProcessorSize
) {
this._chunkProcessor(this._chunkProcessorBuffer);
this._chunkProcessorBuffer = {
raw: new ArrayBuffer(0),
mono: new ArrayBuffer(0),
};
}
} else {
this._chunkProcessor(data);
}
}
};
const node = source.connect(processor);
const analyser = context.createAnalyser();
analyser.fftSize = 8192;
analyser.smoothingTimeConstant = 0.1;
node.connect(analyser);
if (this.outputToSpeakers) {
// eslint-disable-next-line no-console
console.warn(
'Warning: Output to speakers may affect sound quality,\n' +
'especially due to system audio feedback preventative measures.\n' +
'use only for debugging',
);
analyser.connect(context.destination);
}
this.source = source;
this.node = node;
this.analyser = analyser;
this.processor = processor;
return true;
}