0

I have an Angular 6 app that allows the user to record audio from within their browser and upload it to a server. For recording it uses the WebAudioTrack.js library, which is included via a <script> tag because I couldn't find any way to compile it into the Angular app.

About half of the time the recording works fine. The rest of the time it works fine (with VU meter and everything) up until the point when the user clicks the stop button. When that happens, the Chrome console reports the error as:

Uncaught (in promise) DOMException

When I'm running Angular in development mode, the Chrome error messages usually include a link to the line of code that caused the error. In this case, though, the link just points to line 1 of the HTML page, i.e. the DOCTYPE line.

By inserting a lot of console.log statements I was able to follow the program execution, and I believe I have partly narrowed down the source of the problem. It appears to be happening in the chunk of code below, at the line which calls this.audioTrack.stopRecording((e).

// "Stop" button was clicked
recordStop() {

  /* Browser shows error when following line is called */
  this.audioTrack.stopRecording((e) => {

    /* When browser errs, this code is never reached */
    this.blobWav = this.audioTrack.blob;
    this.audioURL = this.audioTrack.getBlobSrc();
    this.tearDownMediaStream();
    this.recordState = 'playbackready';
    window.clearInterval(this.timer);
  });
}

Note that this does not appear to be related to the new Chrome autoplay policy which requires user interaction with a page before using an AudioContext to play audio, and throws similar error messages when a paused AudioContext is used. I have already taken that into account, and the state of the AudioContext is reported as "running". Also, this is recording, not playback.

I've tried checking the return value of this.audioTrack.stopRecording((e) and outputting that to the console. Code:

recordStop() {
  var retval = this.audioTrack.stopRecording((e) => {
    // ...
  });
  console.log(retval);
}

Output:

WebAudioTrack {context: AudioContext, sampleRate: 48000, bufferSize: 4096, numberOfAudioChannels: 1, volume: 1, …}

So, I guess that's not a return value but a copy of the object.

I also tried adding a .catch to it, but it doesn't support that method.

I am stuck on what to do next to continue narrowing down the source of this intermittent error. I also am a bit overwhelmed by the syntax for promises, especially in Angular. I can barely manage to write them, so I'm not yet at the point where I am comfortable debugging them.

What can I do to get the browser to give me more detail on this error message? Or is there some way I can catch the DOMException, handle it, and output more detail?

Derrick Miller
  • 1,860
  • 3
  • 21
  • 37
  • 1
    Perhaps add a global handler for `onunhandledrejection` like [this](https://stackoverflow.com/questions/55178612/why-does-onerror-not-intercept-exceptions-from-promises-and-async-functions/55178672#55178672)? Not sure if logging the error there could give you more detail (eg check the `.stack` property of the error) – CertainPerformance Mar 30 '19 at 00:05
  • Though. ideally, fix the code *inside* of the `stopRecording` call, if you're able, so that errors are caught – CertainPerformance Mar 30 '19 at 00:07
  • You are a superhero. I added the `onunhandledrejection` code you suggested and was able to see additional details. I wasn't able to find a stack property, but I did find a `PromiseRejectionEvent.reason` that gave me something to go on. It contains "EncodingError" and "Unable to decode audio data". So. now I'm off to Google those messages and try to fix the problem. Thanks for your help! – Derrick Miller Mar 30 '19 at 00:23
  • That library you use loads https://webrtchacks.github.io/adapter/adapter-latest.js ... there is ONE point in that webrtchacks library that throws a DomException with NO "message" when it is constructed, it's in the `navigator.mediaDevices.getUserMedia` function - though I can't see why that would be invoked at the point in the code you get your error – Jaromanda X Mar 30 '19 at 00:29
  • Is the error thrown before the callback you passed to `stopRecording` fired? In that case, it would be *AudioContext.decodeAudio*'s promise (that the lib doesn't use). Otherwise, that's in **your** code, and we need to see it. – Kaiido Mar 30 '19 at 00:59
  • @Kaiido: The error is shown and nothing in the callback gets executed. If I change the callback to simply do `console.log("i am here")` it never shows up in the console. Does that mean the error is happening in the WebAudioTrack library? – Derrick Miller Mar 30 '19 at 01:09
  • Yes, or at least before the callback gets executed. Are you sure stopRecording get called? – Kaiido Mar 30 '19 at 01:16
  • I just added a bunch of console.log() statements to WebAudioTrack.js and yes, stopRecording() does get called. I've now narrowed down the promise error to line 169 of https://github.com/danielstorey/WebAudioTrack/blob/b17bd8d7511caae02a62d71280fb1a1e159541df/WebAudioTrack.js – Derrick Miller Mar 30 '19 at 01:30

1 Answers1

0

There's a lot of issues with audio play, pause and stop with chrome, they are async calls so I would recommend trying a .then(() => {}) when stopping audio and turning the stopRecording into an async function:

here is some documentation on the bug, hope it helps: https://developers.google.com/web/updates/2017/06/play-request-was-interrupted

Matt Berg
  • 499
  • 1
  • 4
  • 10
  • The stopRecording() function is part of the third-party WebAudioTrack.js library. In order to turn it into an async function as you suggest, would I need to make changes within that library? Or is it simply a matter of how I call it from my software? – Derrick Miller Mar 30 '19 at 00:57