Improve error recovery when starting a recording

This helps return the microphone access to the user.
This commit is contained in:
Travis Ralston 2021-05-05 22:30:22 -06:00
parent b5c25498c8
commit b61fe2f8e6
2 changed files with 89 additions and 68 deletions

View file

@ -73,7 +73,9 @@ class ConsoleLogger {
// Convert objects and errors to helpful things // Convert objects and errors to helpful things
args = args.map((arg) => { args = args.map((arg) => {
if (arg instanceof Error) { if (arg instanceof DOMException) {
return arg.message + ` (${arg.name} | ${arg.code}) ` + (arg.stack ? `\n${arg.stack}` : '');
} else if (arg instanceof Error) {
return arg.message + (arg.stack ? `\n${arg.stack}` : ''); return arg.message + (arg.stack ? `\n${arg.stack}` : '');
} else if (typeof (arg) === 'object') { } else if (typeof (arg) === 'object') {
try { try {

View file

@ -90,6 +90,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
} }
private async makeRecorder() { private async makeRecorder() {
try {
this.recorderStream = await navigator.mediaDevices.getUserMedia({ this.recorderStream = await navigator.mediaDevices.getUserMedia({
audio: { audio: {
channelCount: CHANNELS, channelCount: CHANNELS,
@ -114,6 +115,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
// web audio API prefers this be done async to avoid holding the main thread with math. // web audio API prefers this be done async to avoid holding the main thread with math.
const mxRecorderWorkletPath = document.body.dataset.vectorRecorderWorkletScript; const mxRecorderWorkletPath = document.body.dataset.vectorRecorderWorkletScript;
if (!mxRecorderWorkletPath) { if (!mxRecorderWorkletPath) {
// noinspection ExceptionCaughtLocallyJS
throw new Error("Unable to create recorder: no worklet script registered"); throw new Error("Unable to create recorder: no worklet script registered");
} }
await this.recorderContext.audioWorklet.addModule(mxRecorderWorkletPath); await this.recorderContext.audioWorklet.addModule(mxRecorderWorkletPath);
@ -162,6 +164,23 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
newBuf.set(buf, this.buffer.length); newBuf.set(buf, this.buffer.length);
this.buffer = newBuf; this.buffer = newBuf;
}; };
} catch (e) {
console.error("Error starting recording: ", e);
if (e instanceof DOMException) { // Unhelpful DOMExceptions are common - parse them sanely
console.error(`${e.name} (${e.code}): ${e.message}`);
}
// Clean up as best as possible
if (this.recorderStream) this.recorderStream.getTracks().forEach(t => t.stop());
if (this.recorderSource) this.recorderSource.disconnect();
if (this.recorder) this.recorder.close();
if (this.recorderContext) {
// noinspection ES6MissingAwait - not important that we wait
this.recorderContext.close();
}
throw e; // rethrow so upstream can handle it
}
} }
private get audioBuffer(): Uint8Array { private get audioBuffer(): Uint8Array {