Skip to content
This repository was archived by the owner on Nov 16, 2023. It is now read-only.

Commit 4146014

Browse files
author
Alexey Reznichenko
committed
Fixes #51
Apparently, recognizers are not being gc-ed fast enough, so if multiple are created in a row, we get an error "Failed to construct AudioContext". To get around this, only create a context in MicAudioSource.TurnOn and explicitly close the current context in TurnOff.
1 parent 9aef18a commit 4146014

File tree

6 files changed

+74
-37
lines changed

6 files changed

+74
-37
lines changed

distrib/speech.browser.sdk-min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

distrib/speech.browser.sdk.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,8 @@ declare module "src/common.browser/MicAudioSource" {
619619
readonly Events: EventSource<AudioSourceEvent>;
620620
private Listen;
621621
private OnEvent;
622+
private CreateAudioContext;
623+
private DestroyAudioContext;
622624
}
623625
}
624626
declare module "src/common.browser/FileAudioSource" {

distrib/speech.browser.sdk.js

Lines changed: 27 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

distrib/speech.browser.sdk.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/common.browser/MicAudioSource.ts

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,6 @@ export class MicAudioSource implements IAudioSource {
5050
this.id = audioSourceId ? audioSourceId : CreateNoDashGuid();
5151
this.events = new EventSource<AudioSourceEvent>();
5252
this.recorder = recorder;
53-
54-
// https://developer.mozilla.org/en-US/docs/Web/API/AudioContext
55-
const contextCtor = ((window as any).AudioContext)
56-
|| ((window as any).webkitAudioContext)
57-
|| false;
58-
59-
if (!contextCtor) {
60-
throw new Error("Browser does not support Web Audio API (AudioContext is not available).");
61-
}
62-
63-
this.context = new contextCtor();
6453
}
6554

6655
public TurnOn = (): Promise<boolean> => {
@@ -70,6 +59,8 @@ export class MicAudioSource implements IAudioSource {
7059

7160
this.initializeDeferral = new Deferred<boolean>();
7261

62+
this.CreateAudioContext();
63+
7364
const nav = window.navigator as INavigatorUserMedia;
7465

7566
let getUserMedia = (
@@ -175,20 +166,10 @@ export class MicAudioSource implements IAudioSource {
175166
}
176167
}
177168

178-
this.recorder.ReleaseMediaResources(this.context);
179-
180169
this.OnEvent(new AudioSourceOffEvent(this.id)); // no stream now
181170
this.initializeDeferral = null;
182171

183-
if (this.context.state === "running") {
184-
// Suspend actually takes a callback, but analogous to the
185-
// resume method, it'll be only fire if suspend is called
186-
// in a direct response to the user action. The later is not always
187-
// the case, as TurnOff is always called, when the receive an
188-
// end-of-speech message from the service. So, doing a best effort
189-
// fire-and-forget here.
190-
this.context.suspend();
191-
}
172+
this.DestroyAudioContext();
192173

193174
return PromiseHelper.FromResult(true);
194175
}
@@ -218,4 +199,42 @@ export class MicAudioSource implements IAudioSource {
218199
this.events.OnEvent(event);
219200
Events.Instance.OnEvent(event);
220201
}
202+
203+
private CreateAudioContext = (): void => {
204+
if (!!this.context) {
205+
return;
206+
}
207+
208+
// https://developer.mozilla.org/en-US/docs/Web/API/AudioContext
209+
const AudioContext = ((window as any).AudioContext)
210+
|| ((window as any).webkitAudioContext)
211+
|| false;
212+
213+
if (!AudioContext) {
214+
throw new Error("Browser does not support Web Audio API (AudioContext is not available).");
215+
}
216+
217+
this.context = new AudioContext();
218+
}
219+
220+
private DestroyAudioContext = (): void => {
221+
if (!this.context) {
222+
return;
223+
}
224+
225+
this.recorder.ReleaseMediaResources(this.context);
226+
227+
if ("close" in this.context) {
228+
this.context.close();
229+
this.context = null;
230+
} else if (this.context.state === "running") {
231+
// Suspend actually takes a callback, but analogous to the
232+
// resume method, it'll be only fired if suspend is called
233+
// in a direct response to a user action. The later is not always
234+
// the case, as TurnOff is also called, when we receive an
235+
// end-of-speech message from the service. So, doing a best effort
236+
// fire-and-forget here.
237+
this.context.suspend();
238+
}
239+
}
221240
}

0 commit comments

Comments
 (0)