Commit 3f0e5c58 authored by David Reid's avatar David Reid

Merge branch 'dev' of https://github.com/dr-soft/mini_al into dev

parents e52e9f13 de4f8842
// Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file. // Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file.
// mini_al - v0.8.11 - 2018-11-21 // mini_al - v0.8.12 - 2018-11-27
// //
// David Reid - davidreidsoftware@gmail.com // David Reid - davidreidsoftware@gmail.com
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
// - OSS (FreeBSD) // - OSS (FreeBSD)
// - OpenSL|ES (Android only) // - OpenSL|ES (Android only)
// - OpenAL // - OpenAL
// - SDL // - SDL2
// - Null (Silence) // - Null (Silence)
// //
// Supported Formats: // Supported Formats:
...@@ -77,8 +77,7 @@ ...@@ -77,8 +77,7 @@
// //
// Building for Emscripten // Building for Emscripten
// ----------------------- // -----------------------
// The Emscripten build currently uses SDL 1.2 for it's backend which means specifying "-s USE_SDL=2" is unecessary // The Emscripten build uses SDL2 and requires "-s USE_SDL=2" on the command line.
// as of this version.
// //
// //
// Playback Example // Playback Example
...@@ -1840,16 +1839,11 @@ struct mal_context ...@@ -1840,16 +1839,11 @@ struct mal_context
mal_handle hSDL; // SDL mal_handle hSDL; // SDL
mal_proc SDL_InitSubSystem; mal_proc SDL_InitSubSystem;
mal_proc SDL_QuitSubSystem; mal_proc SDL_QuitSubSystem;
mal_proc SDL_CloseAudio;
mal_proc SDL_OpenAudio;
mal_proc SDL_PauseAudio;
mal_proc SDL_GetNumAudioDevices; mal_proc SDL_GetNumAudioDevices;
mal_proc SDL_GetAudioDeviceName; mal_proc SDL_GetAudioDeviceName;
mal_proc SDL_CloseAudioDevice; mal_proc SDL_CloseAudioDevice;
mal_proc SDL_OpenAudioDevice; mal_proc SDL_OpenAudioDevice;
mal_proc SDL_PauseAudioDevice; mal_proc SDL_PauseAudioDevice;
mal_bool32 usingSDL1;
} sdl; } sdl;
#endif #endif
#ifdef MAL_SUPPORT_NULL #ifdef MAL_SUPPORT_NULL
...@@ -19564,8 +19558,6 @@ mal_result mal_context_init__openal(mal_context* pContext) ...@@ -19564,8 +19558,6 @@ mal_result mal_context_init__openal(mal_context* pContext)
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#ifdef MAL_HAS_SDL #ifdef MAL_HAS_SDL
//#define MAL_USE_SDL_1
#define MAL_SDL_INIT_AUDIO 0x00000010 #define MAL_SDL_INIT_AUDIO 0x00000010
#define MAL_AUDIO_U8 0x0008 #define MAL_AUDIO_U8 0x0008
#define MAL_AUDIO_S16 0x8010 #define MAL_AUDIO_S16 0x8010
...@@ -19582,11 +19574,6 @@ mal_result mal_context_init__openal(mal_context* pContext) ...@@ -19582,11 +19574,6 @@ mal_result mal_context_init__openal(mal_context* pContext)
#define SDL_MAIN_HANDLED #define SDL_MAIN_HANDLED
#ifdef MAL_EMSCRIPTEN #ifdef MAL_EMSCRIPTEN
#include <SDL/SDL.h> #include <SDL/SDL.h>
// For now just use SDL 1.2 with Emscripten. This avoids the need for "-s USE_SDL=2" at compile time.
#ifndef MAL_USE_SDL_1
#define MAL_USE_SDL_1
#endif
#else #else
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#endif #endif
...@@ -19618,11 +19605,8 @@ typedef int (* MAL_PFN_SDL_InitSubSystem)(mal_uint32 flags); ...@@ -19618,11 +19605,8 @@ typedef int (* MAL_PFN_SDL_InitSubSystem)(mal_uint32 flags);
typedef void (* MAL_PFN_SDL_QuitSubSystem)(mal_uint32 flags); typedef void (* MAL_PFN_SDL_QuitSubSystem)(mal_uint32 flags);
typedef int (* MAL_PFN_SDL_GetNumAudioDevices)(int iscapture); typedef int (* MAL_PFN_SDL_GetNumAudioDevices)(int iscapture);
typedef const char* (* MAL_PFN_SDL_GetAudioDeviceName)(int index, int iscapture); typedef const char* (* MAL_PFN_SDL_GetAudioDeviceName)(int index, int iscapture);
typedef void (* MAL_PFN_SDL_CloseAudio)(void);
typedef void (* MAL_PFN_SDL_CloseAudioDevice)(MAL_SDL_AudioDeviceID dev); typedef void (* MAL_PFN_SDL_CloseAudioDevice)(MAL_SDL_AudioDeviceID dev);
typedef int (* MAL_PFN_SDL_OpenAudio)(MAL_SDL_AudioSpec* desired, MAL_SDL_AudioSpec* obtained);
typedef MAL_SDL_AudioDeviceID (* MAL_PFN_SDL_OpenAudioDevice)(const char* device, int iscapture, const MAL_SDL_AudioSpec* desired, MAL_SDL_AudioSpec* obtained, int allowed_changes); typedef MAL_SDL_AudioDeviceID (* MAL_PFN_SDL_OpenAudioDevice)(const char* device, int iscapture, const MAL_SDL_AudioSpec* desired, MAL_SDL_AudioSpec* obtained, int allowed_changes);
typedef void (* MAL_PFN_SDL_PauseAudio)(int pause_on);
typedef void (* MAL_PFN_SDL_PauseAudioDevice)(MAL_SDL_AudioDeviceID dev, int pause_on); typedef void (* MAL_PFN_SDL_PauseAudioDevice)(MAL_SDL_AudioDeviceID dev, int pause_on);
MAL_SDL_AudioFormat mal_format_to_sdl(mal_format format) MAL_SDL_AudioFormat mal_format_to_sdl(mal_format format)
...@@ -19665,68 +19649,42 @@ mal_result mal_context_enumerate_devices__sdl(mal_context* pContext, mal_enum_de ...@@ -19665,68 +19649,42 @@ mal_result mal_context_enumerate_devices__sdl(mal_context* pContext, mal_enum_de
mal_assert(pContext != NULL); mal_assert(pContext != NULL);
mal_assert(callback != NULL); mal_assert(callback != NULL);
#ifndef MAL_USE_SDL_1 mal_bool32 isTerminated = MAL_FALSE;
if (!pContext->sdl.usingSDL1) {
mal_bool32 isTerminated = MAL_FALSE;
// Playback
if (!isTerminated) {
int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)(0);
for (int i = 0; i < deviceCount; ++i) {
mal_device_info deviceInfo;
mal_zero_object(&deviceInfo);
deviceInfo.id.sdl = i;
mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, 0), (size_t)-1);
mal_bool32 cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData);
if (cbResult == MAL_FALSE) {
isTerminated = MAL_TRUE;
break;
}
}
}
// Capture // Playback
if (!isTerminated) { if (!isTerminated) {
int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)(1); int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)(0);
for (int i = 0; i < deviceCount; ++i) { for (int i = 0; i < deviceCount; ++i) {
mal_device_info deviceInfo; mal_device_info deviceInfo;
mal_zero_object(&deviceInfo); mal_zero_object(&deviceInfo);
deviceInfo.id.sdl = i; deviceInfo.id.sdl = i;
mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, 1), (size_t)-1); mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, 0), (size_t)-1);
mal_bool32 cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); mal_bool32 cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData);
if (cbResult == MAL_FALSE) { if (cbResult == MAL_FALSE) {
isTerminated = MAL_TRUE; isTerminated = MAL_TRUE;
break; break;
}
} }
} }
} else }
#endif
{
// SDL1 only uses default devices, and does not support capture.
mal_bool32 cbResult = MAL_TRUE;
// Playback. // Capture
if (cbResult) { if (!isTerminated) {
int deviceCount = ((MAL_PFN_SDL_GetNumAudioDevices)pContext->sdl.SDL_GetNumAudioDevices)(1);
for (int i = 0; i < deviceCount; ++i) {
mal_device_info deviceInfo; mal_device_info deviceInfo;
mal_zero_object(&deviceInfo); mal_zero_object(&deviceInfo);
mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
cbResult = callback(pContext, mal_device_type_playback, &deviceInfo, pUserData);
}
#if 0 // No capture with SDL1. deviceInfo.id.sdl = i;
// Capture. mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(i, 1), (size_t)-1);
if (cbResult) {
mal_device_info deviceInfo; mal_bool32 cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData);
mal_zero_object(&deviceInfo); if (cbResult == MAL_FALSE) {
mal_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); isTerminated = MAL_TRUE;
cbResult = callback(pContext, mal_device_type_capture, &deviceInfo, pUserData); break;
}
} }
#endif
} }
return MAL_SUCCESS; return MAL_SUCCESS;
...@@ -19737,24 +19695,7 @@ mal_result mal_context_get_device_info__sdl(mal_context* pContext, mal_device_ty ...@@ -19737,24 +19695,7 @@ mal_result mal_context_get_device_info__sdl(mal_context* pContext, mal_device_ty
mal_assert(pContext != NULL); mal_assert(pContext != NULL);
(void)shareMode; (void)shareMode;
#ifndef MAL_USE_SDL_1 if (pDeviceID == NULL) {
if (!pContext->sdl.usingSDL1) {
if (pDeviceID == NULL) {
if (deviceType == mal_device_type_playback) {
pDeviceInfo->id.sdl = 0;
mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
} else {
pDeviceInfo->id.sdl = 0;
mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
}
} else {
pDeviceInfo->id.sdl = pDeviceID->sdl;
mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, (deviceType == mal_device_type_playback) ? 0 : 1), (size_t)-1);
}
} else
#endif
{
// SDL1 uses default devices.
if (deviceType == mal_device_type_playback) { if (deviceType == mal_device_type_playback) {
pDeviceInfo->id.sdl = 0; pDeviceInfo->id.sdl = 0;
mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
...@@ -19762,6 +19703,9 @@ mal_result mal_context_get_device_info__sdl(mal_context* pContext, mal_device_ty ...@@ -19762,6 +19703,9 @@ mal_result mal_context_get_device_info__sdl(mal_context* pContext, mal_device_ty
pDeviceInfo->id.sdl = 0; pDeviceInfo->id.sdl = 0;
mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MAL_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
} }
} else {
pDeviceInfo->id.sdl = pDeviceID->sdl;
mal_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, (deviceType == mal_device_type_playback) ? 0 : 1), (size_t)-1);
} }
// To get an accurate idea on the backend's native format we need to open the device. Not ideal, but it's the only way. An // To get an accurate idea on the backend's native format we need to open the device. Not ideal, but it's the only way. An
...@@ -19785,40 +19729,20 @@ mal_result mal_context_get_device_info__sdl(mal_context* pContext, mal_device_ty ...@@ -19785,40 +19729,20 @@ mal_result mal_context_get_device_info__sdl(mal_context* pContext, mal_device_ty
MAL_SDL_AudioSpec desiredSpec, obtainedSpec; MAL_SDL_AudioSpec desiredSpec, obtainedSpec;
mal_zero_memory(&desiredSpec, sizeof(desiredSpec)); mal_zero_memory(&desiredSpec, sizeof(desiredSpec));
#ifndef MAL_USE_SDL_1 int isCapture = (deviceType == mal_device_type_playback) ? 0 : 1;
if (!pContext->sdl.usingSDL1) {
int isCapture = (deviceType == mal_device_type_playback) ? 0 : 1;
const char* pDeviceName = NULL; const char* pDeviceName = NULL;
if (pDeviceID != NULL) { if (pDeviceID != NULL) {
pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture); pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture);
} }
MAL_SDL_AudioDeviceID tempDeviceID = ((MAL_PFN_SDL_OpenAudioDevice)pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE);
if (tempDeviceID == 0) {
return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
((MAL_PFN_SDL_CloseAudioDevice)pContext->sdl.SDL_CloseAudioDevice)(tempDeviceID);
} else
#endif
{
// SDL1 uses default devices.
(void)pDeviceID;
// SDL1 only supports playback as far as I can tell.
if (deviceType != mal_device_type_playback) {
return MAL_NO_DEVICE;
}
MAL_SDL_AudioDeviceID tempDeviceID = ((MAL_PFN_SDL_OpenAudio)pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec);
if (tempDeviceID != 0) {
return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
((MAL_PFN_SDL_CloseAudio)pContext->sdl.SDL_CloseAudio)(); MAL_SDL_AudioDeviceID tempDeviceID = ((MAL_PFN_SDL_OpenAudioDevice)pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE);
if (tempDeviceID == 0) {
return mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
} }
((MAL_PFN_SDL_CloseAudioDevice)pContext->sdl.SDL_CloseAudioDevice)(tempDeviceID);
pDeviceInfo->minChannels = obtainedSpec.channels; pDeviceInfo->minChannels = obtainedSpec.channels;
pDeviceInfo->maxChannels = obtainedSpec.channels; pDeviceInfo->maxChannels = obtainedSpec.channels;
pDeviceInfo->minSampleRate = obtainedSpec.freq; pDeviceInfo->minSampleRate = obtainedSpec.freq;
...@@ -19845,14 +19769,7 @@ void mal_device_uninit__sdl(mal_device* pDevice) ...@@ -19845,14 +19769,7 @@ void mal_device_uninit__sdl(mal_device* pDevice)
{ {
mal_assert(pDevice != NULL); mal_assert(pDevice != NULL);
#ifndef MAL_USE_SDL_1 ((MAL_PFN_SDL_CloseAudioDevice)pDevice->pContext->sdl.SDL_CloseAudioDevice)(pDevice->sdl.deviceID);
if (!pDevice->pContext->sdl.usingSDL1) {
((MAL_PFN_SDL_CloseAudioDevice)pDevice->pContext->sdl.SDL_CloseAudioDevice)(pDevice->sdl.deviceID);
} else
#endif
{
((MAL_PFN_SDL_CloseAudio)pDevice->pContext->sdl.SDL_CloseAudio)();
}
} }
...@@ -19864,7 +19781,7 @@ void mal_audio_callback__sdl(void* pUserData, mal_uint8* pBuffer, int bufferSize ...@@ -19864,7 +19781,7 @@ void mal_audio_callback__sdl(void* pUserData, mal_uint8* pBuffer, int bufferSize
mal_uint32 bufferSizeInFrames = (mal_uint32)bufferSizeInBytes / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); mal_uint32 bufferSizeInFrames = (mal_uint32)bufferSizeInBytes / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
#ifdef MAL_DEBUG_OUTPUT #ifdef MAL_DEBUG_OUTPUT
printf("[SDL] Callback: bufferSizeInBytes=%d, bufferSizeInFrames=%d\n", bufferSizeInBytes, bufferSizeInFrames); printf("[SDL] Callback: bufferSizeInBytes=%d, bufferSizeInFrames=%d\n", bufferSizeInBytes, bufferSizeInFrames);
#endif #endif
if (pDevice->type == mal_device_type_playback) { if (pDevice->type == mal_device_type_playback) {
...@@ -19912,41 +19829,16 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con ...@@ -19912,41 +19829,16 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
desiredSpec.format = MAL_AUDIO_F32; desiredSpec.format = MAL_AUDIO_F32;
} }
#ifndef MAL_USE_SDL_1 int isCapture = (type == mal_device_type_playback) ? 0 : 1;
if (!pDevice->pContext->sdl.usingSDL1) {
int isCapture = (type == mal_device_type_playback) ? 0 : 1;
const char* pDeviceName = NULL;
if (pDeviceID != NULL) {
pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pDevice->pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture);
}
pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE);
if (pDevice->sdl.deviceID == 0) {
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL2 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
} else
#endif
{
// SDL1 uses default devices.
(void)pDeviceID;
// SDL1 only supports playback as far as I can tell. const char* pDeviceName = NULL;
if (type != mal_device_type_playback) { if (pDeviceID != NULL) {
return MAL_NO_DEVICE; pDeviceName = ((MAL_PFN_SDL_GetAudioDeviceName)pDevice->pContext->sdl.SDL_GetAudioDeviceName)(pDeviceID->sdl, isCapture);
} }
// SDL1 does not support floating point formats.
if (desiredSpec.format == MAL_AUDIO_F32) {
desiredSpec.format = MAL_AUDIO_S16;
}
int deviceID = ((MAL_PFN_SDL_OpenAudio)pDevice->pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec);
if (deviceID < 0) {
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL1 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
pDevice->sdl.deviceID = (mal_uint32)deviceID; pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE);
if (pDevice->sdl.deviceID == 0) {
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL2 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
} }
pDevice->internalFormat = mal_format_from_sdl(obtainedSpec.format); pDevice->internalFormat = mal_format_from_sdl(obtainedSpec.format);
...@@ -19958,7 +19850,6 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con ...@@ -19958,7 +19850,6 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
#ifdef MAL_DEBUG_OUTPUT #ifdef MAL_DEBUG_OUTPUT
printf("=== SDL CONFIG ===\n"); printf("=== SDL CONFIG ===\n");
printf(" SDL VERSION: %s\n", pDevice->pContext->sdl.usingSDL1 ? "1" : "2");
printf(" FORMAT: %s -> %s\n", mal_get_format_name(pConfig->format), mal_get_format_name(pDevice->internalFormat)); printf(" FORMAT: %s -> %s\n", mal_get_format_name(pConfig->format), mal_get_format_name(pDevice->internalFormat));
printf(" CHANNELS: %d -> %d\n", desiredSpec.channels, obtainedSpec.channels); printf(" CHANNELS: %d -> %d\n", desiredSpec.channels, obtainedSpec.channels);
printf(" SAMPLE RATE: %d -> %d\n", desiredSpec.freq, obtainedSpec.freq); printf(" SAMPLE RATE: %d -> %d\n", desiredSpec.freq, obtainedSpec.freq);
...@@ -19972,15 +19863,7 @@ mal_result mal_device__start_backend__sdl(mal_device* pDevice) ...@@ -19972,15 +19863,7 @@ mal_result mal_device__start_backend__sdl(mal_device* pDevice)
{ {
mal_assert(pDevice != NULL); mal_assert(pDevice != NULL);
#ifndef MAL_USE_SDL_1 ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 0);
if (!pDevice->pContext->sdl.usingSDL1) {
((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 0);
} else
#endif
{
((MAL_PFN_SDL_PauseAudio)pDevice->pContext->sdl.SDL_PauseAudio)(0);
}
return MAL_SUCCESS; return MAL_SUCCESS;
} }
...@@ -19988,14 +19871,7 @@ mal_result mal_device__stop_backend__sdl(mal_device* pDevice) ...@@ -19988,14 +19871,7 @@ mal_result mal_device__stop_backend__sdl(mal_device* pDevice)
{ {
mal_assert(pDevice != NULL); mal_assert(pDevice != NULL);
#ifndef MAL_USE_SDL_1 ((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 1);
if (!pDevice->pContext->sdl.usingSDL1) {
((MAL_PFN_SDL_PauseAudioDevice)pDevice->pContext->sdl.SDL_PauseAudioDevice)(pDevice->sdl.deviceID, 1);
} else
#endif
{
((MAL_PFN_SDL_PauseAudio)pDevice->pContext->sdl.SDL_PauseAudio)(1);
}
mal_device__set_state(pDevice, MAL_STATE_STOPPED); mal_device__set_state(pDevice, MAL_STATE_STOPPED);
mal_stop_proc onStop = pDevice->onStop; mal_stop_proc onStop = pDevice->onStop;
...@@ -20024,14 +19900,11 @@ mal_result mal_context_init__sdl(mal_context* pContext) ...@@ -20024,14 +19900,11 @@ mal_result mal_context_init__sdl(mal_context* pContext)
// Run-time linking. // Run-time linking.
const char* libNames[] = { const char* libNames[] = {
#if defined(MAL_WIN32) #if defined(MAL_WIN32)
"SDL2.dll", "SDL2.dll"
"SDL.dll"
#elif defined(MAL_APPLE) #elif defined(MAL_APPLE)
"SDL2.framework/SDL2", "SDL2.framework/SDL2"
"SDL.framework/SDL"
#else #else
"libSDL2-2.0.so.0", "libSDL2-2.0.so.0"
"libSDL-1.2.so.0"
#endif #endif
}; };
...@@ -20048,42 +19921,22 @@ mal_result mal_context_init__sdl(mal_context* pContext) ...@@ -20048,42 +19921,22 @@ mal_result mal_context_init__sdl(mal_context* pContext)
pContext->sdl.SDL_InitSubSystem = mal_dlsym(pContext->sdl.hSDL, "SDL_InitSubSystem"); pContext->sdl.SDL_InitSubSystem = mal_dlsym(pContext->sdl.hSDL, "SDL_InitSubSystem");
pContext->sdl.SDL_QuitSubSystem = mal_dlsym(pContext->sdl.hSDL, "SDL_QuitSubSystem"); pContext->sdl.SDL_QuitSubSystem = mal_dlsym(pContext->sdl.hSDL, "SDL_QuitSubSystem");
pContext->sdl.SDL_CloseAudio = mal_dlsym(pContext->sdl.hSDL, "SDL_CloseAudio");
pContext->sdl.SDL_OpenAudio = mal_dlsym(pContext->sdl.hSDL, "SDL_OpenAudio");
pContext->sdl.SDL_PauseAudio = mal_dlsym(pContext->sdl.hSDL, "SDL_PauseAudio");
#ifndef MAL_USE_SDL_1
pContext->sdl.SDL_GetNumAudioDevices = mal_dlsym(pContext->sdl.hSDL, "SDL_GetNumAudioDevices"); pContext->sdl.SDL_GetNumAudioDevices = mal_dlsym(pContext->sdl.hSDL, "SDL_GetNumAudioDevices");
pContext->sdl.SDL_GetAudioDeviceName = mal_dlsym(pContext->sdl.hSDL, "SDL_GetAudioDeviceName"); pContext->sdl.SDL_GetAudioDeviceName = mal_dlsym(pContext->sdl.hSDL, "SDL_GetAudioDeviceName");
pContext->sdl.SDL_CloseAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_CloseAudioDevice"); pContext->sdl.SDL_CloseAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_CloseAudioDevice");
pContext->sdl.SDL_OpenAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_OpenAudioDevice"); pContext->sdl.SDL_OpenAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_OpenAudioDevice");
pContext->sdl.SDL_PauseAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_PauseAudioDevice"); pContext->sdl.SDL_PauseAudioDevice = mal_dlsym(pContext->sdl.hSDL, "SDL_PauseAudioDevice");
#endif
#else #else
// Compile-time linking. // Compile-time linking.
pContext->sdl.SDL_InitSubSystem = (mal_proc)SDL_InitSubSystem; pContext->sdl.SDL_InitSubSystem = (mal_proc)SDL_InitSubSystem;
pContext->sdl.SDL_QuitSubSystem = (mal_proc)SDL_QuitSubSystem; pContext->sdl.SDL_QuitSubSystem = (mal_proc)SDL_QuitSubSystem;
pContext->sdl.SDL_CloseAudio = (mal_proc)SDL_CloseAudio;
pContext->sdl.SDL_OpenAudio = (mal_proc)SDL_OpenAudio;
pContext->sdl.SDL_PauseAudio = (mal_proc)SDL_PauseAudio;
#ifndef MAL_USE_SDL_1
pContext->sdl.SDL_GetNumAudioDevices = (mal_proc)SDL_GetNumAudioDevices; pContext->sdl.SDL_GetNumAudioDevices = (mal_proc)SDL_GetNumAudioDevices;
pContext->sdl.SDL_GetAudioDeviceName = (mal_proc)SDL_GetAudioDeviceName; pContext->sdl.SDL_GetAudioDeviceName = (mal_proc)SDL_GetAudioDeviceName;
pContext->sdl.SDL_CloseAudioDevice = (mal_proc)SDL_CloseAudioDevice; pContext->sdl.SDL_CloseAudioDevice = (mal_proc)SDL_CloseAudioDevice;
pContext->sdl.SDL_OpenAudioDevice = (mal_proc)SDL_OpenAudioDevice; pContext->sdl.SDL_OpenAudioDevice = (mal_proc)SDL_OpenAudioDevice;
pContext->sdl.SDL_PauseAudioDevice = (mal_proc)SDL_PauseAudioDevice; pContext->sdl.SDL_PauseAudioDevice = (mal_proc)SDL_PauseAudioDevice;
#endif
#endif #endif
// We need to determine whether or not we are using SDL2 or SDL1. We can know this by looking at whether or not certain
// function pointers are NULL.
if (pContext->sdl.SDL_GetNumAudioDevices == NULL ||
pContext->sdl.SDL_GetAudioDeviceName == NULL ||
pContext->sdl.SDL_CloseAudioDevice == NULL ||
pContext->sdl.SDL_OpenAudioDevice == NULL ||
pContext->sdl.SDL_PauseAudioDevice == NULL) {
pContext->sdl.usingSDL1 = MAL_TRUE;
}
int resultSDL = ((MAL_PFN_SDL_InitSubSystem)pContext->sdl.SDL_InitSubSystem)(MAL_SDL_INIT_AUDIO); int resultSDL = ((MAL_PFN_SDL_InitSubSystem)pContext->sdl.SDL_InitSubSystem)(MAL_SDL_INIT_AUDIO);
if (resultSDL != 0) { if (resultSDL != 0) {
return MAL_ERROR; return MAL_ERROR;
...@@ -28562,6 +28415,11 @@ mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount ...@@ -28562,6 +28415,11 @@ mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount
// REVISION HISTORY // REVISION HISTORY
// ================ // ================
// //
// v0.8.12 - 2018-11-27
// - Drop support for SDL 1.2. The Emscripten build now requires "-s USE_SDL=2".
// - Fix a linking error with ALSA.
// - Fix a bug on iOS where the device name is not set correctly.
//
// v0.8.11 - 2018-11-21 // v0.8.11 - 2018-11-21
// - iOS bug fixes. // - iOS bug fixes.
// - Minor tweaks to PulseAudio. // - Minor tweaks to PulseAudio.
...@@ -5,7 +5,6 @@ This is research into a new resampler for mini_al. Not yet complete. ...@@ -5,7 +5,6 @@ This is research into a new resampler for mini_al. Not yet complete.
Requirements: Requirements:
- Selection of different algorithms. The following at a minimum: - Selection of different algorithms. The following at a minimum:
- Passthrough
- Linear with optional filtering - Linear with optional filtering
- Sinc - Sinc
- Floating point pipeline for f32 and fixed point integer pipeline for s16 - Floating point pipeline for f32 and fixed point integer pipeline for s16
...@@ -39,6 +38,8 @@ Other Notes: ...@@ -39,6 +38,8 @@ Other Notes:
Random Notes: Random Notes:
- You cannot change the algorithm after initialization. - You cannot change the algorithm after initialization.
- It is recommended to keep the mal_resampler object aligned to MAL_SIMD_ALIGNMENT, though it is not necessary. - It is recommended to keep the mal_resampler object aligned to MAL_SIMD_ALIGNMENT, though it is not necessary.
- Ratios need to be in the range of MAL_RESAMPLER_MIN_RATIO and MAL_RESAMPLER_MAX_RATIO. If you need extreme ratios
then you will need to chain resamplers together.
*/ */
#ifndef mal_resampler_h #ifndef mal_resampler_h
#define mal_resampler_h #define mal_resampler_h
...@@ -51,22 +52,18 @@ Random Notes: ...@@ -51,22 +52,18 @@ Random Notes:
typedef struct mal_resampler mal_resampler; typedef struct mal_resampler mal_resampler;
/* Client callbacks. */ /* Client callbacks. */
typedef mal_uint32 (* mal_resampler_read_from_client_proc) (mal_resampler* pResampler, mal_uint32 frameCount, void** ppFrames); typedef mal_uint32 (* mal_resampler_read_from_client_proc)(mal_resampler* pResampler, mal_uint32 frameCount, void** ppFrames);
/* Backend functions. */ /* Backend functions. */
typedef mal_result (* mal_resampler_init_proc) (mal_resampler* pResampler); typedef mal_result (* mal_resampler_init_proc)(mal_resampler* pResampler);
typedef mal_uint64 (* mal_resampler_read_proc) (mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames); typedef mal_uint64 (* mal_resampler_read_proc)(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames);
typedef mal_uint64 (* mal_resampler_seek_proc) (mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options); typedef mal_uint64 (* mal_resampler_seek_proc)(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options);
typedef mal_result (* mal_resampler_get_cached_time_proc) (mal_resampler* pResampler, double* pInputTime, double* pOutputTime);
typedef mal_uint64 (* mal_resampler_get_required_input_frame_count_proc) (mal_resampler* pResampler, mal_uint64 outputFrameCount);
typedef mal_uint64 (* mal_resampler_get_expected_output_frame_count_proc)(mal_resampler* pResampler, mal_uint64 inputFrameCount);
typedef enum typedef enum
{ {
mal_resample_algorithm_sinc = 0, /* Default. */ mal_resampler_algorithm_sinc = 0, /* Default. */
mal_resample_algorithm_linear, /* Fastest. */ mal_resampler_algorithm_linear, /* Fastest. */
mal_resample_algorithm_passthrough /* No resampling. */ } mal_resampler_algorithm;
} mal_resample_algorithm;
typedef enum typedef enum
{ {
...@@ -81,7 +78,7 @@ typedef struct ...@@ -81,7 +78,7 @@ typedef struct
mal_uint32 sampleRateIn; mal_uint32 sampleRateIn;
mal_uint32 sampleRateOut; mal_uint32 sampleRateOut;
double ratio; /* ratio = in/out */ double ratio; /* ratio = in/out */
mal_resample_algorithm algorithm; mal_resampler_algorithm algorithm;
mal_resampler_end_of_input_mode endOfInputMode; mal_resampler_end_of_input_mode endOfInputMode;
mal_resampler_read_from_client_proc onRead; mal_resampler_read_from_client_proc onRead;
void* pUserData; void* pUserData;
...@@ -94,7 +91,7 @@ struct mal_resampler ...@@ -94,7 +91,7 @@ struct mal_resampler
float f32[MAL_RESAMPLER_CACHE_SIZE_IN_BYTES/sizeof(float)]; float f32[MAL_RESAMPLER_CACHE_SIZE_IN_BYTES/sizeof(float)];
mal_int16 s16[MAL_RESAMPLER_CACHE_SIZE_IN_BYTES/sizeof(mal_int16)]; mal_int16 s16[MAL_RESAMPLER_CACHE_SIZE_IN_BYTES/sizeof(mal_int16)];
} cache; /* Do not use directly. Keep this as the first member of this structure for SIMD alignment purposes. */ } cache; /* Do not use directly. Keep this as the first member of this structure for SIMD alignment purposes. */
mal_uint16 firstCachedFrame; mal_uint16 firstCachedFrameOffset;
mal_uint16 cacheLengthInFrames; /* The number of valid frames sitting in the cache. May be less than the cache's capacity. */ mal_uint16 cacheLengthInFrames; /* The number of valid frames sitting in the cache. May be less than the cache's capacity. */
mal_uint16 windowLength; mal_uint16 windowLength;
double windowTime; /* By input rate. Relative to the start of the cache. */ double windowTime; /* By input rate. Relative to the start of the cache. */
...@@ -102,9 +99,6 @@ struct mal_resampler ...@@ -102,9 +99,6 @@ struct mal_resampler
mal_resampler_init_proc init; mal_resampler_init_proc init;
mal_resampler_read_proc read; mal_resampler_read_proc read;
mal_resampler_seek_proc seek; mal_resampler_seek_proc seek;
mal_resampler_get_cached_time_proc getCachedTime;
mal_resampler_get_required_input_frame_count_proc getRequiredInputFrameCount;
mal_resampler_get_expected_output_frame_count_proc getExpectedOutputFrameCount;
}; };
/* /*
...@@ -149,8 +143,6 @@ mal_uint64 mal_resampler_seek(mal_resampler* pResampler, mal_uint64 frameCount, ...@@ -149,8 +143,6 @@ mal_uint64 mal_resampler_seek(mal_resampler* pResampler, mal_uint64 frameCount,
Retrieves the number of cached input frames. Retrieves the number of cached input frames.
This is equivalent to: (mal_uint64)ceil(mal_resampler_get_cached_input_time(pResampler)); This is equivalent to: (mal_uint64)ceil(mal_resampler_get_cached_input_time(pResampler));
See also: mal_resampler_get_cached_frame_counts()
*/ */
mal_uint64 mal_resampler_get_cached_input_frame_count(mal_resampler* pResampler); mal_uint64 mal_resampler_get_cached_input_frame_count(mal_resampler* pResampler);
...@@ -158,20 +150,24 @@ mal_uint64 mal_resampler_get_cached_input_frame_count(mal_resampler* pResampler) ...@@ -158,20 +150,24 @@ mal_uint64 mal_resampler_get_cached_input_frame_count(mal_resampler* pResampler)
Retrieves the number of whole output frames that can be calculated from the currently cached input frames. Retrieves the number of whole output frames that can be calculated from the currently cached input frames.
This is equivalent to: (mal_uint64)floor(mal_resampler_get_cached_output_time(pResampler)); This is equivalent to: (mal_uint64)floor(mal_resampler_get_cached_output_time(pResampler));
See also: mal_resampler_get_cached_frame_counts()
*/ */
mal_uint64 mal_resampler_get_cached_output_frame_count(mal_resampler* pResampler); mal_uint64 mal_resampler_get_cached_output_frame_count(mal_resampler* pResampler);
/* /*
The same as mal_resampler_get_cached_input_frame_count(), except returns a fractional value representing the exact amount The same as mal_resampler_get_cached_input_frame_count(), except returns a fractional value representing the exact amount
of time in input rate making up the cached input. of time in input rate making up the cached input.
When the end of input mode is set to mal_resampler_end_of_input_mode_no_consume, the input frames currently sitting in the
window are not included in the calculation.
*/ */
double mal_resampler_get_cached_input_time(mal_resampler* pResampler); double mal_resampler_get_cached_input_time(mal_resampler* pResampler);
/* /*
The same as mal_resampler_get_cached_output_frame_count(), except returns a fractional value representing the exact amount The same as mal_resampler_get_cached_output_frame_count(), except returns a fractional value representing the exact amount
of time in output rate making up the cached output. of time in output rate making up the cached output.
When the end of input mode is set to mal_resampler_end_of_input_mode_no_consume, the input frames currently sitting in the
window are not included in the calculation.
*/ */
double mal_resampler_get_cached_output_time(mal_resampler* pResampler); double mal_resampler_get_cached_output_time(mal_resampler* pResampler);
...@@ -192,10 +188,10 @@ Calculates the number of whole output frames that would be output after fully re ...@@ -192,10 +188,10 @@ Calculates the number of whole output frames that would be output after fully re
input frames from the client. input frames from the client.
A detail to keep in mind is how cached input frames are handled. This function calculates the output frame count based on A detail to keep in mind is how cached input frames are handled. This function calculates the output frame count based on
inputFrameCount + mal_resampler_get_cached_input_frame_count(). It essentially calcualtes how many output frames will be inputFrameCount + mal_resampler_get_cached_input_time(). It essentially calcualtes how many output frames will be returned
returned if an additional inputFrameCount frames were read from the client and consumed by the resampler. You can adjust if an additional inputFrameCount frames were read from the client and consumed by the resampler. You can adjust the return
the return value by mal_resampler_get_cached_output_frame_count() which calculates the number of output frames that can be value by mal_resampler_get_cached_output_frame_count() which calculates the number of output frames that can be output from
output from the currently cached input. the currently cached input.
When the end of input mode is set to mal_resampler_end_of_input_mode_no_consume, the input frames sitting in the filter When the end of input mode is set to mal_resampler_end_of_input_mode_no_consume, the input frames sitting in the filter
window are not included in the calculation. window are not included in the calculation.
...@@ -204,27 +200,23 @@ mal_uint64 mal_resampler_get_expected_output_frame_count(mal_resampler* pResampl ...@@ -204,27 +200,23 @@ mal_uint64 mal_resampler_get_expected_output_frame_count(mal_resampler* pResampl
#endif #endif
#ifdef MINI_AL_IMPLEMENTATION #ifdef MINI_AL_IMPLEMENTATION
mal_uint64 mal_resampler_read__passthrough(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames);
mal_uint64 mal_resampler_seek__passthrough(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options); #ifndef MAL_RESAMPLER_MIN_RATIO
mal_result mal_resampler_get_cached_time__passthrough(mal_resampler* pResampler, double* pInputTime, double* pOutputTime); #define MAL_RESAMPLER_MIN_RATIO 0.001
mal_uint64 mal_resampler_get_required_input_frame_count__passthrough(mal_resampler* pResampler, mal_uint64 outputFrameCount); #endif
mal_uint64 mal_resampler_get_expected_output_frame_count__passthrough(mal_resampler* pResampler, mal_uint64 inputFrameCount); #ifndef MAL_RESAMPLER_MAX_RATIO
#define MAL_RESAMPLER_MAX_RATIO 100.0
#endif
mal_uint64 mal_resampler_read__linear(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames); mal_uint64 mal_resampler_read__linear(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames);
mal_uint64 mal_resampler_seek__linear(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options); mal_uint64 mal_resampler_seek__linear(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options);
mal_result mal_resampler_get_cached_time__linear(mal_resampler* pResampler, double* pInputTime, double* pOutputTime);
mal_uint64 mal_resampler_get_required_input_frame_count__linear(mal_resampler* pResampler, mal_uint64 outputFrameCount);
mal_uint64 mal_resampler_get_expected_output_frame_count__linear(mal_resampler* pResampler, mal_uint64 inputFrameCount);
mal_result mal_resampler_init__sinc(mal_resampler* pResampler); mal_result mal_resampler_init__sinc(mal_resampler* pResampler);
mal_uint64 mal_resampler_read__sinc(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames); mal_uint64 mal_resampler_read__sinc(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames);
mal_uint64 mal_resampler_seek__sinc(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options); mal_uint64 mal_resampler_seek__sinc(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options);
mal_result mal_resampler_get_cached_time__sinc(mal_resampler* pResampler, double* pInputTime, double* pOutputTime);
mal_uint64 mal_resampler_get_required_input_frame_count__sinc(mal_resampler* pResampler, mal_uint64 outputFrameCount);
mal_uint64 mal_resampler_get_expected_output_frame_count__sinc(mal_resampler* pResampler, mal_uint64 inputFrameCount);
/* TODO: Add this to mini_al.h */ /* TODO: Add this to mini_al.h */
#define MAL_ALIGN_INT(val, alignment) (((val) + ((alignment-1))) & ~((alignment)-1)) #define MAL_ALIGN_INT(val, alignment) (((val) + ((alignment)-1)) & ~((alignment)-1))
#define MAL_ALIGN_PTR(ptr, alignment) (void*)MAL_ALIGN_INT(((mal_uintptr)(ptr)), (alignment)) #define MAL_ALIGN_PTR(ptr, alignment) (void*)MAL_ALIGN_INT(((mal_uintptr)(ptr)), (alignment))
/* /*
...@@ -276,34 +268,18 @@ mal_result mal_resampler_init(const mal_resampler_config* pConfig, mal_resampler ...@@ -276,34 +268,18 @@ mal_result mal_resampler_init(const mal_resampler_config* pConfig, mal_resampler
} }
switch (pResampler->config.algorithm) { switch (pResampler->config.algorithm) {
case mal_resample_algorithm_passthrough: case mal_resampler_algorithm_linear:
{ {
pResampler->init = NULL; pResampler->init = NULL;
pResampler->read = mal_resampler_read__passthrough; pResampler->read = mal_resampler_read__linear;
pResampler->seek = mal_resampler_seek__passthrough; pResampler->seek = mal_resampler_seek__linear;
pResampler->getCachedTime = mal_resampler_get_cached_time__passthrough;
pResampler->getRequiredInputFrameCount = mal_resampler_get_required_input_frame_count__passthrough;
pResampler->getExpectedOutputFrameCount = mal_resampler_get_expected_output_frame_count__passthrough;
} break; } break;
case mal_resample_algorithm_linear: case mal_resampler_algorithm_sinc:
{ {
pResampler->init = NULL; pResampler->init = mal_resampler_init__sinc;
pResampler->read = mal_resampler_read__linear; pResampler->read = mal_resampler_read__sinc;
pResampler->seek = mal_resampler_seek__linear; pResampler->seek = mal_resampler_seek__sinc;
pResampler->getCachedTime = mal_resampler_get_cached_time__linear;
pResampler->getRequiredInputFrameCount = mal_resampler_get_required_input_frame_count__linear;
pResampler->getExpectedOutputFrameCount = mal_resampler_get_expected_output_frame_count__linear;
} break;
case mal_resample_algorithm_sinc:
{
pResampler->init = mal_resampler_init__sinc;
pResampler->read = mal_resampler_read__sinc;
pResampler->seek = mal_resampler_seek__sinc;
pResampler->getCachedTime = mal_resampler_get_cached_time__sinc;
pResampler->getRequiredInputFrameCount = mal_resampler_get_required_input_frame_count__sinc;
pResampler->getExpectedOutputFrameCount = mal_resampler_get_expected_output_frame_count__sinc;
} break; } break;
} }
...@@ -332,9 +308,14 @@ mal_result mal_resampler_set_rate(mal_resampler* pResampler, mal_uint32 sampleRa ...@@ -332,9 +308,14 @@ mal_result mal_resampler_set_rate(mal_resampler* pResampler, mal_uint32 sampleRa
return MAL_INVALID_ARGS; return MAL_INVALID_ARGS;
} }
double ratio = (double)pResampler->config.sampleRateIn / (double)pResampler->config.sampleRateOut;
if (ratio < MAL_RESAMPLER_MIN_RATIO || ratio > MAL_RESAMPLER_MAX_RATIO) {
return MAL_INVALID_ARGS; /* Ratio is too extreme. */
}
pResampler->config.sampleRateIn = sampleRateIn; pResampler->config.sampleRateIn = sampleRateIn;
pResampler->config.sampleRateOut = sampleRateOut; pResampler->config.sampleRateOut = sampleRateOut;
pResampler->config.ratio = (double)pResampler->config.sampleRateIn / (double)pResampler->config.sampleRateOut; pResampler->config.ratio = ratio;
return MAL_SUCCESS; return MAL_SUCCESS;
} }
...@@ -345,8 +326,8 @@ mal_result mal_resampler_set_rate_ratio(mal_resampler* pResampler, double ratio) ...@@ -345,8 +326,8 @@ mal_result mal_resampler_set_rate_ratio(mal_resampler* pResampler, double ratio)
return MAL_INVALID_ARGS; return MAL_INVALID_ARGS;
} }
if (ratio == 0) { if (ratio < MAL_RESAMPLER_MIN_RATIO || ratio > MAL_RESAMPLER_MAX_RATIO) {
return MAL_INVALID_ARGS; return MAL_INVALID_ARGS; /* Ratio is too extreme. */
} }
pResampler->config.ratio = ratio; pResampler->config.ratio = ratio;
...@@ -369,6 +350,7 @@ mal_uint64 mal_resampler_read(mal_resampler* pResampler, mal_uint64 frameCount, ...@@ -369,6 +350,7 @@ mal_uint64 mal_resampler_read(mal_resampler* pResampler, mal_uint64 frameCount,
return mal_resampler_seek(pResampler, frameCount, 0); return mal_resampler_seek(pResampler, frameCount, 0);
} }
return pResampler->read(pResampler, frameCount, ppFrames); return pResampler->read(pResampler, frameCount, ppFrames);
} }
...@@ -382,6 +364,7 @@ mal_uint64 mal_resampler_seek(mal_resampler* pResampler, mal_uint64 frameCount, ...@@ -382,6 +364,7 @@ mal_uint64 mal_resampler_seek(mal_resampler* pResampler, mal_uint64 frameCount,
return 0; /* Nothing to do, so return early. */ return 0; /* Nothing to do, so return early. */
} }
return pResampler->seek(pResampler, frameCount, options); return pResampler->seek(pResampler, frameCount, options);
} }
...@@ -396,43 +379,50 @@ mal_uint64 mal_resampler_get_cached_output_frame_count(mal_resampler* pResampler ...@@ -396,43 +379,50 @@ mal_uint64 mal_resampler_get_cached_output_frame_count(mal_resampler* pResampler
return (mal_uint64)floor(mal_resampler_get_cached_output_time(pResampler)); return (mal_uint64)floor(mal_resampler_get_cached_output_time(pResampler));
} }
double mal_resampler__calculate_cached_input_time(mal_resampler* pResampler)
{
/*
The cached input time depends on whether or not the end of the input is being consumed. If so, it's the difference between the
last cached frame and the halfway point of the window, rounded down. Otherwise it's between the last cached frame and the end
of the window.
*/
double cachedInputTime = pResampler->cacheLengthInFrames;
if (pResampler->config.endOfInputMode == mal_resampler_end_of_input_mode_consume) {
cachedInputTime -= (pResampler->windowTime + (pResampler->windowLength >> 1));
} else {
cachedInputTime -= (pResampler->windowTime + pResampler->windowLength);
}
return cachedInputTime;
}
double mal_resampler_get_cached_input_time(mal_resampler* pResampler) double mal_resampler_get_cached_input_time(mal_resampler* pResampler)
{ {
if (pResampler == NULL || pResampler->getCachedTime == NULL) { if (pResampler == NULL) {
return 0; /* Invalid args. */ return 0; /* Invalid args. */
} }
double inputTime = 0; return mal_resampler__calculate_cached_input_time(pResampler);
double outputTime = 0; }
mal_result result = pResampler->getCachedTime(pResampler, &inputTime, &outputTime);
if (result != MAL_SUCCESS) {
return 0;
}
return inputTime; double mal_resampler__calculate_cached_output_time(mal_resampler* pResampler)
{
return mal_resampler__calculate_cached_input_time(pResampler) / pResampler->config.ratio;
} }
double mal_resampler_get_cached_output_time(mal_resampler* pResampler) double mal_resampler_get_cached_output_time(mal_resampler* pResampler)
{ {
if (pResampler == NULL || pResampler->getCachedTime == NULL) { if (pResampler == NULL) {
return 0; /* Invalid args. */ return 0; /* Invalid args. */
} }
double inputTime = 0; return mal_resampler__calculate_cached_output_time(pResampler);
double outputTime = 0;
mal_result result = pResampler->getCachedTime(pResampler, &inputTime, &outputTime);
if (result != MAL_SUCCESS) {
return 0;
}
return outputTime;
} }
mal_uint64 mal_resampler_get_required_input_frame_count(mal_resampler* pResampler, mal_uint64 outputFrameCount) mal_uint64 mal_resampler_get_required_input_frame_count(mal_resampler* pResampler, mal_uint64 outputFrameCount)
{ {
if (pResampler == NULL || pResampler->getRequiredInputFrameCount == NULL) { if (pResampler == NULL) {
return 0; /* Invalid args. */ return 0; /* Invalid args. */
} }
...@@ -440,145 +430,42 @@ mal_uint64 mal_resampler_get_required_input_frame_count(mal_resampler* pResample ...@@ -440,145 +430,42 @@ mal_uint64 mal_resampler_get_required_input_frame_count(mal_resampler* pResample
return 0; return 0;
} }
return pResampler->getRequiredInputFrameCount(pResampler, outputFrameCount); /* First grab the amount of output time sitting in the cache. */
} double cachedOutputTime = mal_resampler__calculate_cached_output_time(pResampler);
if (cachedOutputTime >= outputFrameCount) {
mal_uint64 mal_resampler_get_expected_output_frame_count(mal_resampler* pResampler, mal_uint64 inputFrameCount) return 0; /* All of the necessary input data is cached. No additional data is required from the client. */
{
if (pResampler == NULL || pResampler->getExpectedOutputFrameCount == NULL) {
return 0; /* Invalid args. */
}
if (inputFrameCount == 0) {
return 0;
} }
return pResampler->getExpectedOutputFrameCount(pResampler, inputFrameCount);
}
/*
Passthrough
*/
mal_uint64 mal_resampler_read__passthrough(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames)
{
mal_assert(pResampler != NULL);
mal_assert(pResampler->config.onRead != NULL);
mal_assert(frameCount > 0);
mal_assert(ppFrames != NULL);
/* /*
It's tempting to to just call pResampler->config.onRead() and pass in ppFrames directly, however this violates Getting here means more input data will be required. A detail to consider here is that we are accepting an unsigned 64-bit integer
our requirement that all buffers passed into onRead() are aligned to MAL_SIMD_ALIGNMENT. If any of the ppFrames for the output frame count, however we need to consider sub-frame timing which we're doing by using a double. There will not be
buffers are misaligned we need to read into a temporary buffer. enough precision in the double to represent the whole 64-bit range of the input variable. For now I'm not handling this explicitly
because I think it's unlikely outputFrameCount will be set to something so huge anyway, but it will be something to think about in
order to get this working properly for the whole 64-bit range.
The return value must always be larger than 0 after this point. If it's not we have an error.
*/ */
mal_bool32 isOutputBufferAligned = MAL_TRUE; double nonCachedOutputTime = outputFrameCount - cachedOutputTime;
for (mal_uint32 iChannel = 0; iChannel < pResampler->config.channels; ++iChannel) { mal_assert(nonCachedOutputTime > 0);
if (((mal_uintptr)ppFrames[iChannel] & (MAL_SIMD_ALIGNMENT-1)) != 0) {
isOutputBufferAligned = MAL_FALSE;
break;
}
}
if (frameCount <= 0xFFFFFFFF && isOutputBufferAligned) { mal_uint64 requiredInputFrames = (mal_uint64)ceil(nonCachedOutputTime * pResampler->config.ratio);
return pResampler->config.onRead(pResampler, (mal_uint32)frameCount, ppFrames); /* Fast path. */ mal_assert(requiredInputFrames > 0);
} else {
MAL_DECLARE_ALIGNED_STACK_BUFFER(float, ppRunningFrames, 4096, pResampler->config.channels);
mal_uint64 totalFramesRead = 0;
while (frameCount > 0) {
mal_uint64 framesToReadNow = (pResampler->config.format == mal_format_f32) ? ppRunningFramesFrameCount : ppRunningFramesFrameCount*2; /* x2 for the s16 frame count because ppRunningFramesFrameCount is based on f32. */
if (framesToReadNow > frameCount) {
framesToReadNow = frameCount;
}
mal_uint32 framesJustRead = pResampler->config.onRead(pResampler, (mal_uint32)framesToReadNow, (void**)ppRunningFrames);
if (framesJustRead == 0) {
break;
}
totalFramesRead += framesJustRead;
frameCount -= framesJustRead;
mal_uint32 bytesJustRead = framesJustRead * mal_get_bytes_per_sample(pResampler->config.format);
for (mal_uint32 iChannel = 0; iChannel < pResampler->config.channels; ++iChannel) {
mal_copy_memory(ppFrames[iChannel], ppRunningFrames[iChannel], bytesJustRead);
ppFrames[iChannel] = mal_offset_ptr(ppFrames[iChannel], bytesJustRead);
}
if (framesJustRead < framesToReadNow) {
break;
}
}
return totalFramesRead; return requiredInputFrames;
}
} }
mal_uint64 mal_resampler_seek__passthrough(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options) mal_uint64 mal_resampler_get_expected_output_frame_count(mal_resampler* pResampler, mal_uint64 inputFrameCount)
{ {
mal_assert(pResampler != NULL); if (pResampler == NULL) {
mal_assert(pResampler->config.onRead != NULL); return 0; /* Invalid args. */
mal_assert(frameCount > 0);
if ((options & MAL_RESAMPLER_SEEK_NO_CLIENT_READ) != 0) {
return frameCount; /* No input from onRead(), so just return immediately. */
} }
/* Getting here means we need to read from onRead(). In this case we just read into a trash buffer. */ if (inputFrameCount == 0) {
MAL_DECLARE_ALIGNED_STACK_BUFFER(float, trash, 4096, pResampler->config.channels); return 0;
mal_uint64 totalFramesRead = 0;
while (frameCount > 0) {
mal_uint64 framesToRead = trashFrameCount;
if (framesToRead > frameCount) {
framesToRead = frameCount;
}
mal_uint64 framesRead = pResampler->config.onRead(pResampler, (mal_uint32)framesToRead, (void**)trash);
totalFramesRead += framesRead;
frameCount -= framesRead;
/* Don't get stuck in a loop if the client returns no samples. */
if (framesRead < framesToRead) {
break;
}
} }
return totalFramesRead; /* What we're actually calculating here is how many whole output frames will be calculated after consuming inputFrameCount + mal_resampler_get_cached_input_time(). */
} return (mal_uint64)floor((mal_resampler__calculate_cached_input_time(pResampler) + inputFrameCount) / pResampler->config.ratio);
mal_result mal_resampler_get_cached_time__passthrough(mal_resampler* pResampler, double* pInputTime, double* pOutputTime)
{
mal_assert(pResampler != NULL);
mal_assert(pInputTime != NULL);
mal_assert(pOutputTime != NULL);
/* The passthrough implementation never caches, so this is always 0. */
*pInputTime = 0;
*pOutputTime = 0;
return MAL_SUCCESS;
}
mal_uint64 mal_resampler_get_required_input_frame_count__passthrough(mal_resampler* pResampler, mal_uint64 outputFrameCount)
{
mal_assert(pResampler != NULL);
mal_assert(outputFrameCount > 0);
/* For passthrough input and output is the same. */
(void)pResampler;
return outputFrameCount;
}
mal_uint64 mal_resampler_get_expected_output_frame_count__passthrough(mal_resampler* pResampler, mal_uint64 inputFrameCount)
{
mal_assert(pResampler != NULL);
mal_assert(inputFrameCount > 0);
/* For passthrough input and output is the same. */
(void)pResampler;
return inputFrameCount;
} }
...@@ -612,36 +499,6 @@ mal_uint64 mal_resampler_seek__linear(mal_resampler* pResampler, mal_uint64 fram ...@@ -612,36 +499,6 @@ mal_uint64 mal_resampler_seek__linear(mal_resampler* pResampler, mal_uint64 fram
return 0; return 0;
} }
mal_result mal_resampler_get_cached_time__linear(mal_resampler* pResampler, double* pInputTime, double* pOutputTime)
{
mal_assert(pResampler != NULL);
mal_assert(pInputTime != NULL);
mal_assert(pOutputTime != NULL);
/* TODO: Implement me. */
return MAL_ERROR;
}
mal_uint64 mal_resampler_get_required_input_frame_count__linear(mal_resampler* pResampler, mal_uint64 outputFrameCount)
{
mal_assert(pResampler != NULL);
mal_assert(outputFrameCount > 0);
/* TODO: Implement me. */
(void)pResampler;
return 0;
}
mal_uint64 mal_resampler_get_expected_output_frame_count__linear(mal_resampler* pResampler, mal_uint64 inputFrameCount)
{
mal_assert(pResampler != NULL);
mal_assert(inputFrameCount > 0);
/* TODO: Implement me. */
(void)pResampler;
return 0;
}
/* /*
Sinc Sinc
...@@ -681,34 +538,4 @@ mal_uint64 mal_resampler_seek__sinc(mal_resampler* pResampler, mal_uint64 frameC ...@@ -681,34 +538,4 @@ mal_uint64 mal_resampler_seek__sinc(mal_resampler* pResampler, mal_uint64 frameC
return 0; return 0;
} }
mal_result mal_resampler_get_cached_time__sinc(mal_resampler* pResampler, double* pInputTime, double* pOutputTime)
{
mal_assert(pResampler != NULL);
mal_assert(pInputTime != NULL);
mal_assert(pOutputTime != NULL);
/* TODO: Implement me. */
return MAL_ERROR;
}
mal_uint64 mal_resampler_get_required_input_frame_count__sinc(mal_resampler* pResampler, mal_uint64 outputFrameCount)
{
mal_assert(pResampler != NULL);
mal_assert(outputFrameCount > 0);
/* TODO: Implement me. */
(void)pResampler;
return 0;
}
mal_uint64 mal_resampler_get_expected_output_frame_count__sinc(mal_resampler* pResampler, mal_uint64 inputFrameCount)
{
mal_assert(pResampler != NULL);
mal_assert(inputFrameCount > 0);
/* TODO: Implement me. */
(void)pResampler;
return 0;
}
#endif #endif
...@@ -322,6 +322,7 @@ ...@@ -322,6 +322,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\mini_al.h" /> <ClInclude Include="..\mini_al.h" />
<ClInclude Include="..\research\mal_resampler.h" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
......
...@@ -44,5 +44,8 @@ ...@@ -44,5 +44,8 @@
<ClInclude Include="..\mini_al.h"> <ClInclude Include="..\mini_al.h">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\research\mal_resampler.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment