mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-26 10:14:04 +02:00
Get basic playback working on the OpenAL backend.
This commit is contained in:
@@ -525,6 +525,8 @@ struct mal_device
|
||||
mal_result workResult; // This is set by the worker thread after it's finished doing a job.
|
||||
mal_uint32 flags; // MAL_DEVICE_FLAG_*
|
||||
|
||||
mal_uint32 internalChannels;
|
||||
|
||||
union
|
||||
{
|
||||
#ifdef MAL_ENABLE_WASAPI
|
||||
@@ -590,6 +592,11 @@ struct mal_device
|
||||
/*ALCdevice**/ mal_ptr pDeviceALC;
|
||||
/*ALuint*/ mal_uint32 sourceAL;
|
||||
/*ALuint*/ mal_uint32 buffersAL[MAL_MAX_PERIODS_OPENAL];
|
||||
/*ALenum*/ mal_uint32 formatAL;
|
||||
mal_uint32 subBufferSizeInFrames; // This is the size of each of the OpenAL buffers (buffersAL).
|
||||
mal_uint8* pIntermediaryBuffer; // This is malloc()'d and is used as the destination for reading from the client. Typed as mal_uint8 for easy offsetting.
|
||||
mal_uint32 iNextBuffer; // The next buffer to unenqueue and then re-enqueue as new data is read.
|
||||
mal_bool32 breakFromMainLoop;
|
||||
} openal;
|
||||
#endif
|
||||
|
||||
@@ -4372,6 +4379,8 @@ static void mal_device_uninit__openal(mal_device* pDevice)
|
||||
} else {
|
||||
((MAL_LPALCCAPTURECLOSEDEVICE)pDevice->pContext->openal.alcCaptureCloseDevice)(pDevice->openal.pDeviceALC);
|
||||
}
|
||||
|
||||
mal_free(pDevice->openal.pIntermediaryBuffer);
|
||||
}
|
||||
|
||||
static mal_result mal_device_init__openal(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, mal_device_config* pConfig, mal_device* pDevice)
|
||||
@@ -4383,10 +4392,13 @@ static mal_result mal_device_init__openal(mal_context* pContext, mal_device_type
|
||||
ALCsizei bufferSizeInSamplesAL = pConfig->bufferSizeInFrames;
|
||||
ALCuint frequencyAL = pConfig->sampleRate;
|
||||
|
||||
mal_uint32 channelsAL = 0;
|
||||
|
||||
// OpenAL supports only mono and stereo.
|
||||
ALCenum formatAL = 0;
|
||||
if (pConfig->channels == 1) {
|
||||
// Mono.
|
||||
channelsAL = 1;
|
||||
if (pConfig->format == mal_format_f32) {
|
||||
if (pContext->openal.isFloat32Supported) {
|
||||
formatAL = MAL_AL_FORMAT_MONO_FLOAT32;
|
||||
@@ -4404,7 +4416,7 @@ static mal_result mal_device_init__openal(mal_context* pContext, mal_device_type
|
||||
}
|
||||
} else {
|
||||
// Stereo.
|
||||
bufferSizeInSamplesAL *= 2;
|
||||
channelsAL = 2;
|
||||
if (pConfig->format == mal_format_f32) {
|
||||
if (pContext->openal.isFloat32Supported) {
|
||||
formatAL = MAL_AL_FORMAT_STEREO_FLOAT32;
|
||||
@@ -4426,6 +4438,8 @@ static mal_result mal_device_init__openal(mal_context* pContext, mal_device_type
|
||||
return MAL_FORMAT_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
bufferSizeInSamplesAL *= channelsAL;
|
||||
|
||||
|
||||
// OpenAL feels a bit unintuitive to me... The global object is a device, and it would appear that each device can have
|
||||
// many context's...
|
||||
@@ -4465,22 +4479,150 @@ static mal_result mal_device_init__openal(mal_context* pContext, mal_device_type
|
||||
pDevice->openal.buffersAL[i] = buffersAL[i];
|
||||
}
|
||||
|
||||
pDevice->internalChannels = channelsAL;
|
||||
|
||||
pDevice->openal.formatAL = formatAL;
|
||||
pDevice->openal.subBufferSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods;
|
||||
pDevice->openal.pIntermediaryBuffer = (mal_uint8*)mal_malloc(pDevice->openal.subBufferSizeInFrames * channelsAL * mal_get_sample_size_in_bytes(pDevice->format));
|
||||
if (pDevice->openal.pIntermediaryBuffer == NULL) {
|
||||
mal_device_uninit__openal(pDevice);
|
||||
return MAL_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return MAL_SUCCESS;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static mal_result mal_device__start_backend__openal(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
if (pDevice->type == mal_device_type_playback) {
|
||||
// Playback.
|
||||
//
|
||||
// When starting playback we want to ensure each buffer is filled and queued before playing the source.
|
||||
pDevice->openal.iNextBuffer = 0;
|
||||
|
||||
for (mal_uint32 i = 0; i < pDevice->periods; ++i) {
|
||||
mal_device__read_frames_from_client(pDevice, pDevice->openal.subBufferSizeInFrames, pDevice->openal.pIntermediaryBuffer);
|
||||
|
||||
ALuint bufferAL = pDevice->openal.buffersAL[i];
|
||||
((MAL_LPALBUFFERDATA)pDevice->pContext->openal.alBufferData)(bufferAL, pDevice->openal.formatAL, pDevice->openal.pIntermediaryBuffer, pDevice->openal.subBufferSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->format), pDevice->sampleRate);
|
||||
((MAL_LPALSOURCEQUEUEBUFFERS)pDevice->pContext->openal.alSourceQueueBuffers)(pDevice->openal.sourceAL, 1, &bufferAL);
|
||||
}
|
||||
|
||||
// Start the source only after filling and queueing each buffer.
|
||||
((MAL_LPALSOURCEPLAY)pDevice->pContext->openal.alSourcePlay)(pDevice->openal.sourceAL);
|
||||
} else {
|
||||
// Capture.
|
||||
((MAL_LPALCCAPTURESTART)pDevice->pContext->openal.alcCaptureStart)(pDevice->openal.pDeviceALC);
|
||||
}
|
||||
|
||||
return MAL_SUCCESS;
|
||||
}
|
||||
|
||||
static mal_result mal_device__stop_backend__openal(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
if (pDevice->type == mal_device_type_playback) {
|
||||
((MAL_LPALSOURCESTOP)pDevice->pContext->openal.alSourceStop)(pDevice->openal.sourceAL);
|
||||
} else {
|
||||
((MAL_LPALCCAPTURESTOP)pDevice->pContext->openal.alcCaptureStop)(pDevice->openal.pDeviceALC);
|
||||
}
|
||||
|
||||
return MAL_SUCCESS;
|
||||
}
|
||||
|
||||
static mal_result mal_device__break_main_loop__openal(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
pDevice->openal.breakFromMainLoop = MAL_TRUE;
|
||||
return MAL_SUCCESS;
|
||||
}
|
||||
|
||||
static mal_uint32 mal_device__get_available_frames__openal(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
if (pDevice->type == mal_device_type_playback) {
|
||||
ALint processedBufferCount = 0;
|
||||
((MAL_LPALGETSOURCEI)pDevice->pContext->openal.alGetSourcei)(pDevice->openal.sourceAL, AL_BUFFERS_PROCESSED, &processedBufferCount);
|
||||
|
||||
return processedBufferCount * pDevice->openal.subBufferSizeInFrames;
|
||||
} else {
|
||||
ALint samplesAvailable = 0;
|
||||
((MAL_LPALCGETINTEGERV)pDevice->pContext->openal.alcGetIntegerv)(pDevice->openal.pDeviceALC, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &samplesAvailable);
|
||||
|
||||
return samplesAvailable / pDevice->channels;
|
||||
}
|
||||
}
|
||||
|
||||
static mal_uint32 mal_device__wait_for_frames__openal(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
while (!pDevice->openal.breakFromMainLoop) {
|
||||
mal_uint32 framesAvailable = mal_device__get_available_frames__openal(pDevice);
|
||||
if (framesAvailable > 0) {
|
||||
return framesAvailable;
|
||||
}
|
||||
|
||||
mal_sleep(1);
|
||||
}
|
||||
|
||||
// We'll get here if the loop was terminated. When capturing we want to return whatever is available. For playback we just drop it.
|
||||
if (pDevice->type == mal_device_type_playback) {
|
||||
return 0;
|
||||
} else {
|
||||
return mal_device__get_available_frames__openal(pDevice);
|
||||
}
|
||||
}
|
||||
|
||||
static mal_result mal_device__main_loop__openal(mal_device* pDevice)
|
||||
{
|
||||
mal_assert(pDevice != NULL);
|
||||
|
||||
pDevice->openal.breakFromMainLoop = MAL_FALSE;
|
||||
while (!pDevice->openal.breakFromMainLoop) {
|
||||
mal_uint32 framesAvailable = mal_device__wait_for_frames__openal(pDevice);
|
||||
if (framesAvailable == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it's a playback device, don't bother grabbing more data if the device is being stopped.
|
||||
if (pDevice->openal.breakFromMainLoop && pDevice->type == mal_device_type_playback) {
|
||||
return MAL_FALSE;
|
||||
}
|
||||
|
||||
if (pDevice->type == mal_device_type_playback) {
|
||||
while (framesAvailable > 0) {
|
||||
mal_uint32 framesToRead = (framesAvailable > pDevice->openal.subBufferSizeInFrames) ? pDevice->openal.subBufferSizeInFrames : framesAvailable;
|
||||
|
||||
ALuint bufferAL = pDevice->openal.buffersAL[pDevice->openal.iNextBuffer];
|
||||
pDevice->openal.iNextBuffer = (pDevice->openal.iNextBuffer + 1) % pDevice->periods;
|
||||
|
||||
mal_device__read_frames_from_client(pDevice, framesToRead, pDevice->openal.pIntermediaryBuffer);
|
||||
|
||||
((MAL_LPALSOURCEUNQUEUEBUFFERS)pDevice->pContext->openal.alSourceUnqueueBuffers)(pDevice->openal.sourceAL, 1, &bufferAL);
|
||||
((MAL_LPALBUFFERDATA)pDevice->pContext->openal.alBufferData)(bufferAL, pDevice->openal.formatAL, pDevice->openal.pIntermediaryBuffer, pDevice->openal.subBufferSizeInFrames * pDevice->internalChannels * mal_get_sample_size_in_bytes(pDevice->format), pDevice->sampleRate);
|
||||
((MAL_LPALSOURCEQUEUEBUFFERS)pDevice->pContext->openal.alSourceQueueBuffers)(pDevice->openal.sourceAL, 1, &bufferAL);
|
||||
|
||||
framesAvailable -= framesToRead;
|
||||
}
|
||||
} else {
|
||||
while (framesAvailable > 0) {
|
||||
mal_uint32 framesToSend = (framesAvailable > pDevice->openal.subBufferSizeInFrames) ? pDevice->openal.subBufferSizeInFrames : framesAvailable;
|
||||
((MAL_LPALCCAPTURESAMPLES)pDevice->pContext->openal.alcCaptureSamples)(pDevice->openal.pDeviceALC, pDevice->openal.pIntermediaryBuffer, framesToSend);
|
||||
|
||||
mal_device__send_frames_to_client(pDevice, framesToSend, pDevice->openal.pIntermediaryBuffer);
|
||||
framesAvailable -= framesToSend;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MAL_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
#endif // OpenAL
|
||||
|
||||
|
||||
@@ -4504,6 +4646,11 @@ static mal_result mal_device__start_backend(mal_device* pDevice)
|
||||
result = mal_device__start_backend__alsa(pDevice);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_OPENAL
|
||||
if (pDevice->pContext->backend == mal_backend_openal) {
|
||||
result = mal_device__start_backend__openal(pDevice);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_NULL
|
||||
if (pDevice->pContext->backend == mal_backend_null) {
|
||||
result = mal_device__start_backend__null(pDevice);
|
||||
@@ -4533,6 +4680,11 @@ static mal_result mal_device__stop_backend(mal_device* pDevice)
|
||||
result = mal_device__stop_backend__alsa(pDevice);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_OPENAL
|
||||
if (pDevice->pContext->backend == mal_backend_openal) {
|
||||
result = mal_device__stop_backend__openal(pDevice);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_NULL
|
||||
if (pDevice->pContext->backend == mal_backend_null) {
|
||||
result = mal_device__stop_backend__null(pDevice);
|
||||
@@ -4562,6 +4714,11 @@ static mal_result mal_device__break_main_loop(mal_device* pDevice)
|
||||
result = mal_device__break_main_loop__alsa(pDevice);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_OPENAL
|
||||
if (pDevice->pContext->backend == mal_backend_openal) {
|
||||
result = mal_device__break_main_loop__openal(pDevice);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_NULL
|
||||
if (pDevice->pContext->backend == mal_backend_null) {
|
||||
result = mal_device__break_main_loop__null(pDevice);
|
||||
@@ -4591,6 +4748,11 @@ static mal_result mal_device__main_loop(mal_device* pDevice)
|
||||
result = mal_device__main_loop__alsa(pDevice);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_OPENAL
|
||||
if (pDevice->pContext->backend == mal_backend_openal) {
|
||||
result = mal_device__main_loop__openal(pDevice);
|
||||
}
|
||||
#endif
|
||||
#ifdef MAL_ENABLE_NULL
|
||||
if (pDevice->pContext->backend == mal_backend_null) {
|
||||
result = mal_device__main_loop__null(pDevice);
|
||||
|
||||
Reference in New Issue
Block a user