App-MHFS

 view release on metacpan or  search on metacpan

share/public_html/static/music_inc/src/miniaudio.h  view on Meta::CPAN

Some backends and platforms may only support default playback and capture devices.

In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also,
do not try to call `ma_context_get_device_info()` from within the callback.

Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation.


Example 1 - Simple Enumeration
------------------------------
ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
{
    printf("Device Name: %s\n", pInfo->name);
    return MA_TRUE;
}

ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData);
if (result != MA_SUCCESS) {
    // Error.
}


See Also
--------
ma_context_get_devices()
*/
MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);

/*
Retrieves basic information about every active playback and/or capture device.

This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos`
parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback.


Parameters
----------
pContext (in)
    A pointer to the context performing the enumeration.

ppPlaybackDeviceInfos (out)
    A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices.

pPlaybackDeviceCount (out)
    A pointer to an unsigned integer that will receive the number of playback devices.

ppCaptureDeviceInfos (out)
    A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices.

pCaptureDeviceCount (out)
    A pointer to an unsigned integer that will receive the number of capture devices.


Return Value
------------
MA_SUCCESS if successful; any other error code otherwise.


Thread Safety
-------------
Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple
threads. Instead, you need to make a copy of the returned data with your own higher level synchronization.


Remarks
-------
It is _not_ safe to assume the first device in the list is the default device.

You can pass in NULL for the playback or capture lists in which case they'll be ignored.

The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers.


See Also
--------
ma_context_get_devices()
*/
MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);

/*
Retrieves information about a device of the given type, with the specified ID and share mode.


Parameters
----------
pContext (in)
    A pointer to the context performing the query.

deviceType (in)
    The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`.

pDeviceID (in)
    The ID of the device being queried.

shareMode (in)
    The share mode to query for device capabilities. This should be set to whatever you're intending on using when initializing the device. If you're unsure,
    set this to `ma_share_mode_shared`.

pDeviceInfo (out)
    A pointer to the `ma_device_info` structure that will receive the device information.


Return Value
------------
MA_SUCCESS if successful; any other error code otherwise.


Thread Safety
-------------
Safe. This is guarded using a simple mutex lock.


Remarks
-------
Do _not_ call this from within the `ma_context_enumerate_devices()` callback.

It's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in
shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify
which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if
the requested share mode is unsupported.

share/public_html/static/music_inc/src/miniaudio.h  view on Meta::CPAN

    if (resultALSA < 0) {
        ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
        return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.", ma_result_from_errno(-resultALSA));
    }

    resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary);
    if (resultALSA < 0) {
        bufferBoundary = internalPeriodSizeInFrames * internalPeriods;
    }

    /*printf("TRACE: bufferBoundary=%ld\n", bufferBoundary);*/

    if (deviceType == ma_device_type_playback && !isUsingMMap) {   /* Only playback devices in writei/readi mode need a start threshold. */
        /*
        Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to
        the size of a period. But for full-duplex we need to set it such that it is at least two periods.
        */
        resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2);
        if (resultALSA < 0) {
            ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
            ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed.", ma_result_from_errno(-resultALSA));
        }

        resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary);
        if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */
            ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
            ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed.", ma_result_from_errno(-resultALSA));
        }
    }

    resultALSA = ((ma_snd_pcm_sw_params_proc)pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams);
    if (resultALSA < 0) {
        ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
        return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.", ma_result_from_errno(-resultALSA));
    }

    ma__free_from_callbacks(pSWParams, &pContext->allocationCallbacks);
    pSWParams = NULL;


    /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */
    {
        ma_snd_pcm_chmap_t* pChmap = ((ma_snd_pcm_get_chmap_proc)pContext->alsa.snd_pcm_get_chmap)(pPCM);
        if (pChmap != NULL) {
            ma_uint32 iChannel;

            /* There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). */
            if (pChmap->channels >= internalChannels) {
                /* Drop excess channels. */
                for (iChannel = 0; iChannel < internalChannels; ++iChannel) {
                    internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
                }
            } else {
                ma_uint32 i;

                /*
                Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate
                channels. If validation fails, fall back to defaults.
                */
                ma_bool32 isValid = MA_TRUE;

                /* Fill with defaults. */
                ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);

                /* Overwrite first pChmap->channels channels. */
                for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) {
                    internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
                }

                /* Validate. */
                for (i = 0; i < internalChannels && isValid; ++i) {
                    ma_uint32 j;
                    for (j = i+1; j < internalChannels; ++j) {
                        if (internalChannelMap[i] == internalChannelMap[j]) {
                            isValid = MA_FALSE;
                            break;
                        }
                    }
                }

                /* If our channel map is invalid, fall back to defaults. */
                if (!isValid) {
                    ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
                }
            }

            free(pChmap);
            pChmap = NULL;
        } else {
            /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */
            ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
        }
    }


    /* We're done. Prepare the device. */
    resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM);
    if (resultALSA < 0) {
        ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
        return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.", ma_result_from_errno(-resultALSA));
    }


    if (deviceType == ma_device_type_capture) {
        pDevice->alsa.pPCMCapture                    = (ma_ptr)pPCM;
        pDevice->alsa.isUsingMMapCapture             = isUsingMMap;
        pDevice->capture.internalFormat              = internalFormat;
        pDevice->capture.internalChannels            = internalChannels;
        pDevice->capture.internalSampleRate          = internalSampleRate;
        ma_channel_map_copy(pDevice->capture.internalChannelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS));
        pDevice->capture.internalPeriodSizeInFrames  = internalPeriodSizeInFrames;
        pDevice->capture.internalPeriods             = internalPeriods;
    } else {
        pDevice->alsa.pPCMPlayback                   = (ma_ptr)pPCM;
        pDevice->alsa.isUsingMMapPlayback            = isUsingMMap;
        pDevice->playback.internalFormat             = internalFormat;
        pDevice->playback.internalChannels           = internalChannels;

share/public_html/static/music_inc/src/miniaudio.h  view on Meta::CPAN

            pDeviceInfo->minSampleRate = (ma_uint32)pAudioSession.sampleRate;
            pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
        }
    }
#endif
    
    (void)pDeviceInfo; /* Unused. */
    return MA_SUCCESS;
}


static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList)
{
    ma_device* pDevice = (ma_device*)pUserData;
    ma_stream_layout layout;

    MA_ASSERT(pDevice != NULL);

#if defined(MA_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. */
    layout = ma_stream_layout_interleaved;
    if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) {
        layout = ma_stream_layout_deinterleaved;
    }
    
    if (layout == ma_stream_layout_interleaved) {
        /* For now we can assume everything is interleaved. */
        UInt32 iBuffer;
        for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
            if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) {
                ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
                if (frameCountForThisBuffer > 0) {
                    if (pDevice->type == ma_device_type_duplex) {
                        ma_device__handle_duplex_callback_playback(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData, &pDevice->coreaudio.duplexRB);
                    } else {
                        ma_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData);
                    }
                }
                
            #if defined(MA_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 miniaudio does not yet support non-interleaved streams. We just
                output silence here.
                */
                MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);

            #if defined(MA_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. */
        MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS);   /* This should heve been validated at initialization time. */
        
        /*
        For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
        very strange has happened and we're not going to support it.
        */
        if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) {
            ma_uint8 tempBuffer[4096];
            UInt32 iBuffer;
        
            for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {
                ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);
                ma_uint32 framesRemaining = frameCountPerBuffer;

                while (framesRemaining > 0) {
                    void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
                    ma_uint32 iChannel;
                    ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
                    if (framesToRead > framesRemaining) {
                        framesToRead = framesRemaining;
                    }
                    
                    if (pDevice->type == ma_device_type_duplex) {
                        ma_device__handle_duplex_callback_playback(pDevice, framesToRead, tempBuffer, &pDevice->coreaudio.duplexRB);
                    } else {
                        ma_device__read_frames_from_client(pDevice, framesToRead, tempBuffer);
                    }
                    
                    for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
                        ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
                    }
                    
                    ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
                    
                    framesRemaining -= framesToRead;
                }
            }
        }
    }
    
    (void)pActionFlags;
    (void)pTimeStamp;
    (void)busNumber;
    (void)frameCount;

    return noErr;
}

static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList)
{
    ma_device* pDevice = (ma_device*)pUserData;
    AudioBufferList* pRenderedBufferList;
    ma_stream_layout layout;
    OSStatus status;

    MA_ASSERT(pDevice != NULL);
    
    pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
    MA_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. */

share/public_html/static/music_inc/src/miniaudio.h  view on Meta::CPAN

        layout = ma_stream_layout_deinterleaved;
    }
    
#if defined(MA_DEBUG_OUTPUT)
    printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers);
#endif
    
    status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
    if (status != noErr) {
    #if defined(MA_DEBUG_OUTPUT)
        printf("  ERROR: AudioUnitRender() failed with %d\n", status);
    #endif
        return status;
    }
    
    if (layout == ma_stream_layout_interleaved) {
        UInt32 iBuffer;
        for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
            if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {
                if (pDevice->type == ma_device_type_duplex) {
                    ma_device__handle_duplex_callback_capture(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData, &pDevice->coreaudio.duplexRB);
                } else {
                    ma_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
                }
            #if defined(MA_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 miniaudio does not yet support non-interleaved streams.
                */
                ma_uint8 silentBuffer[4096];
                ma_uint32 framesRemaining;
                
                MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer));
                
                framesRemaining = frameCount;
                while (framesRemaining > 0) {
                    ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
                    if (framesToSend > framesRemaining) {
                        framesToSend = framesRemaining;
                    }
                    
                    if (pDevice->type == ma_device_type_duplex) {
                        ma_device__handle_duplex_callback_capture(pDevice, framesToSend, silentBuffer, &pDevice->coreaudio.duplexRB);
                    } else {
                        ma_device__send_frames_to_client(pDevice, framesToSend, silentBuffer);
                    }
                    
                    framesRemaining -= framesToSend;
                }
                
            #if defined(MA_DEBUG_OUTPUT)
                printf("  WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, pRenderedBufferList->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. */
        MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS);    /* This should have been validated at initialization time. */
        
        /*
        For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
        very strange has happened and we're not going to support it.
        */
        if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) {
            ma_uint8 tempBuffer[4096];
            UInt32 iBuffer;
            for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {
                ma_uint32 framesRemaining = frameCount;
                while (framesRemaining > 0) {
                    void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
                    ma_uint32 iChannel;
                    ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_sample(pDevice->capture.internalFormat);
                    if (framesToSend > framesRemaining) {
                        framesToSend = framesRemaining;
                    }
                    
                    for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
                        ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
                    }
                    
                    ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);

                    if (pDevice->type == ma_device_type_duplex) {
                        ma_device__handle_duplex_callback_capture(pDevice, framesToSend, tempBuffer, &pDevice->coreaudio.duplexRB);
                    } else {
                        ma_device__send_frames_to_client(pDevice, framesToSend, tempBuffer);
                    }

                    framesRemaining -= framesToSend;
                }
            }
        }
    }

    (void)pActionFlags;
    (void)pTimeStamp;
    (void)busNumber;
    (void)frameCount;
    (void)pUnusedBufferList;

    return noErr;
}

static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)
{
    ma_device* pDevice = (ma_device*)pUserData;
    MA_ASSERT(pDevice != NULL);
    
    /*
    There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like
    AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_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 (ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED || ma_device__get_state(pDevice) == MA_STATE_STOPPING || ma_device__get_state(pDevice) == MA_STATE_STOPPED) {
        ma_stop_proc onStop = pDevice->onStop;
        if (onStop) {
            onStop(pDevice);

share/public_html/static/music_inc/src/miniaudio.h  view on Meta::CPAN


static ma_bool32 ma_decoder_seek_bytes(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
{
    ma_bool32 wasSuccessful;

    MA_ASSERT(pDecoder != NULL);

    wasSuccessful = pDecoder->onSeek(pDecoder, byteOffset, origin);
    if (wasSuccessful) {
        if (origin == ma_seek_origin_start) {
            pDecoder->readPointerInBytes = (ma_uint64)byteOffset;
        } else {
            pDecoder->readPointerInBytes += byteOffset;
        }
    }

    return wasSuccessful;
}


MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
{
    ma_decoder_config config;
    MA_ZERO_OBJECT(&config);
    config.format     = outputFormat;
    config.channels   = ma_min(outputChannels, ma_countof(config.channelMap));
    config.sampleRate = outputSampleRate;
    config.resampling.algorithm = ma_resample_algorithm_linear;
    config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
    config.resampling.speex.quality = 3;

    /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */

    return config;
}

MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)
{
    ma_decoder_config config;
    if (pConfig != NULL) {
        config = *pConfig;
    } else {
        MA_ZERO_OBJECT(&config);
    }

    return config;
}

static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig)
{
    ma_data_converter_config converterConfig;

    MA_ASSERT(pDecoder != NULL);
    MA_ASSERT(pConfig  != NULL);

    /* Make sure we're not asking for too many channels. */
    if (pConfig->channels > MA_MAX_CHANNELS) {
        return MA_INVALID_ARGS;
    }

    /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */
    if (pDecoder->internalChannels > MA_MAX_CHANNELS) {
        return MA_INVALID_ARGS;
    }


    /* Output format. */
    if (pConfig->format == ma_format_unknown) {
        pDecoder->outputFormat = pDecoder->internalFormat;
    } else {
        pDecoder->outputFormat = pConfig->format;
    }

    if (pConfig->channels == 0) {
        pDecoder->outputChannels = pDecoder->internalChannels;
    } else {
        pDecoder->outputChannels = pConfig->channels;
    }

    if (pConfig->sampleRate == 0) {
        pDecoder->outputSampleRate = pDecoder->internalSampleRate;
    } else {
        pDecoder->outputSampleRate = pConfig->sampleRate;
    }

    if (ma_channel_map_blank(pDecoder->outputChannels, pConfig->channelMap)) {
        ma_get_standard_channel_map(ma_standard_channel_map_default, pDecoder->outputChannels, pDecoder->outputChannelMap);
    } else {
        MA_COPY_MEMORY(pDecoder->outputChannelMap, pConfig->channelMap, sizeof(pConfig->channelMap));
    }

    
    converterConfig = ma_data_converter_config_init(
        pDecoder->internalFormat,     pDecoder->outputFormat, 
        pDecoder->internalChannels,   pDecoder->outputChannels,
        pDecoder->internalSampleRate, pDecoder->outputSampleRate
    );
    ma_channel_map_copy(converterConfig.channelMapIn,  pDecoder->internalChannelMap, pDecoder->internalChannels);
    ma_channel_map_copy(converterConfig.channelMapOut, pDecoder->outputChannelMap,   pDecoder->outputChannels);
    converterConfig.channelMixMode             = pConfig->channelMixMode;
    converterConfig.ditherMode                 = pConfig->ditherMode;
    converterConfig.resampling.allowDynamicSampleRate = MA_FALSE;   /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */
    converterConfig.resampling.algorithm       = pConfig->resampling.algorithm;
    converterConfig.resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder;
    converterConfig.resampling.speex.quality   = pConfig->resampling.speex.quality;

    return ma_data_converter_init(&converterConfig, &pDecoder->converter);
}

/* WAV */
#ifdef dr_wav_h
#define MA_HAS_WAV

static size_t ma_decoder_internal_on_read__wav(void* pUserData, void* pBufferOut, size_t bytesToRead)
{
    ma_decoder* pDecoder = (ma_decoder*)pUserData;
    MA_ASSERT(pDecoder != NULL);

    return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
}



( run in 2.905 seconds using v1.01-cache-2.11-cpan-75ffa21a3d4 )