mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-24 01:04:02 +02:00
Potential fixes for Core Audio.
This commit is contained in:
@@ -1114,6 +1114,11 @@ void mal_pcm_f32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dithe
|
|||||||
void mal_pcm_f32_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
|
void mal_pcm_f32_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
|
||||||
void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode);
|
void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode);
|
||||||
|
|
||||||
|
// Deinterleaves an interleaved buffer.
|
||||||
|
void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
|
||||||
|
|
||||||
|
// Interleaves a group of deinterleaved buffers.
|
||||||
|
void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -14900,27 +14905,62 @@ OSStatus mal_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* p
|
|||||||
#if defined(MAL_DEBUG_OUTPUT)
|
#if defined(MAL_DEBUG_OUTPUT)
|
||||||
printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers);
|
printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// For now we can assume everything is interleaved.
|
|
||||||
for (UInt32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
|
|
||||||
if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
|
|
||||||
mal_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
|
|
||||||
if (frameCountForThisBuffer > 0) {
|
|
||||||
mal_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(MAL_DEBUG_OUTPUT)
|
|
||||||
printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
// This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
|
|
||||||
// not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams. We just
|
|
||||||
// output silence here.
|
|
||||||
mal_zero_memory(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
|
|
||||||
|
|
||||||
#if defined(MAL_DEBUG_OUTPUT)
|
// We need to check whether or not we are outputting interleaved or non-interleaved samples. The
|
||||||
printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
|
// way we do this is slightly different for each type.
|
||||||
#endif
|
mal_stream_layout layout = mal_stream_layout_interleaved;
|
||||||
|
if (pBufferList->mBuffers[0].mNumberChannels != pDevice->internalChannels) {
|
||||||
|
layout = mal_stream_layout_deinterleaved;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layout == mal_stream_layout_interleaved) {
|
||||||
|
// For now we can assume everything is interleaved.
|
||||||
|
for (UInt32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
|
||||||
|
if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
|
||||||
|
mal_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
|
||||||
|
if (frameCountForThisBuffer > 0) {
|
||||||
|
mal_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(MAL_DEBUG_OUTPUT)
|
||||||
|
printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
// This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
|
||||||
|
// not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams. We just
|
||||||
|
// output silence here.
|
||||||
|
mal_zero_memory(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
|
||||||
|
|
||||||
|
#if defined(MAL_DEBUG_OUTPUT)
|
||||||
|
printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This
|
||||||
|
// assumes each buffer is the same size.
|
||||||
|
mal_uint8 tempBuffer[4096];
|
||||||
|
for (UInt32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->internalChannels) {
|
||||||
|
mal_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
|
||||||
|
|
||||||
|
mal_uint32 framesRemaining = frameCountPerBuffer;
|
||||||
|
while (framesRemaining > 0) {
|
||||||
|
mal_uint32 framesToRead = sizeof(tempBuffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
|
||||||
|
if (framesToRead > framesRemaining) {
|
||||||
|
framesToRead = framesRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
mal_device__read_frames_from_client(pDevice, framesToRead, tempBuffer);
|
||||||
|
|
||||||
|
void* ppDeinterleavedBuffers[MAL_MAX_CHANNELS];
|
||||||
|
for (mal_uint32 iChannel = 0; iChannel < pDevice->internalChannels; ++iChannel) {
|
||||||
|
ppDeinterleavedBuffers[iChannel] = (void*)mal_offset_ptr(pBufferList->mBuffers[iBuffer].mData, (frameCountPerBuffer - framesRemaining) * mal_get_bytes_per_sample(pDevice->internalFormat));
|
||||||
|
}
|
||||||
|
|
||||||
|
mal_deinterleave_pcm_frames(pDevice->internalFormat, pDevice->internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
|
||||||
|
|
||||||
|
framesRemaining -= framesToRead;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14941,6 +14981,13 @@ OSStatus mal_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pA
|
|||||||
AudioBufferList* pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
|
AudioBufferList* pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
|
||||||
mal_assert(pRenderedBufferList);
|
mal_assert(pRenderedBufferList);
|
||||||
|
|
||||||
|
// We need to check whether or not we are outputting interleaved or non-interleaved samples. The
|
||||||
|
// way we do this is slightly different for each type.
|
||||||
|
mal_stream_layout layout = mal_stream_layout_interleaved;
|
||||||
|
if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->internalChannels) {
|
||||||
|
layout = mal_stream_layout_deinterleaved;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(MAL_DEBUG_OUTPUT)
|
#if defined(MAL_DEBUG_OUTPUT)
|
||||||
printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers);
|
printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers);
|
||||||
#endif
|
#endif
|
||||||
@@ -14953,16 +15000,46 @@ OSStatus mal_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pA
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For now we can assume everything is interleaved.
|
if (layout == mal_stream_layout_interleaved) {
|
||||||
for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
|
for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
|
||||||
if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
|
if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
|
||||||
mal_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
|
mal_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
|
||||||
#if defined(MAL_DEBUG_OUTPUT)
|
#if defined(MAL_DEBUG_OUTPUT)
|
||||||
printf(" mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
|
printf(" mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
// This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
|
// This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
|
||||||
// not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams.
|
// not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams.
|
||||||
|
|
||||||
|
// TODO: Call mal_device__send_frames_to_client() with silence.
|
||||||
|
|
||||||
|
#if defined(MAL_DEBUG_OUTPUT)
|
||||||
|
printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderBufferList->mBuffers[iBuffer].mNumberChannels, pRenderBufferList->mBuffers[iBuffer].mDataByteSize);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This
|
||||||
|
// assumes each buffer is the same size.
|
||||||
|
mal_uint8 tempBuffer[4096];
|
||||||
|
for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->internalChannels) {
|
||||||
|
mal_uint32 framesRemaining = frameCount;
|
||||||
|
while (framesRemaining > 0) {
|
||||||
|
mal_uint32 framesToSend = sizeof(tempBuffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
|
||||||
|
if (framesToSend > framesRemaining) {
|
||||||
|
framesToSend = framesRemaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ppDeinterleavedBuffers[MAL_MAX_CHANNELS];
|
||||||
|
for (mal_uint32 iChannel = 0; iChannel < pDevice->internalChannels; ++iChannel) {
|
||||||
|
ppDeinterleavedBuffers[iChannel] = (void*)mal_offset_ptr(pRenderedBufferList->mBuffers[iBuffer].mData, (frameCount - framesRemaining) * mal_get_bytes_per_sample(pDevice->internalFormat));
|
||||||
|
}
|
||||||
|
|
||||||
|
mal_interleave_pcm_frames(pDevice->internalFormat, pDevice->internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
|
||||||
|
mal_device__send_frames_to_client(pDevice, framesToSend, tempBuffer);
|
||||||
|
|
||||||
|
framesRemaining -= framesToSend;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26163,6 +26240,92 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
|
||||||
|
{
|
||||||
|
if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
|
||||||
|
return; // Invalid args.
|
||||||
|
}
|
||||||
|
|
||||||
|
// For efficiency we do this per format.
|
||||||
|
switch (format) {
|
||||||
|
case mal_format_s16:
|
||||||
|
{
|
||||||
|
const mal_int16* pSrcS16 = (const mal_int16*)pInterleavedPCMFrames;
|
||||||
|
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
|
||||||
|
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
|
||||||
|
mal_int16* pDstS16 = (mal_int16*)ppDeinterleavedPCMFrames[iChannel];
|
||||||
|
pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case mal_format_f32:
|
||||||
|
{
|
||||||
|
const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
|
||||||
|
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
|
||||||
|
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
|
||||||
|
float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
|
||||||
|
pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
mal_uint32 sampleSizeInBytes = mal_get_bytes_per_sample(format);
|
||||||
|
|
||||||
|
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
|
||||||
|
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
|
||||||
|
void* pDst = mal_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
|
||||||
|
const void* pSrc = mal_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
|
||||||
|
memcpy(pDst, pSrc, sampleSizeInBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case mal_format_s16:
|
||||||
|
{
|
||||||
|
mal_int16* pDstS16 = (mal_int16*)pInterleavedPCMFrames;
|
||||||
|
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
|
||||||
|
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
|
||||||
|
const mal_int16* pSrcS16 = (const mal_int16*)ppDeinterleavedPCMFrames[iChannel];
|
||||||
|
pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case mal_format_f32:
|
||||||
|
{
|
||||||
|
float* pDstF32 = (float*)pInterleavedPCMFrames;
|
||||||
|
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
|
||||||
|
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
|
||||||
|
const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
|
||||||
|
pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
mal_uint32 sampleSizeInBytes = mal_get_bytes_per_sample(format);
|
||||||
|
|
||||||
|
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
|
||||||
|
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
|
||||||
|
void* pDst = mal_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
|
||||||
|
const void* pSrc = mal_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
|
||||||
|
memcpy(pDst, pSrc, sampleSizeInBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
|||||||
Reference in New Issue
Block a user