OSS: Prime the playback buffer when starting the device.

This commit is contained in:
David Reid
2026-01-14 08:06:46 +10:00
parent bdde9c46b0
commit 01fd4b7662
+48 -8
View File
@@ -39612,17 +39612,48 @@ static void ma_device_uninit__oss(ma_device* pDevice)
ma_free(pDeviceStateOSS, ma_device_get_allocation_callbacks(pDevice));
}
static void ma_device_prime_playback_buffer__oss(ma_device* pDevice)
{
ma_device_state_oss* pDeviceStateOSS = ma_device_get_backend_state__oss(pDevice);
ma_device_type deviceType = ma_device_get_type(pDevice);
ma_uint8 buffer[4096];
ma_uint32 bpf;
ma_uint32 framesToWrite;
ma_uint32 framesWritten;
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
framesToWrite = pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods;
framesWritten = 0;
MA_ZERO_MEMORY(buffer, sizeof(buffer));
while (framesWritten < framesToWrite) {
ma_uint32 framesToWriteThisIteration = sizeof(buffer) / bpf;
ma_uint32 framesRemaining = framesToWrite - framesWritten;
if (framesToWriteThisIteration > framesRemaining) {
framesToWriteThisIteration = framesRemaining;
}
/* Just a guard to ensure we don't get stuck in a loop. Should never happen in practice (would require a massive channel count). */
if (framesToWriteThisIteration == 0) {
break;
}
write(pDeviceStateOSS->fdPlayback, buffer, framesToWriteThisIteration);
framesWritten += framesToWriteThisIteration;
}
}
}
/*
Note on Starting and Stopping
=============================
In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when
trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will
fail. Instead what we need to do is just not write or read to and from the device when the
device is not running.
As a result, both the start and stop functions for OSS are just empty stubs. The starting and
stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check
the device state, and if the device is stopped they will simply not do any kind of processing.
device is not running. As a result, the stop function for OSS is just an empty stub.
The downside to this technique is that I've noticed a fairly lengthy delay in stopping the
device, up to a second. This is on a virtual machine, and as such might just be due to the
@@ -39630,13 +39661,22 @@ virtual drivers, but I'm not fully sure. I am not sure how to work around this p
the moment that's just how it's going to have to be.
When starting the device, OSS will automatically start it when write() or read() is called.
UPDATE: 2026-01-14
I'm no longer getting the stop delay when running from inside Virtual Box.
*/
static ma_result ma_device_start__oss(ma_device* pDevice)
{
MA_ASSERT(pDevice != NULL);
ma_device_type deviceType = ma_device_get_type(pDevice);
/* The device is automatically started with reading and writing. */
(void)pDevice;
/*
For playback we'll start the device by priming it with silence. Otherwise we'll do nothing
and let it start in step(). We want the playback buffer to be initially filled with silence
for the benefit of duplex mode.
*/
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
ma_device_prime_playback_buffer__oss(pDevice);
}
return MA_SUCCESS;
}