Commit 9c18db9a authored by David Reid's avatar David Reid

Begin work on enabling the new DSP system.

DSP is broken with this commit.
parent 7e2d176a
......@@ -529,33 +529,34 @@ typedef int mal_result;
#define MAL_SUCCESS 0
#define MAL_ERROR -1 // A generic error.
#define MAL_INVALID_ARGS -2
#define MAL_OUT_OF_MEMORY -3
#define MAL_FORMAT_NOT_SUPPORTED -4
#define MAL_NO_BACKEND -5
#define MAL_NO_DEVICE -6
#define MAL_API_NOT_FOUND -7
#define MAL_DEVICE_BUSY -8
#define MAL_DEVICE_NOT_INITIALIZED -9
#define MAL_DEVICE_ALREADY_STARTED -10
#define MAL_DEVICE_ALREADY_STARTING -11
#define MAL_DEVICE_ALREADY_STOPPED -12
#define MAL_DEVICE_ALREADY_STOPPING -13
#define MAL_FAILED_TO_MAP_DEVICE_BUFFER -14
#define MAL_FAILED_TO_UNMAP_DEVICE_BUFFER -15
#define MAL_FAILED_TO_INIT_BACKEND -16
#define MAL_FAILED_TO_READ_DATA_FROM_CLIENT -17
#define MAL_FAILED_TO_READ_DATA_FROM_DEVICE -18
#define MAL_FAILED_TO_SEND_DATA_TO_CLIENT -19
#define MAL_FAILED_TO_SEND_DATA_TO_DEVICE -20
#define MAL_FAILED_TO_OPEN_BACKEND_DEVICE -21
#define MAL_FAILED_TO_START_BACKEND_DEVICE -22
#define MAL_FAILED_TO_STOP_BACKEND_DEVICE -23
#define MAL_FAILED_TO_CONFIGURE_BACKEND_DEVICE -24
#define MAL_FAILED_TO_CREATE_MUTEX -25
#define MAL_FAILED_TO_CREATE_EVENT -26
#define MAL_FAILED_TO_CREATE_THREAD -27
#define MAL_INVALID_DEVICE_CONFIG -28
#define MAL_ACCESS_DENIED -29
#define MAL_INVALID_OPERATION -3
#define MAL_OUT_OF_MEMORY -4
#define MAL_FORMAT_NOT_SUPPORTED -5
#define MAL_NO_BACKEND -6
#define MAL_NO_DEVICE -7
#define MAL_API_NOT_FOUND -8
#define MAL_DEVICE_BUSY -9
#define MAL_DEVICE_NOT_INITIALIZED -10
#define MAL_DEVICE_ALREADY_STARTED -11
#define MAL_DEVICE_ALREADY_STARTING -12
#define MAL_DEVICE_ALREADY_STOPPED -13
#define MAL_DEVICE_ALREADY_STOPPING -14
#define MAL_FAILED_TO_MAP_DEVICE_BUFFER -15
#define MAL_FAILED_TO_UNMAP_DEVICE_BUFFER -16
#define MAL_FAILED_TO_INIT_BACKEND -17
#define MAL_FAILED_TO_READ_DATA_FROM_CLIENT -18
#define MAL_FAILED_TO_READ_DATA_FROM_DEVICE -19
#define MAL_FAILED_TO_SEND_DATA_TO_CLIENT -20
#define MAL_FAILED_TO_SEND_DATA_TO_DEVICE -21
#define MAL_FAILED_TO_OPEN_BACKEND_DEVICE -22
#define MAL_FAILED_TO_START_BACKEND_DEVICE -23
#define MAL_FAILED_TO_STOP_BACKEND_DEVICE -24
#define MAL_FAILED_TO_CONFIGURE_BACKEND_DEVICE -25
#define MAL_FAILED_TO_CREATE_MUTEX -26
#define MAL_FAILED_TO_CREATE_EVENT -27
#define MAL_FAILED_TO_CREATE_THREAD -28
#define MAL_INVALID_DEVICE_CONFIG -29
#define MAL_ACCESS_DENIED -30
typedef void (* mal_log_proc) (mal_context* pContext, mal_device* pDevice, const char* message);
typedef void (* mal_recv_proc)(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples);
......@@ -768,8 +769,6 @@ typedef struct
{
mal_uint32 sampleRateIn;
mal_uint32 sampleRateOut;
mal_format formatIn;
mal_format formatOut;
mal_uint32 channels;
mal_src_algorithm algorithm;
mal_src_read_proc onRead;
......@@ -809,6 +808,7 @@ typedef struct
mal_channel channelMapOut[MAL_MAX_CHANNELS];
mal_channel_mix_mode channelMixMode;
mal_src_algorithm srcAlgorithm;
mal_bool32 allowDynamicSampleRate;
mal_dsp_read_proc onRead;
void* pUserData;
} mal_dsp_config;
......@@ -821,6 +821,7 @@ struct mal_dsp
mal_format_converter formatConverterOut; // For converting data to the requested output format. Used as the final step in the processing pipeline.
mal_channel_router channelRouter; // For channel conversion.
mal_src src; // For sample rate conversion.
mal_bool32 isDynamicSampleRateAllowed : 1; // mal_dsp_set_input_sample_rate() and mal_dsp_set_output_sample_rate() will fail if this is set to false.
mal_bool32 isPreFormatConversionRequired : 1;
mal_bool32 isPostFormatConversionRequired : 1;
mal_bool32 isChannelRoutingRequired : 1;
......@@ -1911,12 +1912,16 @@ mal_uint64 mal_src_read_deinterleaved_ex(mal_src* pSRC, mal_uint64 frameCount, v
mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP);
// Dynamically adjusts the input sample rate.
//
// This will fail is the DSP was not initialized with allowDynamicSampleRate.
mal_result mal_dsp_set_input_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut);
// Dynamically adjusts the output sample rate.
//
// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
// is not acceptable you will need to use your own algorithm.
//
// This will fail is the DSP was not initialized with allowDynamicSampleRate.
mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut);
// Reads a number of frames and runs them through the DSP processor.
......@@ -17816,29 +17821,13 @@ mal_uint32 mal_src_cache_read_frames(mal_src_cache* pCache, mal_uint32 frameCoun
pFramesOut += framesToReadFromMemory * channels;
pCache->iNextFrame = 0;
pCache->cachedFrameCount = 0;
if (pCache->pSRC->config.formatIn == mal_format_f32) {
// No need for a conversion - read straight into the cache.
mal_uint32 framesToReadFromClient = mal_countof(pCache->pCachedFrames) / pCache->pSRC->config.channels;
if (framesToReadFromClient > MAL_SRC_CACHE_SIZE_IN_FRAMES) {
framesToReadFromClient = MAL_SRC_CACHE_SIZE_IN_FRAMES;
}
pCache->cachedFrameCount = pCache->pSRC->config.onRead(pCache->pSRC, framesToReadFromClient, pCache->pCachedFrames, pUserData);
} else {
// A format conversion is required which means we need to use an intermediary buffer.
mal_uint8 pIntermediaryBuffer[sizeof(pCache->pCachedFrames)];
mal_uint32 framesToReadFromClient = mal_min(mal_buffer_frame_capacity(pIntermediaryBuffer, channels, pCache->pSRC->config.formatIn), mal_buffer_frame_capacity(pCache->pCachedFrames, channels, mal_format_f32));
if (framesToReadFromClient > MAL_SRC_CACHE_SIZE_IN_FRAMES) {
framesToReadFromClient = MAL_SRC_CACHE_SIZE_IN_FRAMES;
}
pCache->cachedFrameCount = pCache->pSRC->config.onRead(pCache->pSRC, framesToReadFromClient, pIntermediaryBuffer, pUserData);
// Convert to f32.
mal_pcm_convert(pCache->pCachedFrames, mal_format_f32, pIntermediaryBuffer, pCache->pSRC->config.formatIn, pCache->cachedFrameCount * channels, mal_dither_mode_none);
mal_uint32 framesToReadFromClient = mal_countof(pCache->pCachedFrames) / pCache->pSRC->config.channels;
if (framesToReadFromClient > MAL_SRC_CACHE_SIZE_IN_FRAMES) {
framesToReadFromClient = MAL_SRC_CACHE_SIZE_IN_FRAMES;
}
pCache->cachedFrameCount = pCache->pSRC->config.onRead(pCache->pSRC, framesToReadFromClient, pCache->pCachedFrames, pUserData);
// Get out of this loop if nothing was able to be retrieved.
if (pCache->cachedFrameCount == 0) {
......@@ -17954,55 +17943,28 @@ mal_uint64 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint64 frameCount,
(void)flush; // Passthrough need not care about flushing.
// Fast path. No need for data conversion - just pass right through.
if (pSRC->config.formatIn == pSRC->config.formatOut) {
if (frameCount <= UINT32_MAX) {
return pSRC->config.onRead(pSRC, (mal_uint32)frameCount, pFramesOut, 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->config.onRead(pSRC, framesToReadRightNow, pFramesOut, pUserData);
if (framesRead == 0) {
break;
}
pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pSRC->config.channels * mal_get_bytes_per_sample(pSRC->config.formatOut));
frameCount -= framesRead;
totalFramesRead += framesRead;
if (frameCount <= UINT32_MAX) {
return pSRC->config.onRead(pSRC, (mal_uint32)frameCount, pFramesOut, pUserData);
} else {
mal_uint64 totalFramesRead = 0;
while (frameCount > 0) {
mal_uint32 framesToReadRightNow = UINT32_MAX;
if (framesToReadRightNow > frameCount) {
framesToReadRightNow = (mal_uint32)frameCount;
}
return totalFramesRead;
}
}
// Slower path. Need to do a format conversion.
mal_uint64 totalFramesRead = 0;
while (frameCount > 0) {
mal_uint8 pStagingBuffer[MAL_MAX_CHANNELS * 2048];
mal_uint32 stagingBufferSizeInFrames = sizeof(pStagingBuffer) / mal_get_bytes_per_sample(pSRC->config.formatIn) / pSRC->config.channels;
mal_uint32 framesToRead = stagingBufferSizeInFrames;
if (framesToRead > frameCount) {
framesToRead = (mal_uint32)frameCount;
}
mal_uint32 framesRead = pSRC->config.onRead(pSRC, framesToReadRightNow, pFramesOut, pUserData);
if (framesRead == 0) {
break;
}
mal_uint32 framesRead = pSRC->config.onRead(pSRC, framesToRead, pStagingBuffer, pUserData);
if (framesRead == 0) {
break;
pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pSRC->config.channels * sizeof(float));
frameCount -= framesRead;
totalFramesRead += framesRead;
}
mal_pcm_convert(pFramesOut, pSRC->config.formatOut, pStagingBuffer, pSRC->config.formatIn, framesRead * pSRC->config.channels, mal_dither_mode_none);
pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pSRC->config.channels * mal_get_bytes_per_sample(pSRC->config.formatOut));
frameCount -= framesRead;
totalFramesRead += framesRead;
return totalFramesRead;
}
return totalFramesRead;
}
mal_uint64 mal_src_read_frames_linear(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush, void* pUserData)
......@@ -18069,9 +18031,10 @@ mal_uint64 mal_src_read_frames_linear(mal_src* pSRC, mal_uint64 frameCount, void
}
}
mal_pcm_convert(pFramesOut, pSRC->config.formatOut, pFrame, mal_format_f32, 1 * pSRC->config.channels, mal_dither_mode_none);
//mal_pcm_convert(pFramesOut, pSRC->config.formatOut, pFrame, mal_format_f32, 1 * pSRC->config.channels, mal_dither_mode_none);
mal_copy_memory(pFramesOut, pFrame, 1 * pSRC->config.channels * sizeof(float));
pFramesOut = (mal_uint8*)pFramesOut + (1 * pSRC->config.channels * mal_get_bytes_per_sample(pSRC->config.formatOut));
pFramesOut = (mal_uint8*)pFramesOut + (1 * pSRC->config.channels * sizeof(float));
frameCount -= 1;
totalFramesRead += 1;
......@@ -18806,7 +18769,7 @@ mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP)
// Notice how the Channel Routing and Sample Rate Conversion stages are swapped so that the SRC stage has less data to process.
// First we need to determin what's required and what's not.
if (pConfig->sampleRateIn != pConfig->sampleRateOut) {
if (pConfig->sampleRateIn != pConfig->sampleRateOut || pConfig->allowDynamicSampleRate) {
pDSP->isSRCRequired = MAL_TRUE;
}
if (pConfig->channelsIn != pConfig->channelsOut || !mal_channel_map_equal(pConfig->channelsIn, pConfig->channelMapIn, pConfig->channelMapOut)) {
......@@ -18885,9 +18848,7 @@ mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP)
mal_zero_object(&srcConfig);
srcConfig.sampleRateIn = pConfig->sampleRateIn;
srcConfig.sampleRateOut = pConfig->sampleRateOut;
srcConfig.formatIn = pConfig->formatIn;
srcConfig.formatOut = mal_format_f32;
srcConfig.channels = pConfig->channelsIn;
srcConfig.channels = (pConfig->channelsIn < pConfig->channelsOut) ? pConfig->channelsIn : pConfig->channelsOut;
srcConfig.algorithm = pConfig->srcAlgorithm;
srcConfig.onRead = mal_dsp__src_on_read;
srcConfig.onReadDeinterleaved = mal_dsp__src_on_read_deinterleaved;
......@@ -18916,7 +18877,7 @@ mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP)
#if 0
pDSP->isChannelMappingRequired = MAL_FALSE;
if (pConfig->channelMapIn[0] != MAL_CHANNEL_NONE && pConfig->channelMapOut[0] != MAL_CHANNEL_NONE) { // <-- Channel mapping will be ignored if the first channel map is MAL_CHANNEL_NONE.
// When using channel mapping we need to figure out a shuffling table. The first thing to do is convert the input channel map
......@@ -18976,6 +18937,7 @@ mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP)
} else {
pDSP->isPassthrough = MAL_FALSE;
}
#endif
return MAL_SUCCESS;
}
......@@ -18983,45 +18945,9 @@ mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP)
mal_result mal_dsp_refresh_sample_rate(mal_dsp* pDSP)
{
// If we already have an SRC pipeline initialized we do _not_ want to re-create it. Instead we adjust it. If we didn't previously
// have an SRC pipeline in place we'll need to initialize it.
if (pDSP->isSRCRequired) {
if (pDSP->src.config.sampleRateIn != pDSP->src.config.sampleRateOut) {
mal_src_set_input_sample_rate(&pDSP->src, pDSP->src.config.sampleRateIn);
mal_src_set_output_sample_rate(&pDSP->src, pDSP->src.config.sampleRateOut);
} else {
pDSP->isSRCRequired = MAL_FALSE;
}
} else {
// We may need a new SRC pipeline.
if (pDSP->src.config.sampleRateIn != pDSP->src.config.sampleRateOut) {
pDSP->isSRCRequired = MAL_TRUE;
mal_src_config srcConfig;
srcConfig.sampleRateIn = pDSP->src.config.sampleRateIn;
srcConfig.sampleRateOut = pDSP->src.config.sampleRateOut;
srcConfig.formatIn = pDSP->src.config.formatIn;
srcConfig.formatOut = mal_format_f32;
srcConfig.channels = pDSP->channelRouter.config.channelsIn;
srcConfig.algorithm = pDSP->src.config.algorithm;
srcConfig.onRead = pDSP->src.config.onRead;
srcConfig.onReadDeinterleaved = pDSP->src.config.onReadDeinterleaved;
srcConfig.pUserData = pDSP->src.config.pUserData;
mal_result result = mal_src_init(&srcConfig, &pDSP->src);
if (result != MAL_SUCCESS) {
return result;
}
} else {
pDSP->isSRCRequired = MAL_FALSE;
}
}
// Update whether or not the pipeline is a passthrough.
if (pDSP->formatConverterIn.config.formatIn == pDSP->formatConverterOut.config.formatOut && pDSP->channelRouter.config.channelsIn == pDSP->channelRouter.config.channelsOut && pDSP->src.config.sampleRateIn == pDSP->src.config.sampleRateOut && !pDSP->isChannelMappingRequired) {
pDSP->isPassthrough = MAL_TRUE;
} else {
pDSP->isPassthrough = MAL_FALSE;
}
// The SRC stage will already have been initialized so we can just set it there.
mal_src_set_input_sample_rate(&pDSP->src, pDSP->src.config.sampleRateIn);
mal_src_set_output_sample_rate(&pDSP->src, pDSP->src.config.sampleRateOut);
return MAL_SUCCESS;
}
......@@ -19037,6 +18963,11 @@ mal_result mal_dsp_set_input_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateIn)
return MAL_INVALID_ARGS;
}
// Must have been initialized with allowDynamicSampleRate.
if (!pDSP->isDynamicSampleRateAllowed) {
return MAL_INVALID_OPERATION;
}
pDSP->src.config.sampleRateIn = sampleRateIn;
return mal_dsp_refresh_sample_rate(pDSP);
}
......@@ -19052,6 +18983,11 @@ mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOu
return MAL_INVALID_ARGS;
}
// Must have been initialized with allowDynamicSampleRate.
if (!pDSP->isDynamicSampleRateAllowed) {
return MAL_INVALID_OPERATION;
}
pDSP->src.config.sampleRateOut = sampleRateOut;
return mal_dsp_refresh_sample_rate(pDSP);
}
......@@ -19067,23 +19003,25 @@ mal_uint64 mal_dsp_read_ex(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOu
// Fast path.
if (pDSP->isPassthrough) {
if (frameCount <= UINT32_MAX) {
if (frameCount <= 0xFFFFFFFF) {
return (mal_uint32)pDSP->onRead(pDSP, (mal_uint32)frameCount, pFramesOut, pUserData);
} else {
mal_uint8* pNextFramesOut = (mal_uint8*)pFramesOut;
mal_uint64 totalFramesRead = 0;
while (frameCount > 0) {
mal_uint32 framesToReadRightNow = UINT32_MAX;
if (framesToReadRightNow > frameCount) {
framesToReadRightNow = (mal_uint32)frameCount;
while (totalFramesRead < frameCount) {
mal_uint64 framesRemaining = (frameCount - totalFramesRead);
mal_uint64 framesToReadRightNow = framesRemaining;
if (framesToReadRightNow > 0xFFFFFFFF) {
framesToReadRightNow = 0xFFFFFFFF;
}
mal_uint32 framesRead = pDSP->onRead(pDSP, framesToReadRightNow, pFramesOut, pUserData);
mal_uint32 framesRead = pDSP->onRead(pDSP, (mal_uint32)framesToReadRightNow, pNextFramesOut, pUserData);
if (framesRead == 0) {
break;
}
pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pDSP->channelRouter.config.channelsOut * mal_get_bytes_per_sample(pDSP->formatConverterOut.config.formatOut));
frameCount -= framesRead;
pNextFramesOut += framesRead * pDSP->channelRouter.config.channelsOut * mal_get_bytes_per_sample(pDSP->formatConverterOut.config.formatOut);
totalFramesRead += framesRead;
}
......@@ -19091,10 +19029,19 @@ mal_uint64 mal_dsp_read_ex(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOu
}
}
// Slower path. The real is done here. To do this all we need to do is read from the last stage in the pipeline.
mal_assert(pDSP->isPostFormatConversionRequired == MAL_TRUE);
mal_dsp_callback_data data;
data.pDSP = pDSP;
data.flush = flush;
data.pUserDataForClient = pUserData;
return mal_format_converter_read(&pDSP->formatConverterOut, frameCount, pFramesOut, &data);
#if 0
// Slower path - where the real work is done.
mal_uint8 pFrames[2][MAL_MAX_CHANNELS * 512 * MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES];
mal_format pFramesFormat[2];
mal_uint64 totalFramesRead = 0;
while (frameCount > 0) {
......@@ -19109,10 +19056,8 @@ mal_uint64 mal_dsp_read_ex(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOu
mal_uint32 framesRead = 0;
if (pDSP->isSRCRequired) {
framesRead = (mal_uint32)mal_src_read_ex(&pDSP->src, framesToRead, pFrames[iFrames], flush, pUserData);
pFramesFormat[iFrames] = pDSP->src.config.formatOut; // Should always be f32.
} else {
framesRead = pDSP->onRead(pDSP, framesToRead, pFrames[iFrames], pUserData);
pFramesFormat[iFrames] = pDSP->formatConverterIn.config.formatIn;
}
if (framesRead == 0) {
......@@ -19122,28 +19067,21 @@ mal_uint64 mal_dsp_read_ex(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOu
// Channel mixing. The input format must be in f32 which may require a conversion.
if (pDSP->channelRouter.config.channelsIn != pDSP->channelRouter.config.channelsOut) {
if (pFramesFormat[iFrames] != mal_format_f32) {
mal_pcm_convert(pFrames[(iFrames + 1) % 2], mal_format_f32, pFrames[iFrames], pDSP->formatConverterIn.config.formatIn, framesRead * pDSP->channelRouter.config.channelsIn, mal_dither_mode_none);
iFrames = (iFrames + 1) % 2;
pFramesFormat[iFrames] = mal_format_f32;
}
mal_dsp_mix_channels((float*)(pFrames[(iFrames + 1) % 2]), pDSP->channelRouter.config.channelsOut, pDSP->channelRouter.config.channelMapOut, (const float*)(pFrames[iFrames]), pDSP->channelRouter.config.channelsIn, pDSP->channelRouter.config.channelMapIn, framesRead, pDSP->channelRouter.config.mixingMode);
iFrames = (iFrames + 1) % 2;
pFramesFormat[iFrames] = mal_format_f32;
}
// Channel mapping.
if (pDSP->isChannelMappingRequired) {
for (mal_uint32 i = 0; i < framesRead; ++i) {
mal_rearrange_channels(pFrames[iFrames] + (i * pDSP->channelRouter.config.channelsOut * mal_get_bytes_per_sample(pFramesFormat[iFrames])), pDSP->channelRouter.config.channelsOut, pDSP->channelShuffleTable, pFramesFormat[iFrames]);
mal_rearrange_channels(pFrames[iFrames] + (i * pDSP->channelRouter.config.channelsOut * mal_get_bytes_per_sample(mal_format_f32)), pDSP->channelRouter.config.channelsOut, pDSP->channelShuffleTable, mal_format_f32);
}
}
// Final conversion to output format.
mal_pcm_convert(pFramesOut, pDSP->formatConverterOut.config.formatOut, pFrames[iFrames], pFramesFormat[iFrames], framesRead * pDSP->channelRouter.config.channelsOut, mal_dither_mode_none);
mal_pcm_convert(pFramesOut, pDSP->formatConverterOut.config.formatOut, pFrames[iFrames], mal_format_f32, framesRead * pDSP->channelRouter.config.channelsOut, mal_dither_mode_none);
pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pDSP->channelRouter.config.channelsOut * mal_get_bytes_per_sample(pDSP->formatConverterOut.config.formatOut));
frameCount -= framesRead;
......@@ -19151,6 +19089,7 @@ mal_uint64 mal_dsp_read_ex(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOu
}
return totalFramesRead;
#endif
}
......
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