@@ -20,6 +20,7 @@ interface AudioOptions {
20
20
speechAudioCallback : ( ) => void ;
21
21
speechResampler : SRC ;
22
22
soundExpressionResampler : SRC ;
23
+ recordingResampler : SRC ;
23
24
}
24
25
25
26
export class BoardAudio {
@@ -32,6 +33,8 @@ export class BoardAudio {
32
33
private muteNode : GainNode | undefined ;
33
34
private sensitivityNode : GainNode | undefined ;
34
35
36
+ private recordingResampler : SRC | undefined ;
37
+
35
38
default : BufferedAudio | undefined ;
36
39
speech : BufferedAudio | undefined ;
37
40
soundExpression : BufferedAudio | undefined ;
@@ -46,10 +49,13 @@ export class BoardAudio {
46
49
speechAudioCallback,
47
50
speechResampler,
48
51
soundExpressionResampler,
52
+ recordingResampler,
49
53
} : AudioOptions ) {
50
54
if ( ! this . context ) {
51
55
throw new Error ( "Context must be pre-created from a user event" ) ;
52
56
}
57
+ this . recordingResampler = recordingResampler ;
58
+
53
59
this . muteNode = this . context . createGain ( ) ;
54
60
this . muteNode . gain . setValueAtTime (
55
61
this . muted ? 0 : 1 ,
@@ -221,17 +227,13 @@ export class BoardAudio {
221
227
source . connect ( this . sensitivityNode ! ) ;
222
228
223
229
const recorder = this . context ! . createScriptProcessor ( 2048 , 1 , 1 ) ;
224
- const inputSampleRate = 44100 ;
225
- const sampleRateConverter = await createSampleRateConverter (
226
- 1 ,
227
- inputSampleRate ,
228
- sampleRate ,
229
- {
230
- converterType : ConverterType . SRC_SINC_FASTEST ,
231
- }
232
- ) ;
230
+
231
+ const inputSampleRate = this . context ! . sampleRate ;
232
+ this . recordingResampler ! . inputSampleRate = inputSampleRate ;
233
+ this . recordingResampler ! . outputSampleRate = sampleRate ;
234
+
233
235
recorder . onaudioprocess = ( e ) => {
234
- const resampled = sampleRateConverter . full (
236
+ const resampled = this . recordingResampler ! . full (
235
237
e . inputBuffer . getChannelData ( 0 )
236
238
) ;
237
239
onChunk ( resampled ) ;
@@ -248,8 +250,7 @@ export class BoardAudio {
248
250
recorder . disconnect ( ) ;
249
251
this . sensitivityNode ! . disconnect ( ) ;
250
252
source . disconnect ( ) ;
251
- micStream . getTracks ( ) . forEach ( ( track ) => track . stop ( ) ) ;
252
- sampleRateConverter . destroy ( ) ;
253
+ micStream ?. getTracks ( ) . forEach ( ( track ) => track . stop ( ) ) ;
253
254
this . microphoneEl . style . display = "none" ;
254
255
this . stopActiveRecording = undefined ;
255
256
} ;
@@ -273,7 +274,6 @@ export class BoardAudio {
273
274
274
275
class BufferedAudio {
275
276
nextStartTime : number = - 1 ;
276
- private sampleRate : number = - 1 ;
277
277
278
278
constructor (
279
279
private context : AudioContext ,
@@ -291,19 +291,14 @@ class BufferedAudio {
291
291
}
292
292
293
293
setSampleRate ( sampleRate : number ) {
294
- this . sampleRate = sampleRate ;
295
294
this . resampler . inputSampleRate = sampleRate ;
296
295
}
297
296
298
297
writeData ( data : Float32Array ) {
299
- let sampleRate = this . sampleRate ;
300
- // In practice the supported range is less than the 8k..96k required by the spec
301
- let alwaysResample = true ;
302
- if ( alwaysResample || sampleRate < 8_000 || sampleRate > 96_000 ) {
303
- // We need to resample
304
- sampleRate = this . context . sampleRate ;
305
- data = this . resampler . full ( data ) ;
306
- }
298
+ // In practice the supported range is less than the 8k..96k required by the spec and varies by browser
299
+ // for a consistent performance profile we're always resampling for now rather than letting Web Audio do it
300
+ let sampleRate = this . context . sampleRate ;
301
+ data = this . resampler . full ( data ) ;
307
302
308
303
// Use createXXX instead to support Safari 14.0.
309
304
const buffer = this . context . createBuffer ( 1 , data . length , sampleRate ) ;
0 commit comments