Commit 25618ab3 authored by David Reid's avatar David Reid

Add support for initializing a device without a context.

This commit also changes mal_src_read_frames and mal_dsp_read_frames so
that they take and return 64-bit frame counts.
parent c47c416d
...@@ -77,21 +77,15 @@ ...@@ -77,21 +77,15 @@
// // then return the number of frames you wrote. // // then return the number of frames you wrote.
// // // //
// // The user data (pDevice->pUserData) is set by mal_device_init(). // // The user data (pDevice->pUserData) is set by mal_device_init().
// return (mal_uint32)drwav_read_f32((drwav*)pDevice->pUserData, frameCount * pDevice->channels, (float*)pSamples) / pDevice->channels; // return (mal_uint32)mal_decoder_read((mal_decoder*)pDevice->pUserData, frameCount, pSamples);
// } // }
// //
// ... // ...
// //
// mal_context context; // mal_device_config config = mal_device_config_init_playback(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, on_send_frames_to_device);
// if (mal_context_init(NULL, 0, NULL, &context) != MAL_SUCCESS) {
// printf("Failed to initialize context.");
// return -3;
// }
//
// mal_device_config config = mal_device_config_init_playback(mal_format_s16, wav.channels, wav.sampleRate, on_send_frames_to_device);
// //
// mal_device device; // mal_device device;
// mal_result result = mal_device_init(&context, mal_device_type_playback, NULL, &config, pMyData, &device); // mal_result result = mal_device_init(NULL, mal_device_type_playback, NULL, &config, &decoder /*pUserData*/, &device);
// if (result != MAL_SUCCESS) { // if (result != MAL_SUCCESS) {
// return -1; // return -1;
// } // }
...@@ -967,6 +961,7 @@ struct mal_device ...@@ -967,6 +961,7 @@ struct mal_device
mal_bool32 usingDefaultBufferSize : 1; mal_bool32 usingDefaultBufferSize : 1;
mal_bool32 usingDefaultPeriods : 1; mal_bool32 usingDefaultPeriods : 1;
mal_bool32 exclusiveMode : 1; mal_bool32 exclusiveMode : 1;
mal_bool32 isOwnerOfContext : 1; // When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into mal_device_init().
mal_format internalFormat; mal_format internalFormat;
mal_uint32 internalChannels; mal_uint32 internalChannels;
mal_uint32 internalSampleRate; mal_uint32 internalSampleRate;
...@@ -1155,6 +1150,13 @@ mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, ma ...@@ -1155,6 +1150,13 @@ mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, ma
// Initializes a device. // Initializes a device.
// //
// The context can be null in which case it uses the default. This is equivalent to passing in a
// context that was initialized like so:
//
// mal_context_init(NULL, 0, NULL, &context);
//
// Do not pass in null for the context if you are needing to open multiple devices.
//
// The device ID (pDeviceID) can be null, in which case the default device is used. Otherwise, you // The device ID (pDeviceID) can be null, in which case the default device is used. Otherwise, you
// can retrieve the ID by calling mal_enumerate_devices() and using the ID from the returned data. // can retrieve the ID by calling mal_enumerate_devices() and using the ID from the returned data.
// Set pDeviceID to NULL to use the default device. Do _not_ rely on the first device ID returned // Set pDeviceID to NULL to use the default device. Do _not_ rely on the first device ID returned
...@@ -1192,6 +1194,12 @@ mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, ma ...@@ -1192,6 +1194,12 @@ mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, ma
// This is just slow due to the nature of it being an initialization API. // This is just slow due to the nature of it being an initialization API.
mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, void* pUserData, mal_device* pDevice); mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, void* pUserData, mal_device* pDevice);
// Initializes a device without a context, with extra parameters for controlling the configuration
// of the internal self-managed context.
//
// See mal_device_init().
mal_result mal_device_init_ex(mal_backend backends[], mal_uint32 backendCount, const mal_context_config* pContextConfig, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, void* pUserData, mal_device* pDevice);
// Uninitializes a device. // Uninitializes a device.
// //
// This will explicitly stop the device. You do not need to call mal_device_stop() beforehand, but it's // This will explicitly stop the device. You do not need to call mal_device_stop() beforehand, but it's
...@@ -1428,7 +1436,7 @@ mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOu ...@@ -1428,7 +1436,7 @@ mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOu
// Reads a number of frames. // Reads a number of frames.
// //
// Returns the number of frames actually read. // Returns the number of frames actually read.
mal_uint32 mal_src_read_frames(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut); mal_uint64 mal_src_read_frames(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut);
// The same mal_src_read_frames() with extra control over whether or not the internal buffers should be flushed at the end. // The same mal_src_read_frames() with extra control over whether or not the internal buffers should be flushed at the end.
// //
...@@ -1436,7 +1444,7 @@ mal_uint32 mal_src_read_frames(mal_src* pSRC, mal_uint32 frameCount, void* pFram ...@@ -1436,7 +1444,7 @@ mal_uint32 mal_src_read_frames(mal_src* pSRC, mal_uint32 frameCount, void* pFram
// version of this function does _not_ flush this buffer because otherwise it causes glitches for streaming based conversion // version of this function does _not_ flush this buffer because otherwise it causes glitches for streaming based conversion
// pipelines. The problem, however, is that sometimes you need those last few samples (such as if you're doing a bulk conversion // pipelines. The problem, however, is that sometimes you need those last few samples (such as if you're doing a bulk conversion
// of a static file). Enabling flushing will fix this for you. // of a static file). Enabling flushing will fix this for you.
mal_uint32 mal_src_read_frames_ex(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); mal_uint64 mal_src_read_frames_ex(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush);
...@@ -1459,12 +1467,12 @@ mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOu ...@@ -1459,12 +1467,12 @@ mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOu
// //
// This this _not_ flush the internal buffers which means you may end up with a few less frames than you may expect. Look at // This this _not_ flush the internal buffers which means you may end up with a few less frames than you may expect. Look at
// mal_dsp_read_frames_ex() if you want to flush the buffers at the end of the read. // mal_dsp_read_frames_ex() if you want to flush the buffers at the end of the read.
mal_uint32 mal_dsp_read_frames(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut); mal_uint64 mal_dsp_read_frames(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut);
// The same mal_dsp_read_frames() with extra control over whether or not the internal buffers should be flushed at the end. // The same mal_dsp_read_frames() with extra control over whether or not the internal buffers should be flushed at the end.
// //
// See documentation for mal_src_read_frames_ex() for an explanation on flushing. // See documentation for mal_src_read_frames_ex() for an explanation on flushing.
mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); mal_uint64 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush);
// High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to // High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to
// determine the required size of the output buffer. // determine the required size of the output buffer.
...@@ -1472,7 +1480,7 @@ mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pF ...@@ -1472,7 +1480,7 @@ mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pF
// A return value of 0 indicates an error. // A return value of 0 indicates an error.
// //
// This function is useful for one-off bulk conversions, but if you're streaming data you should use the DSP APIs instead. // This function is useful for one-off bulk conversions, but if you're streaming data you should use the DSP APIs instead.
mal_uint32 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint32 frameCountIn); mal_uint64 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint64 frameCountIn);
// Helper for initializing a mal_dsp_config object. // Helper for initializing a mal_dsp_config object.
mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut); mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut);
...@@ -2745,7 +2753,7 @@ static inline mal_uint32 mal_device__read_frames_from_client(mal_device* pDevice ...@@ -2745,7 +2753,7 @@ static inline mal_uint32 mal_device__read_frames_from_client(mal_device* pDevice
mal_assert(frameCount > 0); mal_assert(frameCount > 0);
mal_assert(pSamples != NULL); mal_assert(pSamples != NULL);
mal_uint32 framesRead = mal_dsp_read_frames(&pDevice->dsp, frameCount, pSamples); mal_uint32 framesRead = (mal_uint32)mal_dsp_read_frames(&pDevice->dsp, frameCount, pSamples);
mal_uint32 samplesRead = framesRead * pDevice->internalChannels; mal_uint32 samplesRead = framesRead * pDevice->internalChannels;
mal_uint32 sampleSize = mal_get_sample_size_in_bytes(pDevice->internalFormat); mal_uint32 sampleSize = mal_get_sample_size_in_bytes(pDevice->internalFormat);
mal_uint32 consumedBytes = samplesRead*sampleSize; mal_uint32 consumedBytes = samplesRead*sampleSize;
...@@ -2771,7 +2779,7 @@ static inline void mal_device__send_frames_to_client(mal_device* pDevice, mal_ui ...@@ -2771,7 +2779,7 @@ static inline void mal_device__send_frames_to_client(mal_device* pDevice, mal_ui
mal_uint32 chunkFrameCount = sizeof(chunkBuffer) / mal_get_sample_size_in_bytes(pDevice->format) / pDevice->channels; mal_uint32 chunkFrameCount = sizeof(chunkBuffer) / mal_get_sample_size_in_bytes(pDevice->format) / pDevice->channels;
for (;;) { for (;;) {
mal_uint32 framesJustRead = mal_dsp_read_frames(&pDevice->dsp, chunkFrameCount, chunkBuffer); mal_uint32 framesJustRead = (mal_uint32)mal_dsp_read_frames(&pDevice->dsp, chunkFrameCount, chunkBuffer);
if (framesJustRead == 0) { if (framesJustRead == 0) {
break; break;
} }
...@@ -9735,6 +9743,11 @@ mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, ma ...@@ -9735,6 +9743,11 @@ mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, ma
mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, void* pUserData, mal_device* pDevice) mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, void* pUserData, mal_device* pDevice)
{ {
if (pContext == NULL) {
return mal_device_init_ex(NULL, 0, NULL, type, pDeviceID, pConfig, pUserData, pDevice);
}
if (pDevice == NULL) { if (pDevice == NULL) {
return mal_post_error(pDevice, "mal_device_init() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS); return mal_post_error(pDevice, "mal_device_init() called with invalid arguments (pDevice == NULL).", MAL_INVALID_ARGS);
} }
...@@ -9742,6 +9755,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi ...@@ -9742,6 +9755,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
return mal_post_error(pDevice, "mal_device_init() called with invalid arguments (pConfig == NULL).", MAL_INVALID_ARGS); return mal_post_error(pDevice, "mal_device_init() called with invalid arguments (pConfig == NULL).", MAL_INVALID_ARGS);
} }
// Make a copy of the config to ensure we don't override the caller's object. // Make a copy of the config to ensure we don't override the caller's object.
mal_device_config config = *pConfig; mal_device_config config = *pConfig;
...@@ -9761,11 +9775,6 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi ...@@ -9761,11 +9775,6 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
} }
if (pContext == NULL) {
return mal_post_error(pDevice, "mal_device_init() called with invalid arguments (pContext == NULL).", MAL_INVALID_ARGS);
}
// Basic config validation. // Basic config validation.
if (config.channels == 0) { if (config.channels == 0) {
return mal_post_error(pDevice, "mal_device_init() called with an invalid config. Channel count must be greater than 0.", MAL_INVALID_DEVICE_CONFIG); return mal_post_error(pDevice, "mal_device_init() called with an invalid config. Channel count must be greater than 0.", MAL_INVALID_DEVICE_CONFIG);
...@@ -9967,6 +9976,30 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi ...@@ -9967,6 +9976,30 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
return MAL_SUCCESS; return MAL_SUCCESS;
} }
mal_result mal_device_init_ex(mal_backend backends[], mal_uint32 backendCount, const mal_context_config* pContextConfig, mal_device_type type, mal_device_id* pDeviceID, const mal_device_config* pConfig, void* pUserData, mal_device* pDevice)
{
mal_context* pContext = (mal_context*)mal_malloc(sizeof(*pContext));
if (pContext == NULL) {
return MAL_OUT_OF_MEMORY;
}
mal_result result = mal_context_init(backends, backendCount, pContextConfig, pContext);
if (result != MAL_SUCCESS) {
mal_free(pContext);
return result;
}
result = mal_device_init(pContext, type, pDeviceID, pConfig, pUserData, pDevice);
if (result != MAL_SUCCESS) {
mal_context_uninit(pContext);
mal_free(pContext);
return result;
}
pDevice->isOwnerOfContext = MAL_TRUE;
return result;
}
void mal_device_uninit(mal_device* pDevice) void mal_device_uninit(mal_device* pDevice)
{ {
if (!mal_device__is_initialized(pDevice)) return; if (!mal_device__is_initialized(pDevice)) return;
...@@ -10039,6 +10072,12 @@ void mal_device_uninit(mal_device* pDevice) ...@@ -10039,6 +10072,12 @@ void mal_device_uninit(mal_device* pDevice)
} }
#endif #endif
if (pDevice->isOwnerOfContext) {
mal_context_uninit(pDevice->pContext);
mal_free(pDevice->pContext);
}
mal_zero_object(pDevice); mal_zero_object(pDevice);
} }
...@@ -10379,8 +10418,8 @@ mal_uint32 mal_src_cache_read_frames(mal_src_cache* pCache, mal_uint32 frameCoun ...@@ -10379,8 +10418,8 @@ mal_uint32 mal_src_cache_read_frames(mal_src_cache* pCache, mal_uint32 frameCoun
} }
mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); mal_uint64 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush);
mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush); mal_uint64 mal_src_read_frames_linear(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush);
mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* pUserData, mal_src* pSRC) mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* pUserData, mal_src* pSRC)
{ {
...@@ -10428,12 +10467,12 @@ mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOu ...@@ -10428,12 +10467,12 @@ mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOu
return MAL_SUCCESS; return MAL_SUCCESS;
} }
mal_uint32 mal_src_read_frames(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut) mal_uint64 mal_src_read_frames(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut)
{ {
return mal_src_read_frames_ex(pSRC, frameCount, pFramesOut, MAL_FALSE); return mal_src_read_frames_ex(pSRC, frameCount, pFramesOut, MAL_FALSE);
} }
mal_uint32 mal_src_read_frames_ex(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) mal_uint64 mal_src_read_frames_ex(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush)
{ {
if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) return 0; if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) return 0;
...@@ -10453,7 +10492,7 @@ mal_uint32 mal_src_read_frames_ex(mal_src* pSRC, mal_uint32 frameCount, void* pF ...@@ -10453,7 +10492,7 @@ mal_uint32 mal_src_read_frames_ex(mal_src* pSRC, mal_uint32 frameCount, void* pF
} }
} }
mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) mal_uint64 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush)
{ {
mal_assert(pSRC != NULL); mal_assert(pSRC != NULL);
mal_assert(frameCount > 0); mal_assert(frameCount > 0);
...@@ -10463,17 +10502,38 @@ mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, ...@@ -10463,17 +10502,38 @@ mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount,
// Fast path. No need for data conversion - just pass right through. // Fast path. No need for data conversion - just pass right through.
if (pSRC->config.formatIn == pSRC->config.formatOut) { if (pSRC->config.formatIn == pSRC->config.formatOut) {
return pSRC->onRead(pSRC, frameCount, pFramesOut, pSRC->pUserData); if (frameCount <= UINT32_MAX) {
return pSRC->onRead(pSRC, (mal_uint32)frameCount, pFramesOut, pSRC->pUserData);
} else {
mal_uint64 totalFramesRead = 0;
while (frameCount > 0) {
mal_uint32 framesToReadRightNow = UINT32_MAX;
if (framesToReadRightNow > frameCount) {
framesToReadRightNow = (mal_uint32)frameCount;
}
mal_uint32 framesRead = pSRC->onRead(pSRC, framesToReadRightNow, pFramesOut, pSRC->pUserData);
if (framesRead == 0) {
break;
}
pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pSRC->config.channels * mal_get_sample_size_in_bytes(pSRC->config.formatOut));
frameCount -= framesRead;
totalFramesRead += framesRead;
}
return totalFramesRead;
}
} }
// Slower path. Need to do a format conversion. // Slower path. Need to do a format conversion.
mal_uint32 totalFramesRead = 0; mal_uint64 totalFramesRead = 0;
while (frameCount > 0) { while (frameCount > 0) {
mal_uint8 pStagingBuffer[MAL_MAX_CHANNELS * 2048]; mal_uint8 pStagingBuffer[MAL_MAX_CHANNELS * 2048];
mal_uint32 stagingBufferSizeInFrames = sizeof(pStagingBuffer) / mal_get_sample_size_in_bytes(pSRC->config.formatIn) / pSRC->config.channels; mal_uint32 stagingBufferSizeInFrames = sizeof(pStagingBuffer) / mal_get_sample_size_in_bytes(pSRC->config.formatIn) / pSRC->config.channels;
mal_uint32 framesToRead = stagingBufferSizeInFrames; mal_uint32 framesToRead = stagingBufferSizeInFrames;
if (framesToRead > frameCount) { if (framesToRead > frameCount) {
framesToRead = frameCount; framesToRead = (mal_uint32)frameCount;
} }
mal_uint32 framesRead = pSRC->onRead(pSRC, framesToRead, pStagingBuffer, pSRC->pUserData); mal_uint32 framesRead = pSRC->onRead(pSRC, framesToRead, pStagingBuffer, pSRC->pUserData);
...@@ -10491,7 +10551,7 @@ mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount, ...@@ -10491,7 +10551,7 @@ mal_uint32 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint32 frameCount,
return totalFramesRead; return totalFramesRead;
} }
mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) mal_uint64 mal_src_read_frames_linear(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush)
{ {
mal_assert(pSRC != NULL); mal_assert(pSRC != NULL);
mal_assert(frameCount > 0); mal_assert(frameCount > 0);
...@@ -10517,7 +10577,7 @@ mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void ...@@ -10517,7 +10577,7 @@ mal_uint32 mal_src_read_frames_linear(mal_src* pSRC, mal_uint32 frameCount, void
float factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut; float factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut;
mal_uint32 totalFramesRead = 0; mal_uint64 totalFramesRead = 0;
while (frameCount > 0) { while (frameCount > 0) {
// The bin is where the previous and next frames are located. // The bin is where the previous and next frames are located.
float* pPrevFrame = pSRC->bin; float* pPrevFrame = pSRC->bin;
...@@ -11153,18 +11213,39 @@ mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOu ...@@ -11153,18 +11213,39 @@ mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOu
return MAL_SUCCESS; return MAL_SUCCESS;
} }
mal_uint32 mal_dsp_read_frames(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut) mal_uint64 mal_dsp_read_frames(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut)
{ {
return mal_dsp_read_frames_ex(pDSP, frameCount, pFramesOut, MAL_FALSE); return mal_dsp_read_frames_ex(pDSP, frameCount, pFramesOut, MAL_FALSE);
} }
mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, mal_bool32 flush) mal_uint64 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush)
{ {
if (pDSP == NULL || pFramesOut == NULL) return 0; if (pDSP == NULL || pFramesOut == NULL) return 0;
// Fast path. // Fast path.
if (pDSP->isPassthrough) { if (pDSP->isPassthrough) {
return pDSP->onRead(pDSP, frameCount, pFramesOut, pDSP->pUserDataForOnRead); if (frameCount <= UINT32_MAX) {
return (mal_uint32)pDSP->onRead(pDSP, (mal_uint32)frameCount, pFramesOut, pDSP->pUserDataForOnRead);
} else {
mal_uint64 totalFramesRead = 0;
while (frameCount > 0) {
mal_uint32 framesToReadRightNow = UINT32_MAX;
if (framesToReadRightNow > frameCount) {
framesToReadRightNow = (mal_uint32)frameCount;
}
mal_uint32 framesRead = pDSP->onRead(pDSP, framesToReadRightNow, pFramesOut, pDSP->pUserDataForOnRead);
if (framesRead == 0) {
break;
}
pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pDSP->config.channelsOut * mal_get_sample_size_in_bytes(pDSP->config.formatOut));
frameCount -= framesRead;
totalFramesRead += framesRead;
}
return totalFramesRead;
}
} }
...@@ -11173,19 +11254,19 @@ mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pF ...@@ -11173,19 +11254,19 @@ mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pF
mal_format pFramesFormat[2]; mal_format pFramesFormat[2];
mal_uint32 iFrames = 0; // <-- Used as an index into pFrames and cycles between 0 and 1. mal_uint32 iFrames = 0; // <-- Used as an index into pFrames and cycles between 0 and 1.
mal_uint32 totalFramesRead = 0; mal_uint64 totalFramesRead = 0;
while (frameCount > 0) { while (frameCount > 0) {
iFrames = 0; iFrames = 0;
mal_uint32 framesToRead = mal_countof(pFrames[0]) / (mal_max(pDSP->config.channelsIn, pDSP->config.channelsOut) * MAL_MAX_SAMPLE_SIZE_IN_BYTES); mal_uint32 framesToRead = mal_countof(pFrames[0]) / (mal_max(pDSP->config.channelsIn, pDSP->config.channelsOut) * MAL_MAX_SAMPLE_SIZE_IN_BYTES);
if (framesToRead > frameCount) { if (framesToRead > frameCount) {
framesToRead = frameCount; framesToRead = (mal_uint32)frameCount;
} }
// The initial filling of sample data depends on whether or not we are using SRC. // The initial filling of sample data depends on whether or not we are using SRC.
mal_uint32 framesRead = 0; mal_uint32 framesRead = 0;
if (pDSP->isSRCRequired) { if (pDSP->isSRCRequired) {
framesRead = mal_src_read_frames_ex(&pDSP->src, framesToRead, pFrames[iFrames], flush); framesRead = (mal_uint32)mal_src_read_frames_ex(&pDSP->src, framesToRead, pFrames[iFrames], flush);
pFramesFormat[iFrames] = pDSP->src.config.formatOut; // Should always be f32. pFramesFormat[iFrames] = pDSP->src.config.formatOut; // Should always be f32.
} else { } else {
framesRead = pDSP->onRead(pDSP, framesToRead, pFrames[iFrames], pDSP->pUserDataForOnRead); framesRead = pDSP->onRead(pDSP, framesToRead, pFrames[iFrames], pDSP->pUserDataForOnRead);
...@@ -11231,12 +11312,12 @@ mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pF ...@@ -11231,12 +11312,12 @@ mal_uint32 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint32 frameCount, void* pF
} }
mal_uint32 mal_calculate_frame_count_after_src(mal_uint32 sampleRateOut, mal_uint32 sampleRateIn, mal_uint32 frameCountIn) mal_uint64 mal_calculate_frame_count_after_src(mal_uint32 sampleRateOut, mal_uint32 sampleRateIn, mal_uint64 frameCountIn)
{ {
double srcRatio = (double)sampleRateOut / sampleRateIn; double srcRatio = (double)sampleRateOut / sampleRateIn;
double frameCountOutF = frameCountIn * srcRatio; double frameCountOutF = frameCountIn * srcRatio;
mal_uint32 frameCountOut = (mal_uint32)frameCountOutF; mal_uint64 frameCountOut = (mal_uint64)frameCountOutF;
// If the output frame count is fractional, make sure we add an extra frame to ensure there's enough room for that last sample. // If the output frame count is fractional, make sure we add an extra frame to ensure there's enough room for that last sample.
if ((frameCountOutF - frameCountOut) > 0.0) { if ((frameCountOutF - frameCountOut) > 0.0) {
...@@ -11251,8 +11332,8 @@ typedef struct ...@@ -11251,8 +11332,8 @@ typedef struct
const void* pDataIn; const void* pDataIn;
mal_format formatIn; mal_format formatIn;
mal_uint32 channelsIn; mal_uint32 channelsIn;
mal_uint32 totalFrameCount; mal_uint64 totalFrameCount;
mal_uint32 iNextFrame; mal_uint64 iNextFrame;
} mal_convert_frames__data; } mal_convert_frames__data;
mal_uint32 mal_convert_frames__on_read(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) mal_uint32 mal_convert_frames__on_read(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData)
...@@ -11264,9 +11345,9 @@ mal_uint32 mal_convert_frames__on_read(mal_dsp* pDSP, mal_uint32 frameCount, voi ...@@ -11264,9 +11345,9 @@ mal_uint32 mal_convert_frames__on_read(mal_dsp* pDSP, mal_uint32 frameCount, voi
mal_assert(pData->totalFrameCount >= pData->iNextFrame); mal_assert(pData->totalFrameCount >= pData->iNextFrame);
mal_uint32 framesToRead = frameCount; mal_uint32 framesToRead = frameCount;
mal_uint32 framesRemaining = (pData->totalFrameCount - pData->iNextFrame); mal_uint64 framesRemaining = (pData->totalFrameCount - pData->iNextFrame);
if (framesToRead > framesRemaining) { if (framesToRead > framesRemaining) {
framesToRead = framesRemaining; framesToRead = (mal_uint32)framesRemaining;
} }
mal_uint32 frameSizeInBytes = mal_get_sample_size_in_bytes(pData->formatIn) * pData->channelsIn; mal_uint32 frameSizeInBytes = mal_get_sample_size_in_bytes(pData->formatIn) * pData->channelsIn;
...@@ -11276,13 +11357,13 @@ mal_uint32 mal_convert_frames__on_read(mal_dsp* pDSP, mal_uint32 frameCount, voi ...@@ -11276,13 +11357,13 @@ mal_uint32 mal_convert_frames__on_read(mal_dsp* pDSP, mal_uint32 frameCount, voi
return framesToRead; return framesToRead;
} }
mal_uint32 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint32 frameCountIn) mal_uint64 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint64 frameCountIn)
{ {
if (frameCountIn == 0) { if (frameCountIn == 0) {
return 0; return 0;
} }
mal_uint32 frameCountOut = mal_calculate_frame_count_after_src(sampleRateOut, sampleRateIn, frameCountIn); mal_uint64 frameCountOut = mal_calculate_frame_count_after_src(sampleRateOut, sampleRateIn, frameCountIn);
if (pOut == NULL) { if (pOut == NULL) {
return frameCountOut; return frameCountOut;
} }
...@@ -12519,27 +12600,7 @@ mal_result mal_decoder_uninit(mal_decoder* pDecoder) ...@@ -12519,27 +12600,7 @@ mal_result mal_decoder_uninit(mal_decoder* pDecoder)
mal_uint64 mal_decoder_read(mal_decoder* pDecoder, mal_uint64 frameCount, void* pFramesOut) mal_uint64 mal_decoder_read(mal_decoder* pDecoder, mal_uint64 frameCount, void* pFramesOut)
{ {
if (pDecoder == NULL) return 0; if (pDecoder == NULL) return 0;
return mal_dsp_read_frames_ex(&pDecoder->dsp, frameCount, pFramesOut, MAL_TRUE);
mal_uint64 totalFramesRead = 0;
while (frameCount > 0) {
mal_uint32 framesToRead = 0;
if (frameCount > 0xFFFFFFFF) {
framesToRead = 0xFFFFFFFF;
} else {
framesToRead = (mal_uint32)frameCount;
}
mal_uint32 framesJustRead = mal_dsp_read_frames_ex(&pDecoder->dsp, framesToRead, pFramesOut, MAL_TRUE);
if (framesJustRead == 0) {
break;
}
totalFramesRead += framesJustRead;
frameCount -= framesJustRead;
pFramesOut = (void*)mal_offset_ptr(pFramesOut, framesJustRead * mal_get_sample_size_in_bytes(pDecoder->dsp.config.formatOut)*pDecoder->dsp.config.channelsOut);
}
return totalFramesRead;
} }
mal_result mal_decoder_seek_to_frame(mal_decoder* pDecoder, mal_uint64 frameIndex) mal_result mal_decoder_seek_to_frame(mal_decoder* pDecoder, mal_uint64 frameIndex)
...@@ -12820,7 +12881,10 @@ void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count) ...@@ -12820,7 +12881,10 @@ void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count)
// ================ // ================
// //
// v0.xx - 2018-xx-xx // v0.xx - 2018-xx-xx
// - Add decoder APIs for loading WAV, FLAC and Vorbis files. // - API CHANGE: Change mal_src_read_frames() and mal_dsp_read_frames() to use 64-bit sample counts.
// - Add decoder APIs for loading WAV, FLAC, Vorbis and MP3 files.
// - Allow creation of devices without a context.
// - In this case the context is created and managed internally by the device.
// - Fix build errors with macOS. // - Fix build errors with macOS.
// //
// v0.6c - 2018-02-12 // v0.6c - 2018-02-12
......
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