Description
AudioBufferQueueSourceNode plays streamed 24 kHz LINEAR16 PCM at ~3× the correct rate with heavy continuous static layered on top, on iOS device (real iPhone) AND iOS Simulator. The voice is intelligible but sped up and noisy.
Downgrading the library to 0.11.7 fully restores clean playback on the same device, same JS code, same audio session config, same source data. The bug is therefore a regression introduced between 0.11.7 and 0.12.1. There is at least one other 0.12.0+ regression reported (#1062, StreamerNode SIGABRT), which suggests the queue/streamer paths got reworked in that range.
Observed pace ratio
I instrumented onPositionChanged against wall clock for one TTS-streaming session (24 kHz LINEAR16 PCM, ~50 ms chunks, base64 over WebSocket, fed via enqueueBuffer on an AudioBufferQueueSourceNode).
[init] audioCtxSampleRate=24000, devicePreferredSampleRate=48000, requestedSampleRate=24000
[chunk0] bytes=20160, frames=10080, bufferSampleRate=24000, audioCtxSampleRate=24000, expectedChunkDurationSec=0.420, audioCtxState=running
ERROR NotSupportedError: The number of frames provided (0) is less than or equal to the minimum bound (0) ← fires once, right after the first chunk
[pace] wall=0.514, audioPos=1.184, ratio=2.304
[pace] wall=1.026, audioPos=2.702, ratio=2.634
[pace] wall=1.527, audioPos=4.220, ratio=2.764
[pace] wall=2.038, audioPos=5.738, ratio=2.816
[pace] wall=2.548, audioPos=7.256, ratio=2.848
[pace] wall=3.054, audioPos=8.774, ratio=2.873
[pace] wall=3.562, audioPos=10.292, ratio=2.889
[pace] wall=4.064, audioPos=11.810, ratio=2.906
Steady-state delta: audioPos advances by exactly 1.518 s per 0.5 s of wall time → ratio = 3.036×. The rising-toward-3 trend in the first few samples is just startup latency amortizing (wall clock anchored before the first frame actually emerged from the speaker).
source.playbackRate.value and source.detune.value were checked at construction time: both at their documented defaults (1.0 and 0.0 respectively). No pitchCorrection, no manual playbackRate writes anywhere in our JS.
The NotSupportedError: number of frames provided (0) fires exactly once, right after the first chunk is enqueued. The audio keeps playing through it. I do not know if it's related to the speed/static symptoms or independent, but it is new in 0.12.x. Not present on 0.11.7.
What I have ruled out
- Not the source data. I bypassed the library entirely and wrote the same PCM stream out as a WAV file via a standalone Node script;
afplay on the same machine plays it at correct speed with zero static.
- Not the JS code path. Same code on 0.11.7 plays cleanly; only the library version differs.
- Not the iOS audio session category. Same
playAndRecord + defaultToSpeaker config under both versions.
- Not Bluetooth / route negotiation. Bug reproduces over built-in iPhone speaker and over wired output.
- Not Voice Processing IO (VPIO). Verified by rebuilding with VPIO disabled. Symptoms unchanged on 0.12.1, still clean on 0.11.7.
Steps to reproduce
- Install
react-native-audio-api@0.12.1.
- Create an
AudioContext({ sampleRate: 24000 }).
createBufferQueueSource(), connect to destination, start(0, 0).
- Stream 24 kHz mono LINEAR16 PCM chunks: decode base64 → Int16 → Float32 →
createBuffer(1, frames, 24000) → copyToChannel → enqueueBuffer.
- Listen on iOS (device or simulator, both reproduce).
Result on 0.12.1: ~3× speed + heavy static. Result on 0.11.7: clean playback at correct speed.
I can't share a runnable Snack repro of the streaming path itself, but the recipe above is the entirety of the relevant call surface. A static-file 24 kHz PCM repro should suffice. If it doesn't reproduce with a single concatenated buffer, the bug is likely in the queue/enqueue path specifically.
React Native Audio API version
0.12.1 (broken). 0.11.7 (works).
React Native version
0.83.6
Expo version
55.0.23
Platforms
iOS (device + simulator both reproduce)
JavaScript runtime
Hermes
Workflow
Expo (development build, not Expo Go)
Architecture
Fabric (New Architecture)
Build type
Debug
Device
iPhone (real device) AND iOS Simulator
Device model
iPhone (current generation, built-in speaker). Same symptom on Simulator.
Acknowledgements
Yes
Description
AudioBufferQueueSourceNodeplays streamed 24 kHz LINEAR16 PCM at ~3× the correct rate with heavy continuous static layered on top, on iOS device (real iPhone) AND iOS Simulator. The voice is intelligible but sped up and noisy.Downgrading the library to 0.11.7 fully restores clean playback on the same device, same JS code, same audio session config, same source data. The bug is therefore a regression introduced between 0.11.7 and 0.12.1. There is at least one other 0.12.0+ regression reported (#1062, StreamerNode SIGABRT), which suggests the queue/streamer paths got reworked in that range.
Observed pace ratio
I instrumented
onPositionChangedagainst wall clock for one TTS-streaming session (24 kHz LINEAR16 PCM, ~50 ms chunks, base64 over WebSocket, fed viaenqueueBufferon anAudioBufferQueueSourceNode).Steady-state delta: audioPos advances by exactly 1.518 s per 0.5 s of wall time → ratio = 3.036×. The rising-toward-3 trend in the first few samples is just startup latency amortizing (wall clock anchored before the first frame actually emerged from the speaker).
source.playbackRate.valueandsource.detune.valuewere checked at construction time: both at their documented defaults (1.0and0.0respectively). NopitchCorrection, no manualplaybackRatewrites anywhere in our JS.The
NotSupportedError: number of frames provided (0)fires exactly once, right after the first chunk is enqueued. The audio keeps playing through it. I do not know if it's related to the speed/static symptoms or independent, but it is new in 0.12.x. Not present on 0.11.7.What I have ruled out
afplayon the same machine plays it at correct speed with zero static.playAndRecord+defaultToSpeakerconfig under both versions.Steps to reproduce
react-native-audio-api@0.12.1.AudioContext({ sampleRate: 24000 }).createBufferQueueSource(), connect to destination,start(0, 0).createBuffer(1, frames, 24000)→copyToChannel→enqueueBuffer.Result on 0.12.1: ~3× speed + heavy static. Result on 0.11.7: clean playback at correct speed.
I can't share a runnable Snack repro of the streaming path itself, but the recipe above is the entirety of the relevant call surface. A static-file 24 kHz PCM repro should suffice. If it doesn't reproduce with a single concatenated buffer, the bug is likely in the queue/enqueue path specifically.
React Native Audio API version
0.12.1 (broken). 0.11.7 (works).
React Native version
0.83.6
Expo version
55.0.23
Platforms
iOS (device + simulator both reproduce)
JavaScript runtime
Hermes
Workflow
Expo (development build, not Expo Go)
Architecture
Fabric (New Architecture)
Build type
Debug
Device
iPhone (real device) AND iOS Simulator
Device model
iPhone (current generation, built-in speaker). Same symptom on Simulator.
Acknowledgements
Yes