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 @@
// // then return the number of frames you wrote.
// //
// // 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;
// 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_config config = mal_device_config_init_playback(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, on_send_frames_to_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) {
// return -1;
// }
......@@ -967,6 +961,7 @@ struct mal_device
mal_bool32 usingDefaultBufferSize : 1;
mal_bool32 usingDefaultPeriods : 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_uint32 internalChannels;
mal_uint32 internalSampleRate;
......@@ -1155,6 +1150,13 @@ mal_result mal_enumerate_devices(mal_context* pContext, mal_device_type type, ma
// 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
// 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
......@@ -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.
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.
//
// 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
// Reads a number of frames.
//
// 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.
//
......@@ -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
// 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.
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
//
// 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_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.
//
// 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
// 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
// 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.
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.
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,10 +2753,10 @@ static inline mal_uint32 mal_device__read_frames_from_client(mal_device* pDevice
mal_assert(frameCount > 0);
mal_assert(pSamples != NULL);
mal_uint32 framesRead = mal_dsp_read_frames(&pDevice->dsp, frameCount, pSamples);
mal_uint32 samplesRead = framesRead * pDevice->internalChannels;
mal_uint32 sampleSize = mal_get_sample_size_in_bytes(pDevice->internalFormat);
mal_uint32 consumedBytes = samplesRead*sampleSize;
mal_uint32 framesRead = (mal_uint32)mal_dsp_read_frames(&pDevice->dsp, frameCount, pSamples);
mal_uint32 samplesRead = framesRead * pDevice->internalChannels;
mal_uint32 sampleSize = mal_get_sample_size_in_bytes(pDevice->internalFormat);
mal_uint32 consumedBytes = samplesRead*sampleSize;
mal_uint32 remainingBytes = ((frameCount * pDevice->internalChannels) - samplesRead)*sampleSize;
mal_zero_memory((mal_uint8*)pSamples + consumedBytes, remainingBytes);
......@@ -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;
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) {
break;
}
......@@ -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)
{
if (pContext == NULL) {
return mal_device_init_ex(NULL, 0, NULL, type, pDeviceID, pConfig, pUserData, pDevice);
}
if (pDevice == NULL) {
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
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.
mal_device_config config = *pConfig;
......@@ -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.
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);
......@@ -9967,6 +9976,30 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
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)
{
if (!mal_device__is_initialized(pDevice)) return;
......@@ -10039,6 +10072,12 @@ void mal_device_uninit(mal_device* pDevice)
}
#endif
if (pDevice->isOwnerOfContext) {
mal_context_uninit(pDevice->pContext);
mal_free(pDevice->pContext);
}
mal_zero_object(pDevice);
}
......@@ -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_uint32 mal_src_read_frames_linear(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_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)
{
......@@ -10428,12 +10467,12 @@ mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOu
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);
}
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;
......@@ -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(frameCount > 0);
......@@ -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.
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.
mal_uint32 totalFramesRead = 0;
mal_uint64 totalFramesRead = 0;
while (frameCount > 0) {
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 framesToRead = stagingBufferSizeInFrames;
if (framesToRead > frameCount) {
framesToRead = frameCount;
framesToRead = (mal_uint32)frameCount;
}
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,
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(frameCount > 0);
......@@ -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;
mal_uint32 totalFramesRead = 0;
mal_uint64 totalFramesRead = 0;
while (frameCount > 0) {
// The bin is where the previous and next frames are located.
float* pPrevFrame = pSRC->bin;
......@@ -11153,18 +11213,39 @@ mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOu
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);
}
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;
// Fast path.
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
mal_format pFramesFormat[2];
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) {
iFrames = 0;
mal_uint32 framesToRead = mal_countof(pFrames[0]) / (mal_max(pDSP->config.channelsIn, pDSP->config.channelsOut) * MAL_MAX_SAMPLE_SIZE_IN_BYTES);
if (framesToRead > frameCount) {
framesToRead = frameCount;
framesToRead = (mal_uint32)frameCount;
}
// The initial filling of sample data depends on whether or not we are using SRC.
mal_uint32 framesRead = 0;
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.
} else {
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
}
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 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 ((frameCountOutF - frameCountOut) > 0.0) {
......@@ -11251,8 +11332,8 @@ typedef struct
const void* pDataIn;
mal_format formatIn;
mal_uint32 channelsIn;
mal_uint32 totalFrameCount;
mal_uint32 iNextFrame;
mal_uint64 totalFrameCount;
mal_uint64 iNextFrame;
} mal_convert_frames__data;
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
mal_assert(pData->totalFrameCount >= pData->iNextFrame);
mal_uint32 framesToRead = frameCount;
mal_uint32 framesRemaining = (pData->totalFrameCount - pData->iNextFrame);
mal_uint64 framesRemaining = (pData->totalFrameCount - pData->iNextFrame);
if (framesToRead > framesRemaining) {
framesToRead = framesRemaining;
framesToRead = (mal_uint32)framesRemaining;
}
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
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) {
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) {
return frameCountOut;
}
......@@ -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)
{
if (pDecoder == NULL) return 0;
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;
return mal_dsp_read_frames_ex(&pDecoder->dsp, frameCount, pFramesOut, MAL_TRUE);
}
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)
// ================
//
// 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.
//
// 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