fix(synth): prevent audio thread spin loops and GC storms on playback end#2711
Open
xyfu66 wants to merge 1 commit into
Open
fix(synth): prevent audio thread spin loops and GC storms on playback end#2711xyfu66 wants to merge 1 commit into
xyfu66 wants to merge 1 commit into
Conversation
… end [Root Cause] When AlphaSynth finishes generating all audio chunks, it previously sent a 0-length Float32Array to the output to signal the end of data. However, on platforms like Android, writing 0 samples to the `AudioTrack` is non-blocking and returns immediately. This causes the audio playback head to freeze (underrun), which in turn makes the native worker report `playedSamples = 0`. Since `_onSamplesPlayed(0)` short-circuits and ignores the update, the `_notPlayedSamples` counter never reaches 0. As a result, AlphaSynth never calls `stop()`, never emits the `playerFinished` event, and the native audio thread falls into an infinite 100% CPU spin loop (allocating massive arrays per second and triggering a severe GC storm). [Solution] Instead of sending a 0-length array when no data is left, we now send an array of silence matching the normal micro-buffer size. This allows the output device to block and consume data naturally, preventing the underrun. Consequently, `_notPlayedSamples` accurately drains to 0, allowing AlphaSynth to properly shut down and emit the `playerFinished` event.
Member
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issues
Fixes N/A (Discovered this bug while integrating AlphaTab on Android where it caused infinite GC storms).
Proposed changes
Root Cause
When AlphaSynth finishes generating all audio chunks, it previously sent a 0-length Float32Array to the output to signal the end of data. However, on platforms like Android, writing 0 samples to the
AudioTrackis non-blocking and returns immediately.This causes the audio playback head to freeze (underrun), which in turn makes the native worker report
playedSamples = 0.Since
_onSamplesPlayed(0)short-circuits and ignores the update, the_notPlayedSamplescounter never reaches 0. As a result, AlphaSynth never callsstop(), never emits theplayerFinishedevent, and the native audio thread falls into an infinite 100% CPU spin loop (allocating massive arrays per second and triggering a severe GC storm).Solution
Instead of sending a 0-length array when no data is left, we now send an array of silence matching the normal micro-buffer size. This allows the output device to block and consume data naturally, preventing the underrun. Consequently,
_notPlayedSamplesaccurately drains to 0, allowing AlphaSynth to properly shut down and emit theplayerFinishedevent.Checklist
(No tests were added because this is a fix for a platform-specific AudioTrack underrun deadlock that occurs asynchronously between the TS state machine and the Android native UI thread, making it difficult to cover in standard headless unit tests.)
Further details