What happens
StemDeck creates its AudioContext with no sampleRate, so the graph runs at whatever the operating system's output device is set to. On a machine set to 24-bit/192 kHz, everything downstream runs at 192 kHz.
Nothing in StemDeck benefits from that. Every stem the pipeline produces is 44.1 kHz, 16-bit stereo, fixed at -ar 44100 in app/pipeline/runner.py. At a 192 kHz context the browser upsamples those 44.1 kHz stems on decode and then does several times the work on interpolated samples that carry no information the 44.1 kHz originals did not already have.
What it costs
The pitch chain's WSOLA search is O(overlap x seek) per sequence and both windows are set in milliseconds, so the cost per second of audio is quadratic in the context rate. One active pitch chain, offline render through the real soundtouch-processor.js, as a share of real time:
| Context rate |
Cost |
| 44.1 kHz |
2.1% |
| 48 kHz |
2.4% |
| 96 kHz |
7.3% |
| 192 kHz |
28.2% |
Memory scales linearly with the same number. Decoded AudioBuffers for a five minute, six stem track:
| Context rate |
RAM |
| 44.1 kHz |
606 MB |
| 48 kHz |
659 MB |
| 96 kHz |
1.3 GB |
| 192 kHz |
2.6 GB |
2.6 GB of buffers for a five minute track is enough to put a tab under memory pressure on its own, before any DSP runs.
Who this affects
Anyone who has deliberately set a high output rate, which is exactly the audiophile audience most likely to be running a DAC. #575 was reported by someone running 24-bit/192 kHz for Apple Music and Spotify Hi-Fi.
This is a separate problem from #576. That one was twelve pitch chains doing work on silence, which #577 fixes. This one is real work done at four times thenecessary resolution, and it remains after #577 lands: a single transposed laneat 192 kHz still costs 28% of the audio thread's budget where the same lane at44.1 kHz costs 2%.
Constraints for whoever fixes this
- There are four
AudioContext creation sites: static/js/audioEngine.js, static/js/chunkedAudioEngine.js, static/js/player.js (the footer waveform's decode-only context) and static/mobile/app.js (the shared, gesture-unlocked context the mobile UI passes in). A fix in one engine only is not a fix.
new AudioContext({ sampleRate }) is honoured in Chrome, Firefox and Safari, and the browser resamples to the device on output. I verified in Chromium that a requested 44100 and 48000 are both granted and that baseLatency does not change.
- Whether the browser's output resampling to 192 kHz is clean on Windows WASAPI
is not something I can test without the hardware. It has to be confirmed on a
real 192 kHz device before this ships.
- Lowering the rate for everybody is not obviously right. At 44.1 and 48 kHz the current behaviour is correct and costs nothing, and pinning would introduce a resample where there is none today. Capping only above 48 kHz leaves the common case untouched.
- The context's rate is fixed when it is constructed, so the device rate has to be read before the real context is built, or the app has to be restarted after a device change. It already behaves that way today.
What happens
StemDeck creates its
AudioContextwith nosampleRate, so the graph runs at whatever the operating system's output device is set to. On a machine set to 24-bit/192 kHz, everything downstream runs at 192 kHz.Nothing in StemDeck benefits from that. Every stem the pipeline produces is 44.1 kHz, 16-bit stereo, fixed at
-ar 44100inapp/pipeline/runner.py. At a 192 kHz context the browser upsamples those 44.1 kHz stems on decode and then does several times the work on interpolated samples that carry no information the 44.1 kHz originals did not already have.What it costs
The pitch chain's WSOLA search is O(overlap x seek) per sequence and both windows are set in milliseconds, so the cost per second of audio is quadratic in the context rate. One active pitch chain, offline render through the real
soundtouch-processor.js, as a share of real time:Memory scales linearly with the same number. Decoded
AudioBuffers for a five minute, six stem track:2.6 GB of buffers for a five minute track is enough to put a tab under memory pressure on its own, before any DSP runs.
Who this affects
Anyone who has deliberately set a high output rate, which is exactly the audiophile audience most likely to be running a DAC. #575 was reported by someone running 24-bit/192 kHz for Apple Music and Spotify Hi-Fi.
This is a separate problem from #576. That one was twelve pitch chains doing work on silence, which #577 fixes. This one is real work done at four times thenecessary resolution, and it remains after #577 lands: a single transposed laneat 192 kHz still costs 28% of the audio thread's budget where the same lane at44.1 kHz costs 2%.
Constraints for whoever fixes this
AudioContextcreation sites:static/js/audioEngine.js,static/js/chunkedAudioEngine.js,static/js/player.js(the footer waveform's decode-only context) andstatic/mobile/app.js(the shared, gesture-unlocked context the mobile UI passes in). A fix in one engine only is not a fix.new AudioContext({ sampleRate })is honoured in Chrome, Firefox and Safari, and the browser resamples to the device on output. I verified in Chromium that a requested 44100 and 48000 are both granted and thatbaseLatencydoes not change.is not something I can test without the hardware. It has to be confirmed on a
real 192 kHz device before this ships.