@ -1,5 +1,5 @@
/ / Audio playback and capture library . Public domain . See " unlicense " statement at the end of this file .
/ / mini_al - v0 .8 .8 - 2018 - 09 - 14
/ / mini_al - v0 .8 .10 - 2018 - 1 0- 2 1
/ /
/ / David Reid - davidreidsoftware @ gmail . com
@ -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_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 ) ;
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
@ -1629,6 +1634,7 @@ struct mal_context
mal_proc pa_stream_get_sample_spec ;
mal_proc pa_stream_get_channel_map ;
mal_proc pa_stream_get_buffer_attr ;
mal_proc pa_stream_set_buffer_attr ;
mal_proc pa_stream_get_device_name ;
mal_proc pa_stream_set_write_callback ;
mal_proc pa_stream_set_read_callback ;
@ -4864,7 +4870,7 @@ mal_bool32 mal_device__get_current_frame__null(mal_device* pDevice, mal_uint32*
mal_assert ( pCurrentPos ! = NULL ) ;
* pCurrentPos = 0 ;
mal_uint64 currentFrameAbs = ( mal_uint64 ) ( mal_timer_get_time_in_seconds ( & pDevice - > null_device . timer ) * pDevice - > sampleRate ) / pDevice - > channels ;
mal_uint64 currentFrameAbs = ( mal_uint64 ) ( mal_timer_get_time_in_seconds ( & pDevice - > null_device . timer ) * pDevice - > sampleRate ) ;
* pCurrentPos = ( mal_uint32 ) ( currentFrameAbs % pDevice - > bufferSizeInFrames ) ;
return MAL_TRUE ;
@ -4917,11 +4923,11 @@ mal_uint32 mal_device__wait_for_frames__null(mal_device* pDevice)
while ( ! pDevice - > null_device . breakFromMainLoop ) {
mal_uint32 framesAvailable = mal_device__get_available_frames__null ( pDevice ) ;
if ( framesAvailable > 0 ) {
if ( framesAvailable > = ( pDevice - > bufferSizeInFrames / pDevice - > periods ) ) {
return framesAvailable ;
}
mal_sleep ( mi">16 ) ;
mal_sleep ( n">pDevice - > bufferSizeInMilliseconds / pDevice - > periods ) ;
}
/ / We ' ll get here if the loop was terminated . Just return whatever ' s available .
@ -11784,6 +11790,7 @@ typedef mal_pa_stream_state_t (* mal_pa_stream_get_state_proc)
typedef const mal_pa_sample_spec * ( * mal_pa_stream_get_sample_spec_proc ) ( mal_pa_stream * s ) ;
typedef const mal_pa_channel_map * ( * mal_pa_stream_get_channel_map_proc ) ( mal_pa_stream * s ) ;
typedef const mal_pa_buffer_attr * ( * mal_pa_stream_get_buffer_attr_proc ) ( mal_pa_stream * s ) ;
typedef mal_pa_operation * ( * mal_pa_stream_set_buffer_attr_proc ) ( mal_pa_stream * s , const mal_pa_buffer_attr * attr , mal_pa_stream_success_cb_t cb , void * userdata ) ;
typedef const char * ( * mal_pa_stream_get_device_name_proc ) ( mal_pa_stream * s ) ;
typedef void ( * mal_pa_stream_set_write_callback_proc ) ( mal_pa_stream * s , mal_pa_stream_request_cb_t cb , void * userdata ) ;
typedef void ( * mal_pa_stream_set_read_callback_proc ) ( mal_pa_stream * s , mal_pa_stream_request_cb_t cb , void * userdata ) ;
@ -12305,6 +12312,10 @@ void mal_pulse_device_write_callback(mal_pa_stream* pStream, size_t sizeInBytes,
mal_context * pContext = pDevice - > pContext ;
mal_assert ( pContext ! = NULL ) ;
# ifdef MAL_DEBUG_OUTPUT
printf ( " [PulseAudio] write_callback: sizeInBytes=%d \n " , ( int ) sizeInBytes ) ;
# endif
size_t bytesRemaining = sizeInBytes ;
while ( bytesRemaining > 0 ) {
size_t bytesToReadFromClient = bytesRemaining ;
@ -12319,19 +12330,35 @@ void mal_pulse_device_write_callback(mal_pa_stream* pStream, size_t sizeInBytes,
return ;
}
# ifdef MAL_DEBUG_OUTPUT
printf ( " bytesToReadFromClient=%d " , ( int ) bytesToReadFromClient ) ;
# endif
if ( pBuffer ! = NULL & & bytesToReadFromClient > 0 ) {
mal_uint32 framesToReadFromClient = ( mal_uint32 ) bytesToReadFromClient / ( pDevice - > internalChannels * mal_get_bytes_per_sample ( pDevice - > internalFormat ) ) ;
if ( framesToReadFromClient > 0 ) {
mal_device__read_frames_from_client ( pDevice , framesToReadFromClient , pBuffer ) ;
# ifdef MAL_DEBUG_OUTPUT
printf ( " , framesToReadFromClient=%d \n " , ( int ) framesToReadFromClient ) ;
# endif
error = ( ( mal_pa_stream_write_proc ) pContext - > pulse . pa_stream_write ) ( ( mal_pa_stream * ) pDevice - > pulse . pStream , pBuffer , bytesToReadFromClient , NULL , 0 , MAL_PA_SEEK_RELATIVE ) ;
if ( error < 0 ) {
mal_post_error ( pDevice , MAL_LOG_LEVEL_ERROR , " [PulseAudio] Failed to write data to the PulseAudio stream. " , mal_result_from_pulse ( error ) ) ;
return ;
}
} else {
# ifdef MAL_DEBUG_OUTPUT
printf ( " , framesToReadFromClient=0 \n " ) ;
# endif
}
bytesRemaining - = bytesToReadFromClient ;
} else {
# ifdef MAL_DEBUG_OUTPUT
printf ( " , framesToReadFromClient=0 \n " ) ;
# endif
}
}
}
@ -12460,6 +12487,7 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
mal_pa_sample_spec ss ;
mal_pa_channel_map cmap ;
mal_pa_buffer_attr attr ;
mal_pa_stream_flags_t streamFlags ;
const mal_pa_sample_spec * pActualSS = NULL ;
const mal_pa_channel_map * pActualCMap = NULL ;
@ -12533,53 +12561,6 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
( ( mal_pa_operation_unref_proc ) pContext - > pulse . pa_operation_unref ) ( pOP ) ;
}
#if 0
mal_pa_sample_spec deviceSS ;
mal_pa_channel_map deviceCMap ;
if ( type = = mal_device_type_playback ) {
deviceSS = sinkInfo . sample_spec ;
deviceCMap = sinkInfo . channel_map ;
} else {
deviceSS = sourceInfo . sample_spec ;
deviceCMap = sourceInfo . channel_map ;
}
if ( pDevice - > usingDefaultFormat ) {
ss . format = deviceSS . format ;
} else {
ss . format = mal_format_to_pulse ( pConfig - > format ) ;
}
if ( ss . format = = MAL_PA_SAMPLE_INVALID ) {
ss . format = MAL_PA_SAMPLE_S16LE ;
}
if ( pDevice - > usingDefaultChannels ) {
ss . channels = deviceSS . channels ;
} else {
ss . channels = pConfig - > channels ;
}
if ( pDevice - > usingDefaultSampleRate ) {
ss . rate = deviceSS . rate ;
} else {
ss . rate = pConfig - > sampleRate ;
}
if ( pDevice - > usingDefaultChannelMap ) {
cmap = deviceCMap ;
} else {
cmap . channels = pConfig - > channels ;
for ( mal_uint32 iChannel = 0 ; iChannel < pConfig - > channels ; + + iChannel ) {
cmap . map [ iChannel ] = mal_channel_position_to_pulse ( pConfig - > channelMap [ iChannel ] ) ;
}
if ( ( ( mal_pa_channel_map_valid_proc ) pContext - > pulse . pa_channel_map_valid ) ( & cmap ) = = 0 | | ( ( mal_pa_channel_map_compatible_proc ) pContext - > pulse . pa_channel_map_compatible ) ( & cmap , & ss ) = = 0 ) {
( ( mal_pa_channel_map_init_extend_proc ) pContext - > pulse . pa_channel_map_init_extend ) ( & cmap , ss . channels , MAL_PA_CHANNEL_MAP_DEFAULT ) ; / / The channel map is invalid , so just fall back to the default .
}
}
# else
if ( type = = mal_device_type_playback ) {
ss = sinkInfo . sample_spec ;
cmap = sinkInfo . channel_map ;
@ -12587,7 +12568,7 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
ss = sourceInfo . sample_spec ;
cmap = sourceInfo . channel_map ;
}
# endif
/ / Buffer size .
bufferSizeInFrames = pDevice - > bufferSizeInFrames ;
@ -12606,10 +12587,14 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
}
attr . maxlength = bufferSizeInFrames * mal_get_bytes_per_sample ( mal_format_from_pulse ( ss . format ) ) * ss . channels ;
attr . tlength = attr . maxlength / pConfig - > periods ;
attr . tlength = attr . maxlength ;
attr . prebuf = ( mal_uint32 ) - 1 ;
attr . minreq = attr . tlength ;
attr . fragsize = attr . tlength ;
attr . minreq = attr . maxlength / pConfig - > periods ;
attr . fragsize = attr . maxlength / pConfig - > periods ;
# ifdef MAL_DEBUG_OUTPUT
printf ( " [PulseAudio] attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; bufferSizeInFrames=%d \n " , attr . maxlength , attr . tlength , attr . prebuf , attr . minreq , attr . fragsize , bufferSizeInFrames ) ;
# endif
char streamName [ 256 ] ;
if ( pConfig - > pulse . pStreamName ! = NULL ) {
@ -12628,11 +12613,15 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
}
streamFlags = MAL_PA_STREAM_START_CORKED ;
if ( dev ! = NULL ) {
streamFlags | = MAL_PA_STREAM_DONT_MOVE | MAL_PA_STREAM_FIX_FORMAT | MAL_PA_STREAM_FIX_RATE | MAL_PA_STREAM_FIX_CHANNELS ;
}
if ( type = = mal_device_type_playback ) {
error = ( ( mal_pa_stream_connect_playback_proc ) pContext - > pulse . pa_stream_connect_playback ) ( ( mal_pa_stream * ) pDevice - > pulse . pStream , dev , & attr , MAL_PA_STREAM_START_CORKED , NULL , NULL ) ;
error = ( ( mal_pa_stream_connect_playback_proc ) pContext - > pulse . pa_stream_connect_playback ) ( ( mal_pa_stream * ) pDevice - > pulse . pStream , dev , & attr , streamFlags , NULL , NULL ) ;
} else {
error = ( ( mal_pa_stream_connect_record_proc ) pContext - > pulse . pa_stream_connect_record ) ( ( mal_pa_stream * ) pDevice - > pulse . pStream , dev , & attr , MAL_PA_STREAM_START_CORKED ) ;
error = ( ( mal_pa_stream_connect_record_proc ) pContext - > pulse . pa_stream_connect_record ) ( ( mal_pa_stream * ) pDevice - > pulse . pStream , dev , & attr , streamFlags ) ;
}
if ( error ! = MAL_PA_OK ) {
@ -12652,6 +12641,21 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
/ / Internal format .
pActualSS = ( ( mal_pa_stream_get_sample_spec_proc ) pContext - > pulse . pa_stream_get_sample_spec ) ( ( mal_pa_stream * ) pDevice - > pulse . pStream ) ;
if ( pActualSS ! = NULL ) {
/ / If anything has changed between the requested and the actual sample spec , we need to update the buffer .
if ( ss . format ! = pActualSS - > format | | ss . channels ! = pActualSS - > channels | | ss . rate ! = pActualSS - > rate ) {
attr . maxlength = bufferSizeInFrames * mal_get_bytes_per_sample ( mal_format_from_pulse ( pActualSS - > format ) ) * pActualSS - > channels ;
attr . tlength = attr . maxlength ;
attr . prebuf = ( mal_uint32 ) - 1 ;
attr . minreq = attr . maxlength / pConfig - > periods ;
attr . fragsize = attr . maxlength / pConfig - > periods ;
pOP = ( ( mal_pa_stream_set_buffer_attr_proc ) pContext - > pulse . pa_stream_set_buffer_attr ) ( ( mal_pa_stream * ) pDevice - > pulse . pStream , & attr , NULL , NULL ) ;
if ( pOP ! = NULL ) {
mal_device__wait_for_operation__pulse ( pDevice , pOP ) ;
( ( mal_pa_operation_unref_proc ) pContext - > pulse . pa_operation_unref ) ( pOP ) ;
}
}
ss = * pActualSS ;
}
@ -12680,6 +12684,10 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
pDevice - > bufferSizeInFrames = attr . maxlength / ( mal_get_bytes_per_sample ( pDevice - > internalFormat ) * pDevice - > internalChannels ) ;
pDevice - > periods = attr . maxlength / attr . tlength ;
# ifdef MAL_DEBUG_OUTPUT
printf ( " [PulseAudio] actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; pDevice->bufferSizeInFrames=%d \n " , attr . maxlength , attr . tlength , attr . prebuf , attr . minreq , attr . fragsize , pDevice - > bufferSizeInFrames ) ;
# endif
/ / Grab the name of the device if we can .
dev = ( ( mal_pa_stream_get_device_name_proc ) pContext - > pulse . pa_stream_get_device_name ) ( ( mal_pa_stream * ) pDevice - > pulse . pStream ) ;
@ -12930,6 +12938,7 @@ mal_result mal_context_init__pulse(mal_context* pContext)
pContext - > pulse . pa_stream_get_sample_spec = ( mal_proc ) mal_dlsym ( pContext - > pulse . pulseSO , " pa_stream_get_sample_spec " ) ;
pContext - > pulse . pa_stream_get_channel_map = ( mal_proc ) mal_dlsym ( pContext - > pulse . pulseSO , " pa_stream_get_channel_map " ) ;
pContext - > pulse . pa_stream_get_buffer_attr = ( mal_proc ) mal_dlsym ( pContext - > pulse . pulseSO , " pa_stream_get_buffer_attr " ) ;
pContext - > pulse . pa_stream_set_buffer_attr = ( mal_proc ) mal_dlsym ( pContext - > pulse . pulseSO , " pa_stream_set_buffer_attr " ) ;
pContext - > pulse . pa_stream_get_device_name = ( mal_proc ) mal_dlsym ( pContext - > pulse . pulseSO , " pa_stream_get_device_name " ) ;
pContext - > pulse . pa_stream_set_write_callback = ( mal_proc ) mal_dlsym ( pContext - > pulse . pulseSO , " pa_stream_set_write_callback " ) ;
pContext - > pulse . pa_stream_set_read_callback = ( mal_proc ) mal_dlsym ( pContext - > pulse . pulseSO , " pa_stream_set_read_callback " ) ;
@ -12972,6 +12981,7 @@ mal_result mal_context_init__pulse(mal_context* pContext)
mal_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec ;
mal_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map ;
mal_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr ;
mal_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr ;
mal_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name ;
mal_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback ;
mal_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback ;
@ -13013,6 +13023,7 @@ mal_result mal_context_init__pulse(mal_context* pContext)
pContext - > pulse . pa_stream_get_sample_spec = ( mal_proc ) _pa_stream_get_sample_spec ;
pContext - > pulse . pa_stream_get_channel_map = ( mal_proc ) _pa_stream_get_channel_map ;
pContext - > pulse . pa_stream_get_buffer_attr = ( mal_proc ) _pa_stream_get_buffer_attr ;
pContext - > pulse . pa_stream_set_buffer_attr = ( mal_proc ) _pa_stream_set_buffer_attr ;
pContext - > pulse . pa_stream_get_device_name = ( mal_proc ) _pa_stream_get_device_name ;
pContext - > pulse . pa_stream_set_write_callback = ( mal_proc ) _pa_stream_set_write_callback ;
pContext - > pulse . pa_stream_set_read_callback = ( mal_proc ) _pa_stream_set_read_callback ;
@ -14900,27 +14911,62 @@ OSStatus mal_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* p
# if defined(MAL_DEBUG_OUTPUT)
printf ( " INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d \n " , busNumber , frameCount , pBufferList - > mNumberBuffers ) ;
# endif
/ / 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 ( pBufferList - > mBuffers [ 0 ] . mNumberChannels ! = pDevice - > internalChannels ) {
layout = mal_stream_layout_deinterleaved ;
}
/ / 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 ( 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 ) ;
# 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
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 +14987,13 @@ OSStatus mal_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pA
AudioBufferList * pRenderedBufferList = ( AudioBufferList * ) pDevice - > coreaudio . pAudioBufferList ;
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)
printf ( " INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d \n " , busNumber , frameCount , pRenderedBufferList - > mNumberBuffers ) ;
# endif
@ -14953,16 +15006,58 @@ OSStatus mal_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pA
return status ;
}
/ / For now we can assume everything is interleaved .
for ( UInt32 iBuffer = 0 ; iBuffer < pRenderedBufferList - > mNumberBuffers ; + + iBuffer ) {
if ( pRenderedBufferList - > mBuffers [ iBuffer ] . mNumberChannels = = pDevice - > internalChannels ) {
mal_device__send_frames_to_client ( pDevice , frameCount , pRenderedBufferList - > mBuffers [ iBuffer ] . mData ) ;
# if defined(MAL_DEBUG_OUTPUT)
printf ( " mDataByteSize=%d \n " , pRenderedBufferList - > 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 .
if ( layout = = mal_stream_layout_interleaved ) {
for ( UInt32 iBuffer = 0 ; iBuffer < pRenderedBufferList - > mNumberBuffers ; + + iBuffer ) {
if ( pRenderedBufferList - > mBuffers [ iBuffer ] . mNumberChannels = = pDevice - > internalChannels ) {
mal_device__send_frames_to_client ( pDevice , frameCount , pRenderedBufferList - > mBuffers [ iBuffer ] . mData ) ;
# if defined(MAL_DEBUG_OUTPUT)
printf ( " mDataByteSize=%d \n " , pRenderedBufferList - > 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 .
mal_uint8 silentBuffer [ 4096 ] ;
mal_zero_memory ( silentBuffer , sizeof ( silentBuffer ) ) ;
mal_uint32 framesRemaining = frameCount ;
while ( framesRemaining > 0 ) {
mal_uint32 framesToSend = sizeof ( silentBuffer ) / mal_get_bytes_per_frame ( pDevice - > internalFormat , pDevice - > internalChannels ) ;
if ( framesToSend > framesRemaining ) {
framesToSend = framesRemaining ;
}
mal_device__send_frames_to_client ( pDevice , framesToSend , silentBuffer ) ;
framesRemaining - = framesToSend ;
}
# 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 ;
}
}
}
@ -14976,42 +15071,53 @@ void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPro
mal_device * pDevice = ( mal_device * ) pUserData ;
mal_assert ( pDevice ! = NULL ) ;
UInt32 isRunning ;
UInt32 isRunningSize = sizeof ( isRunning ) ;
OSStatus status = ( ( mal_AudioUnitGetProperty_proc ) pDevice - > pContext - > coreaudio . AudioUnitGetProperty ) ( audioUnit , kAudioOutputUnitProperty_IsRunning , scope , element , & isRunning , & isRunningSize ) ;
if ( status ! = noErr ) {
return ; / / Don ' t really know what to do in this case . . . just ignore it , I suppose . . .
}
if ( ! isRunning ) {
/ / The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device . Some scenarios to consider :
/ /
/ / 1 ) When the device is unplugged , this will be called _before_ the default device change notification .
/ / 2 ) When the device is changed via the default device change notification , this will be called _after_ the switch .
/ /
/ / For case # 1 , we just check if there ' s a new default device available . If so , we just ignore the stop event . For case # 2 we check a flag .
if ( pDevice - > isDefaultDevice & & mal_device__get_state ( pDevice ) ! = MAL_STATE_STOPPING & & mal_device__get_state ( pDevice ) ! = MAL_STATE_STOPPED ) {
/ / It looks like the device is switching through an external event , such as the user unplugging the device or changing the default device
/ / via the operating system ' s sound settings . If we ' re re - initializing the device , we just terminate because we want the stopping of the
/ / device to be seamless to the client ( we don ' t want them receiving the onStop event and thinking that the device has stopped when it
/ / hasn ' t ! ) .
if ( pDevice - > coreaudio . isSwitchingDevice ) {
return ;
}
/ / Getting here means the device is not reinitializing which means it may have been unplugged . From what I can see , it looks like Core Audio
/ / will try switching to the new default device seamlessly . We need to somehow find a way to determine whether or not Core Audio will most
/ / likely be successful in switching to the new device .
/ /
/ / TODO : Try to predict if Core Audio will switch devices . If not , the onStop callback needs to be posted .
return ;
}
/ / Getting here means we need to stop the device .
/ / There ' s been a report of a deadlock here when triggered by mal_device_uninit ( ) . It looks like
/ / AudioUnitGetProprty ( called below ) and AudioComponentInstanceDispose ( called in mal_device_uninit )
/ / can try waiting on the same lock . I ' m going to try working around this by not calling any Core
/ / Audio APIs in the callback when the device has been stopped or uninitialized .
if ( mal_device__get_state ( pDevice ) = = MAL_STATE_UNINITIALIZED | | mal_device__get_state ( pDevice ) = = MAL_STATE_STOPPING ) {
mal_stop_proc onStop = pDevice - > onStop ;
if ( onStop ) {
onStop ( pDevice ) ;
}
} else {
UInt32 isRunning ;
UInt32 isRunningSize = sizeof ( isRunning ) ;
OSStatus status = ( ( mal_AudioUnitGetProperty_proc ) pDevice - > pContext - > coreaudio . AudioUnitGetProperty ) ( audioUnit , kAudioOutputUnitProperty_IsRunning , scope , element , & isRunning , & isRunningSize ) ;
if ( status ! = noErr ) {
return ; / / Don ' t really know what to do in this case . . . just ignore it , I suppose . . .
}
if ( ! isRunning ) {
/ / The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device . Some scenarios to consider :
/ /
/ / 1 ) When the device is unplugged , this will be called _before_ the default device change notification .
/ / 2 ) When the device is changed via the default device change notification , this will be called _after_ the switch .
/ /
/ / For case # 1 , we just check if there ' s a new default device available . If so , we just ignore the stop event . For case # 2 we check a flag .
if ( pDevice - > isDefaultDevice & & mal_device__get_state ( pDevice ) ! = MAL_STATE_STOPPING & & mal_device__get_state ( pDevice ) ! = MAL_STATE_STOPPED ) {
/ / It looks like the device is switching through an external event , such as the user unplugging the device or changing the default device
/ / via the operating system ' s sound settings . If we ' re re - initializing the device , we just terminate because we want the stopping of the
/ / device to be seamless to the client ( we don ' t want them receiving the onStop event and thinking that the device has stopped when it
/ / hasn ' t ! ) .
if ( pDevice - > coreaudio . isSwitchingDevice ) {
return ;
}
/ / Getting here means the device is not reinitializing which means it may have been unplugged . From what I can see , it looks like Core Audio
/ / will try switching to the new default device seamlessly . We need to somehow find a way to determine whether or not Core Audio will most
/ / likely be successful in switching to the new device .
/ /
/ / TODO : Try to predict if Core Audio will switch devices . If not , the onStop callback needs to be posted .
return ;
}
/ / Getting here means we need to stop the device .
mal_stop_proc onStop = pDevice - > onStop ;
if ( onStop ) {
onStop ( pDevice ) ;
}
}
}
}
@ -15288,11 +15394,14 @@ mal_result mal_device_init_internal__coreaudio(mal_context* pContext, mal_device
if ( result ! = MAL_SUCCESS ) {
return result ;
}
pData - > bufferSizeInFramesOut = actualBufferSizeInFrames * pData - > periodsOut ;
# else
actualBufferSizeInFrames = 4096 ;
pData - > bufferSizeInFramesOut = actualBufferSizeInFrames ;
# endif
pData - > bufferSizeInFramesOut = actualBufferSizeInFrames * pData - > periodsOut ;
/ / During testing I discovered that the buffer size can be too big . You ' ll get an error like this :
/ /
@ -15556,6 +15665,15 @@ mal_result mal_context_uninit__coreaudio(mal_context* pContext)
mal_result mal_context_init__coreaudio ( mal_context * pContext )
{
mal_assert ( pContext ! = NULL ) ;
# if defined(MAL_APPLE_MOBILE)
@ autoreleasepool {
AVAudioSession * pAudioSession = [ AVAudioSession sharedInstance ] ;
mal_assert ( pAudioSession ! = NULL ) ;
[ pAudioSession setCategory : AVAudioSessionCategoryPlayAndRecord error : nil ] ;
}
# endif
# if !defined(MAL_NO_RUNTIME_LINKING) && !defined(MAL_APPLE_MOBILE)
pContext - > coreaudio . hCoreFoundation = mal_dlopen ( " CoreFoundation.framework/CoreFoundation " ) ;
@ -19725,7 +19843,7 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
/ / SDL wants the buffer size to be a power of 2. The SDL_AudioSpec property for this is only a Uint16 , so we need
/ / to explicitly clamp this because it will be easy to overflow .
mal_uint32 bufferSize = pConfig - > bufferSizeInFrames ;
mal_uint32 bufferSize = pDevice - > bufferSizeInFrames ;
if ( bufferSize > 32768 ) {
bufferSize = 32768 ;
} else {
@ -19760,7 +19878,7 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
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 SDL device. " , MAL_FAILED_TO_OPEN_BACKEND_DEVICE ) ;
return mal_post_error ( pDevice , MAL_LOG_LEVEL_ERROR , " Failed to open SDL2 device. " , MAL_FAILED_TO_OPEN_BACKEND_DEVICE ) ;
}
} else
# endif
@ -19778,10 +19896,12 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
desiredSpec . format = MAL_AUDIO_S16 ;
}
n">pDevice - > sdl . deviceID = ( ( MAL_PFN_SDL_OpenAudio ) pDevice - > pContext - > sdl . SDL_OpenAudio ) ( & desiredSpec , & obtainedSpec ) ;
if ( pDevice - > sdl . deviceID ! = 0 ) {
return mal_post_error ( pDevice , MAL_LOG_LEVEL_ERROR , " Failed to open SDL device. " , MAL_FAILED_TO_OPEN_BACKEND_DEVICE ) ;
kt">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 - > internalFormat = mal_format_from_sdl ( obtainedSpec . format ) ;
@ -20429,6 +20549,14 @@ mal_result mal_context_init(const mal_backend backends[], mal_uint32 backendCoun
mal_context_post_error ( pContext , NULL , MAL_LOG_LEVEL_WARNING , " Failed to initialize mutex for device info retrieval. mal_context_get_device_info() is not thread safe. " , MAL_FAILED_TO_CREATE_MUTEX ) ;
}
# ifdef MAL_DEBUG_OUTPUT
printf ( " [mini_al] Endian: %s \n " , mal_is_little_endian ( ) ? " LE " : " BE " ) ;
printf ( " [mini_al] SSE2: %s \n " , mal_has_sse2 ( ) ? " YES " : " NO " ) ;
printf ( " [mini_al] AVX2: %s \n " , mal_has_avx2 ( ) ? " YES " : " NO " ) ;
printf ( " [mini_al] AVX512F: %s \n " , mal_has_avx512f ( ) ? " YES " : " NO " ) ;
printf ( " [mini_al] NEON: %s \n " , mal_has_neon ( ) ? " YES " : " NO " ) ;
# endif
pContext - > backend = backend ;
return result ;
}
@ -20767,7 +20895,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
# ifdef MAL_DEBUG_OUTPUT
printf ( " [WASAPI ] %s (%s) \n " , pDevice - > name , ( pDevice - > type = = mal_device_type_playback ) ? " Playback " : " Capture " ) ;
printf ( " [%s ] %s (%s) \n ", mal_get_backend_name ( pDevice - > pContext - > backend ) , pDevice - > name , ( pDevice - > type = = mal_device_type_playback ) ? " Playback " : " Capture " ) ;
printf ( " Format: %s -> %s \n " , mal_get_format_name ( pDevice - > format ) , mal_get_format_name ( pDevice - > internalFormat ) ) ;
printf ( " Channels: %d -> %d \n " , pDevice - > channels , pDevice - > internalChannels ) ;
printf ( " Sample Rate: %d -> %d \n " , pDevice - > sampleRate , pDevice - > internalSampleRate ) ;
@ -20949,7 +21077,7 @@ mal_result mal_device_stop(mal_device* pDevice)
/ / Asynchronous backends need to be handled differently .
if ( mal_context_is_backend_asynchronous ( pDevice - > pContext ) ) {
pDevice - > pContext - > onDeviceStop ( pDevice ) ;
result = pDevice- > pContext - > onDeviceStop ( pDevice ) ;
} else {
/ / Synchronous backends .
@ -26138,6 +26266,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
@ -28296,6 +28510,13 @@ mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount
/ / REVISION HISTORY
/ / = = = = = = = = = = = = = = = =
/ /
/ / v0 .8 .10 - 2018 - 10 - 21
/ / - Core Audio : Fix a hang when uninitializing a device .
/ / - Fix a bug where an incorrect value is returned from mal_device_stop ( ) .
/ /
/ / v0 .8 .9 - 2018 - 09 - 28
/ / - Fix a bug with the SDL backend where device initialization fails .
/ /
/ / v0 .8 .8 - 2018 - 09 - 14
/ / - Fix Linux build with the ALSA backend .
/ / - Minor documentation fix .