On iOS, RecorderController.record() can return successfully and report RecorderState.recording, but the onAudioChunks stream (and therefore the live waveform / any byte-streaming consumer) never receives another event — intermittently, on the 2nd, 3rd, etc. recording started in the same app session (not the first).
Environment
audio_waveforms: 2.0.2
- Flutter: 3.38.5 (stable)
- Dart: 3.10.4
- Platform: iOS 26.2, tested on iOS Simulator (iPhone 17 Pro),
overrideAudioSession: true
- Recorder settings: custom sample rate,
AndroidEncoder/IosEncoder set for AAC
Steps to reproduce
- Call
recorderController.record(path: ..., recorderSettings: ...), listen to recorderController.onAudioChunks, confirm chunks arrive and waveData grows.
- Call
recorderController.stop() (or stop(false) + reset()).
- Call
recorderController.record(...) again with a new file path.
- Repeat step 2–3 a few times.
Expected: every record() call that resolves without throwing, and reports RecorderState.recording, delivers onAudioChunk events (and therefore waveform amplitude data) just like the first call did.
Actual: intermittently (roughly 1 in 3 attempts in our testing), a restart reports RecorderState.recording with no error, but zero onAudioChunk events ever arrive for that session. The next restart after that often works fine again. This reproduces even though nothing else in the app touches AVAudioSession/AVAudioEngine directly — only audio_waveforms calls are involved.
Root cause (as far as we could tell)
In ios/Classes/RecorderBytesStreamEngine.swift, audioEngine is a single AVAudioEngine instance held for the entire app/plugin lifetime:
private var audioEngine = AVAudioEngine()
attach() reinstalls a tap on audioEngine.inputNode and calls audioEngine.start() again; detach() just calls removeTap/stop() on the same instance. Restarting the same AVAudioEngine instance in place after a full stop is a known-flaky pattern on iOS — engine.start() can return without throwing while the input-node tap silently never fires again. This matches our observed symptom exactly (success reported, zero data delivered).
Suggested fix
Create a brand-new AVAudioEngine() on every attach() call instead of reusing one across sessions, and fully release the old one in detach():
class RecorderBytesStreamEngine {
private var audioEngine: AVAudioEngine?
// ...
func attach(result: @escaping FlutterResult, sampleRate: Int) {
let engine = AVAudioEngine()
audioEngine = engine
paused = false
totalFrames = 0
let inputNode = engine.inputNode
inputNode.installTap(onBus: 0, bufferSize: 1024, format: nil) { (buffer, time) in
// ...unchanged...
}
do {
try engine.start()
} catch {
result(FlutterError(code: Constants.audioWaveforms, message: "Error starting Audio Engine", details: error.localizedDescription))
}
}
func detach() {
totalFrames = 0
audioEngine?.inputNode.removeTap(onBus: 0)
audioEngine?.stop()
audioEngine = nil
}
// ...
}
We patched a local fork with exactly this change and verified it resolves the issue: before the patch, roughly 1 in 3 stop→restart cycles failed to deliver any audio chunks; after the patch, 3+ consecutive cycles all worked correctly in our testing.
Happy to open a PR with this change if that's useful — let us know.
On iOS,
RecorderController.record()can return successfully and reportRecorderState.recording, but theonAudioChunksstream (and therefore the live waveform / any byte-streaming consumer) never receives another event — intermittently, on the 2nd, 3rd, etc. recording started in the same app session (not the first).Environment
audio_waveforms: 2.0.2overrideAudioSession: trueAndroidEncoder/IosEncoderset for AACSteps to reproduce
recorderController.record(path: ..., recorderSettings: ...), listen torecorderController.onAudioChunks, confirm chunks arrive andwaveDatagrows.recorderController.stop()(orstop(false)+reset()).recorderController.record(...)again with a new file path.Expected: every
record()call that resolves without throwing, and reportsRecorderState.recording, deliversonAudioChunkevents (and therefore waveform amplitude data) just like the first call did.Actual: intermittently (roughly 1 in 3 attempts in our testing), a restart reports
RecorderState.recordingwith no error, but zeroonAudioChunkevents ever arrive for that session. The next restart after that often works fine again. This reproduces even though nothing else in the app touchesAVAudioSession/AVAudioEnginedirectly — onlyaudio_waveformscalls are involved.Root cause (as far as we could tell)
In
ios/Classes/RecorderBytesStreamEngine.swift,audioEngineis a singleAVAudioEngineinstance held for the entire app/plugin lifetime:attach()reinstalls a tap onaudioEngine.inputNodeand callsaudioEngine.start()again;detach()just callsremoveTap/stop()on the same instance. Restarting the sameAVAudioEngineinstance in place after a full stop is a known-flaky pattern on iOS —engine.start()can return without throwing while the input-node tap silently never fires again. This matches our observed symptom exactly (success reported, zero data delivered).Suggested fix
Create a brand-new
AVAudioEngine()on everyattach()call instead of reusing one across sessions, and fully release the old one indetach():We patched a local fork with exactly this change and verified it resolves the issue: before the patch, roughly 1 in 3 stop→restart cycles failed to deliver any audio chunks; after the patch, 3+ consecutive cycles all worked correctly in our testing.
Happy to open a PR with this change if that's useful — let us know.