File tree Expand file tree Collapse file tree 2 files changed +28
-8
lines changed Expand file tree Collapse file tree 2 files changed +28
-8
lines changed Original file line number Diff line number Diff line change @@ -27,11 +27,9 @@ export class Audio {
27
27
defaultAudioCallback,
28
28
speechAudioCallback,
29
29
} : AudioOptions ) {
30
- this . context = new AudioContext ( {
31
- // The highest rate is the sound expression synth.
32
- sampleRate : 44100 ,
33
- } ) ;
34
-
30
+ if ( ! this . context ) {
31
+ throw new Error ( "Context must be pre-created from a user event" ) ;
32
+ }
35
33
this . muteNode = this . context . createGain ( ) ;
36
34
this . muteNode . gain . setValueAtTime (
37
35
this . muted ? 0 : 1 ,
@@ -62,6 +60,16 @@ export class Audio {
62
60
) ;
63
61
}
64
62
63
+ async createAudioContextFromUserInteraction ( ) : Promise < void > {
64
+ this . context = new AudioContext ( {
65
+ // The highest rate is the sound expression synth.
66
+ sampleRate : 44100 ,
67
+ } ) ;
68
+ if ( this . context . state === "suspended" ) {
69
+ return this . context . resume ( ) ;
70
+ }
71
+ }
72
+
65
73
playSoundExpression ( expr : string ) {
66
74
const soundEffects = parseSoundEffects ( replaceBuiltinSound ( expr ) ) ;
67
75
const onDone = ( ) => {
@@ -138,6 +146,9 @@ export class Audio {
138
146
139
147
boardStopped ( ) {
140
148
this . stopOscillator ( ) ;
149
+ this . speech ?. dispose ( ) ;
150
+ this . soundExpression ?. dispose ( ) ;
151
+ this . default ?. dispose ( ) ;
141
152
}
142
153
143
154
private stopOscillator ( ) {
@@ -188,4 +199,9 @@ class BufferedAudio {
188
199
}
189
200
source . start ( startTime ) ;
190
201
}
202
+
203
+ dispose ( ) {
204
+ // Prevent calls into WASM when the buffer nodes finish.
205
+ this . callback = ( ) => { } ;
206
+ }
191
207
}
Original file line number Diff line number Diff line change @@ -230,9 +230,10 @@ export class Board {
230
230
this . initializePlayButton ( ) ;
231
231
// We start stopped.
232
232
this . displayStoppedState ( ) ;
233
- this . playButton . addEventListener ( "click" , ( ) =>
234
- this . notifications . onRequestFlash ( )
235
- ) ;
233
+ this . playButton . addEventListener ( "click" , async ( ) => {
234
+ await this . audio . createAudioContextFromUserInteraction ( ) ;
235
+ this . notifications . onRequestFlash ( ) ;
236
+ } ) ;
236
237
237
238
this . updateTranslationsInternal ( ) ;
238
239
this . notifications . onReady ( this . getState ( ) ) ;
@@ -481,6 +482,9 @@ export class Board {
481
482
* @returns A promise that resolves when the simulator is stopped.
482
483
*/
483
484
async stop ( brief : boolean = false ) : Promise < void > {
485
+ // Preemptively stop audio so that we don't call into WASM for more data
486
+ this . audio . boardStopped ( ) ;
487
+
484
488
if ( this . panicTimeout ) {
485
489
clearTimeout ( this . panicTimeout ) ;
486
490
this . panicTimeout = null ;
You can’t perform that action at this time.
0 commit comments