Skip to content

AudioBufferQueueSourceNode plays at ~3× speed with heavy static on iOS (regression in 0.12.0+, clean on 0.11.7) #1064

@marcospgp

Description

@marcospgp

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

  1. Install react-native-audio-api@0.12.1.
  2. Create an AudioContext({ sampleRate: 24000 }).
  3. createBufferQueueSource(), connect to destination, start(0, 0).
  4. Stream 24 kHz mono LINEAR16 PCM chunks: decode base64 → Int16 → Float32 → createBuffer(1, frames, 24000)copyToChannelenqueueBuffer.
  5. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions