Skip to content

Commit b887f22

Browse files
Audio fixes
- Use smaller buffer to avoid too much audio after wait=True - Don't reset nextStartTime to avoid overlapping audio - Add indirection to callback so we can clear it properly to avoid calling it when audio finishes after MicroPython has terminated
1 parent 06b4549 commit b887f22

File tree

4 files changed

+27
-17
lines changed

4 files changed

+27
-17
lines changed

src/board/audio/index.ts

+14-11
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@ export class BoardAudio {
3030
currentSoundExpressionCallback: undefined | (() => void);
3131
private stopActiveRecording: (() => void) | undefined;
3232

33-
constructor(
34-
private microphoneEl: SVGElement
35-
) {}
33+
constructor(private microphoneEl: SVGElement) {}
3634

3735
initializeCallbacks({
3836
defaultAudioCallback,
@@ -142,9 +140,9 @@ export class BoardAudio {
142140
setSensitivity(sensitivity: number) {
143141
this.sensitivityNode!.gain.setValueAtTime(
144142
// check if this is correct
145-
sensitivity,
143+
sensitivity,
146144
this.context!.currentTime
147-
)
145+
);
148146
}
149147

150148
setVolume(volume: number) {
@@ -206,7 +204,7 @@ export class BoardAudio {
206204
this.stopRecording();
207205
return;
208206
}
209-
this.microphoneEl.style.display = "unset"
207+
this.microphoneEl.style.display = "unset";
210208

211209
const source = this.context!.createMediaStreamSource(micStream);
212210
source.connect(this.sensitivityNode!);
@@ -241,8 +239,8 @@ export class BoardAudio {
241239
recorder.disconnect();
242240
this.sensitivityNode!.disconnect();
243241
source.disconnect();
244-
micStream.getTracks().forEach(track => track.stop())
245-
this.microphoneEl.style.display = "none"
242+
micStream.getTracks().forEach((track) => track.stop());
243+
this.microphoneEl.style.display = "none";
246244
this.stopActiveRecording = undefined;
247245
};
248246
}
@@ -274,8 +272,9 @@ class BufferedAudio {
274272
) {}
275273

276274
init(sampleRate: number) {
275+
// This is called for each new audio source so don't reset nextStartTime
276+
// or we start to overlap audio
277277
this.sampleRate = sampleRate;
278-
this.nextStartTime = -1;
279278
}
280279

281280
createBuffer(length: number) {
@@ -291,20 +290,24 @@ class BufferedAudio {
291290
// Use createBufferSource instead of new AudioBufferSourceNode to support Safari 14.0.
292291
const source = this.context.createBufferSource();
293292
source.buffer = buffer;
294-
source.onended = this.callback;
293+
source.onended = this.callCallback;
295294
source.connect(this.destination);
296295
const currentTime = this.context.currentTime;
297296
const first = this.nextStartTime < currentTime;
298297
const startTime = first ? currentTime : this.nextStartTime;
299298
this.nextStartTime = startTime + buffer.length / buffer.sampleRate;
300-
// For audio frames, we're frequently out of data. Speech is smooth.
301299
if (first) {
302300
// We're just getting started so buffer another frame.
303301
this.callback();
304302
}
305303
source.start(startTime);
306304
}
307305

306+
private callCallback = () => {
307+
// Indirect so we can clear callback later
308+
this.callback();
309+
};
310+
308311
dispose() {
309312
// Prevent calls into WASM when the buffer nodes finish.
310313
this.callback = () => {};

src/jshal.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,8 @@ mergeInto(LibraryManager.library, {
278278
Module.board.microphone.microphoneOn();
279279
},
280280

281-
mp_js_hal_microphone_set_sensitivity: function (
282-
/** @type {number} */ value
283-
) {
284-
Module.board.audio.setSensitivity(value)
281+
mp_js_hal_microphone_set_sensitivity: function (/** @type {number} */ value) {
282+
Module.board.audio.setSensitivity(value);
285283
},
286284
mp_js_hal_microphone_set_threshold: function (
287285
/** @type {number} */ kind,

src/microbithal_js.c

+10-1
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,16 @@ void microbit_hal_audio_raw_set_rate(uint32_t sample_rate) {
455455
}
456456

457457
void microbit_hal_audio_raw_write_data(const uint8_t *buf, size_t num_samples) {
458-
mp_js_hal_audio_write_data(buf, num_samples);
458+
bool silence = true;
459+
for (const uint8_t *sample = buf; sample < buf + num_samples; ++sample) {
460+
if (*sample != 128) {
461+
silence = false;
462+
break;
463+
}
464+
}
465+
if (!silence) {
466+
mp_js_hal_audio_write_data(buf, num_samples);
467+
}
459468
}
460469

461470
void microbit_hal_audio_speech_init(uint32_t sample_rate) {

src/mpconfigport.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,6 @@ extern uint32_t rng_generate_random_word(void);
129129
((mp_raise_NotImplementedError(MP_ERROR_TEXT("simulator limitation: asm_thumb code"))), p)
130130

131131
// The latency of fetching 32 byte audio frames is too much so increase the size
132-
#define AUDIO_OUTPUT_BUFFER_SIZE (128)
132+
#define AUDIO_OUTPUT_BUFFER_SIZE (64)
133133

134134
#endif

0 commit comments

Comments
 (0)