diff --git a/miniaudio.h b/miniaudio.h index 23002564..32b212a0 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -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; }