App-MHFS

 view release on metacpan or  search on metacpan

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

For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface.

```c
ma_backend backends[] = {
    ma_backend_alsa,
    ma_backend_pulseaudio,
    ma_backend_wasapi,
    ma_backend_dsound
};

ma_context_config config = ma_context_config_init();
config.logCallback = my_log_callback;
config.pUserData   = pMyUserData;

ma_context context;
ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context);
if (result != MA_SUCCESS) {
    // Error.
    if (result == MA_NO_BACKEND) {
        // Couldn't find an appropriate backend.
    }
}
```


See Also
--------
ma_context_config_init()
ma_context_uninit()
*/
MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext);

/*
Uninitializes a context.


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


Thread Safety
-------------
Unsafe. Do not call this function across multiple threads as some backends read and write to global state.


Remarks
-------
Results are undefined if you call this while any device created by this context is still active.


See Also
--------
ma_context_init()
*/
MA_API ma_result ma_context_uninit(ma_context* pContext);

/*
Retrieves the size of the ma_context object.

This is mainly for the purpose of bindings to know how much memory to allocate.
*/
MA_API size_t ma_context_sizeof(void);

/*
Enumerates over every device (both playback and capture).

This is a lower-level enumeration function to the easier to use `ma_context_get_devices()`. Use `ma_context_enumerate_devices()` if you would rather not incur
an internal heap allocation, or it simply suits your code better.

Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require
opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this,
but don't call it from within the enumeration callback.

Returning false from the callback will stop enumeration. Returning true will continue enumeration.


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

callback (in)
    The callback to fire for each enumerated device.

pUserData (in)
    A pointer to application-defined data passed to the callback.


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_ assume the first enumerated device of a given type is the default device.

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) {

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

} drmp3_seek_point;
typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin);
typedef struct
{
    void* pUserData;
    void* (* onMalloc)(size_t sz, void* pUserData);
    void* (* onRealloc)(void* p, size_t sz, void* pUserData);
    void  (* onFree)(void* p, void* pUserData);
} drmp3_allocation_callbacks;
typedef struct
{
    drmp3_uint32 channels;
    drmp3_uint32 sampleRate;
} drmp3_config;
typedef struct
{
    drmp3dec decoder;
    drmp3dec_frame_info frameInfo;
    drmp3_uint32 channels;
    drmp3_uint32 sampleRate;
    drmp3_read_proc onRead;
    drmp3_seek_proc onSeek;
    void* pUserData;
    drmp3_allocation_callbacks allocationCallbacks;
    drmp3_uint32 mp3FrameChannels;
    drmp3_uint32 mp3FrameSampleRate;
    drmp3_uint32 pcmFramesConsumedInMP3Frame;
    drmp3_uint32 pcmFramesRemainingInMP3Frame;
    drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME];
    drmp3_uint64 currentPCMFrame;
    drmp3_uint64 streamCursor;
    drmp3_seek_point* pSeekPoints;
    drmp3_uint32 seekPointCount;
    size_t dataSize;
    size_t dataCapacity;
    size_t dataConsumed;
    drmp3_uint8* pData;
    drmp3_bool32 atEnd : 1;
    struct
    {
        const drmp3_uint8* pData;
        size_t dataSize;
        size_t currentReadPos;
    } memory;
} drmp3;
DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks);
DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks);
#ifndef DR_MP3_NO_STDIO
DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
#endif
DRMP3_API void drmp3_uninit(drmp3* pMP3);
DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut);
DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut);
DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex);
DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3);
DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3);
DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount);
DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints);
DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints);
DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
#ifndef DR_MP3_NO_STDIO
DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
#endif
DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks);
DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks);
#ifdef __cplusplus
}
#endif
#endif
/* dr_mp3_h end */
#endif  /* MA_NO_MP3 */


/**************************************************************************************************************************************************************

Decoding

**************************************************************************************************************************************************************/
#ifndef MA_NO_DECODING

static size_t ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
{
    size_t bytesRead;

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

    bytesRead = pDecoder->onRead(pDecoder, pBufferOut, bytesToRead);
    pDecoder->readPointerInBytes += bytesRead;

    return bytesRead;
}

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;

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

            iscf[gr->n_long_sfb + i + 0] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh));
            iscf[gr->n_long_sfb + i + 1] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh));
            iscf[gr->n_long_sfb + i + 2] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh));
        }
    } else if (gr->preflag)
    {
        static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 };
        for (i = 0; i < 10; i++)
        {
            iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]);
        }
    }
    gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0);
    gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4),  DRMP3_MAX_SCFI - gain_exp);
    for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++)
    {
        scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift);
    }
}
static const float g_drmp3_pow43[129 + 16] = {
    0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f,
    0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.2...
};
static float drmp3_L3_pow_43(int x)
{
    float frac;
    int sign, mult = 256;
    if (x < 129)
    {
        return g_drmp3_pow43[16 + x];
    }
    if (x < 1024)
    {
        mult = 16;
        x <<= 3;
    }
    sign = 2*x & 64;
    frac = (float)((x & 63) - sign) / ((x & ~63) + sign);
    return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult;
}
static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit)
{
    static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,
        -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288,
        -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288,
        -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258,
        -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259,
        -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,...
        -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,...
        -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,...
        -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1...
        -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,88...
        -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,...
        -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,...
        -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-10...
        -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1...
        -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,65...
    static const drmp3_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205};
    static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 };
    static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 };
    static const drmp3_uint8 g_linbits[] =  { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 };
#define DRMP3_PEEK_BITS(n)    (bs_cache >> (32 - n))
#define DRMP3_FLUSH_BITS(n)   { bs_cache <<= (n); bs_sh += (n); }
#define DRMP3_CHECK_BITS      while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; }
#define DRMP3_BSPOS           ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh)
    float one = 0.0f;
    int ireg = 0, big_val_cnt = gr_info->big_values;
    const drmp3_uint8 *sfb = gr_info->sfbtab;
    const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8;
    drmp3_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7);
    int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8;
    bs_next_ptr += 4;
    while (big_val_cnt > 0)
    {
        int tab_num = gr_info->table_select[ireg];
        int sfb_cnt = gr_info->region_count[ireg++];
        const drmp3_int16 *codebook = tabs + tabindex[tab_num];
        int linbits = g_linbits[tab_num];
        if (linbits)
        {
            do
            {
                np = *sfb++ / 2;
                pairs_to_decode = DRMP3_MIN(big_val_cnt, np);
                one = *scf++;
                do
                {
                    int j, w = 5;
                    int leaf = codebook[DRMP3_PEEK_BITS(w)];
                    while (leaf < 0)
                    {
                        DRMP3_FLUSH_BITS(w);
                        w = leaf & 7;
                        leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)];
                    }
                    DRMP3_FLUSH_BITS(leaf >> 8);
                    for (j = 0; j < 2; j++, dst++, leaf >>= 4)
                    {
                        int lsb = leaf & 0x0F;
                        if (lsb == 15)
                        {
                            lsb += DRMP3_PEEK_BITS(linbits);
                            DRMP3_FLUSH_BITS(linbits);
                            DRMP3_CHECK_BITS;
                            *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1);
                        } else
                        {
                            *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
                        }
                        DRMP3_FLUSH_BITS(lsb ? 1 : 0);
                    }
                    DRMP3_CHECK_BITS;
                } while (--pairs_to_decode);
            } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
        } else
        {
            do
            {
                np = *sfb++ / 2;
                pairs_to_decode = DRMP3_MIN(big_val_cnt, np);
                one = *scf++;
                do
                {
                    int j, w = 5;
                    int leaf = codebook[DRMP3_PEEK_BITS(w)];
                    while (leaf < 0)
                    {
                        DRMP3_FLUSH_BITS(w);
                        w = leaf & 7;
                        leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)];
                    }
                    DRMP3_FLUSH_BITS(leaf >> 8);
                    for (j = 0; j < 2; j++, dst++, leaf >>= 4)
                    {
                        int lsb = leaf & 0x0F;
                        *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
                        DRMP3_FLUSH_BITS(lsb ? 1 : 0);

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

        drmp3_uint32 iMP3Frame;
        drmp3_uint32 iSeekPoint;
        if (seekPointCount > totalMP3FrameCount-1) {
            seekPointCount = (drmp3_uint32)totalMP3FrameCount-1;
        }
        pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1);
        if (!drmp3_seek_to_start_of_stream(pMP3)) {
            return DRMP3_FALSE;
        }
        for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) {
            drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
            DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize);
            mp3FrameInfo[iMP3Frame].bytePos       = pMP3->streamCursor - pMP3->dataSize;
            mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount;
            pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
            if (pcmFramesInCurrentMP3FrameIn == 0) {
                return DRMP3_FALSE;
            }
            drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
        }
        nextTargetPCMFrame = 0;
        for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) {
            nextTargetPCMFrame += pcmFramesBetweenSeekPoints;
            for (;;) {
                if (nextTargetPCMFrame < runningPCMFrameCount) {
                    pSeekPoints[iSeekPoint].seekPosInBytes     = mp3FrameInfo[0].bytePos;
                    pSeekPoints[iSeekPoint].pcmFrameIndex      = nextTargetPCMFrame;
                    pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
                    pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
                    break;
                } else {
                    size_t i;
                    drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
                    for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo)-1; ++i) {
                        mp3FrameInfo[i] = mp3FrameInfo[i+1];
                    }
                    mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos       = pMP3->streamCursor - pMP3->dataSize;
                    mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount;
                    pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
                    if (pcmFramesInCurrentMP3FrameIn == 0) {
                        pSeekPoints[iSeekPoint].seekPosInBytes     = mp3FrameInfo[0].bytePos;
                        pSeekPoints[iSeekPoint].pcmFrameIndex      = nextTargetPCMFrame;
                        pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
                        pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
                        break;
                    }
                    drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
                }
            }
        }
        if (!drmp3_seek_to_start_of_stream(pMP3)) {
            return DRMP3_FALSE;
        }
        if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
            return DRMP3_FALSE;
        }
    }
    *pSeekPointCount = seekPointCount;
    return DRMP3_TRUE;
}
DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints)
{
    if (pMP3 == NULL) {
        return DRMP3_FALSE;
    }
    if (seekPointCount == 0 || pSeekPoints == NULL) {
        pMP3->seekPointCount = 0;
        pMP3->pSeekPoints = NULL;
    } else {
        pMP3->seekPointCount = seekPointCount;
        pMP3->pSeekPoints = pSeekPoints;
    }
    return DRMP3_TRUE;
}
static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
{
    drmp3_uint64 totalFramesRead = 0;
    drmp3_uint64 framesCapacity = 0;
    float* pFrames = NULL;
    float temp[4096];
    DRMP3_ASSERT(pMP3 != NULL);
    for (;;) {
        drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels;
        drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
        if (framesJustRead == 0) {
            break;
        }
        if (framesCapacity < totalFramesRead + framesJustRead) {
            drmp3_uint64 oldFramesBufferSize;
            drmp3_uint64 newFramesBufferSize;
            drmp3_uint64 newFramesCap;
            float* pNewFrames;
            newFramesCap = framesCapacity * 2;
            if (newFramesCap < totalFramesRead + framesJustRead) {
                newFramesCap = totalFramesRead + framesJustRead;
            }
            oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float);
            newFramesBufferSize = newFramesCap   * pMP3->channels * sizeof(float);
            if (newFramesBufferSize > DRMP3_SIZE_MAX) {
                break;
            }
            pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
            if (pNewFrames == NULL) {
                drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
                break;
            }
            pFrames = pNewFrames;
            framesCapacity = newFramesCap;
        }
        DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float)));
        totalFramesRead += framesJustRead;
        if (framesJustRead != framesToReadRightNow) {
            break;
        }
    }
    if (pConfig != NULL) {
        pConfig->channels   = pMP3->channels;
        pConfig->sampleRate = pMP3->sampleRate;
    }
    drmp3_uninit(pMP3);
    if (pTotalFrameCount) {

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


`ma_convert_frames()` and `ma_convert_frames_ex()` have been changed. Both of these functions now take a new parameter called `frameCountOut` which specifies
the size of the output buffer in PCM frames. This has been added for safety. In addition to this, the parameters for `ma_convert_frames_ex()` have changed to
take a pointer to a `ma_data_converter_config` object to specify the input and output formats to convert between. This was done to make it more flexible, to
prevent the parameter list getting too long, and to prevent API breakage whenever a new conversion property is added.

`ma_calculate_frame_count_after_src()` has been renamed to `ma_calculate_frame_count_after_resampling()` for consistency with the new `ma_resampler` API.


Filters
-------
The following filters have been added:

    |-------------|-------------------------------------------------------------------|
    | API         | Description                                                       |
    |-------------|-------------------------------------------------------------------|
    | ma_biquad   | Biquad filter (transposed direct form 2)                          |
    | ma_lpf1     | First order low-pass filter                                       |
    | ma_lpf2     | Second order low-pass filter                                      |
    | ma_lpf      | High order low-pass filter (Butterworth)                          |
    | ma_hpf1     | First order high-pass filter                                      |
    | ma_hpf2     | Second order high-pass filter                                     |
    | ma_hpf      | High order high-pass filter (Butterworth)                         |
    | ma_bpf2     | Second order band-pass filter                                     |
    | ma_bpf      | High order band-pass filter                                       |
    | ma_peak2    | Second order peaking filter                                       |
    | ma_notch2   | Second order notching filter                                      |
    | ma_loshelf2 | Second order low shelf filter                                     |
    | ma_hishelf2 | Second order high shelf filter                                    |
    |-------------|-------------------------------------------------------------------|

These filters all support 32-bit floating point and 16-bit signed integer formats natively. Other formats need to be converted beforehand.


Sine, Square, Triangle and Sawtooth Waveforms
---------------------------------------------
Previously miniaudio supported only sine wave generation. This has now been generalized to support sine, square, triangle and sawtooth waveforms. The old
`ma_sine_wave` API has been removed and replaced with the `ma_waveform` API. Use `ma_waveform_config_init()` to initialize a config object, and then pass it
into `ma_waveform_init()`. Then use `ma_waveform_read_pcm_frames()` to read PCM data.


Noise Generation
----------------
A noise generation API has been added. This is used via the `ma_noise` API. Currently white, pink and Brownian noise is supported. The `ma_noise` API is
similar to the waveform API. Use `ma_noise_config_init()` to initialize a config object, and then pass it into `ma_noise_init()` to initialize a `ma_noise`
object. Then use `ma_noise_read_pcm_frames()` to read PCM data.


Miscellaneous Changes
---------------------
The MA_NO_STDIO option has been removed. This would disable file I/O APIs, however this has proven to be too hard to maintain for it's perceived value and was
therefore removed.

Internal functions have all been made static where possible. If you get warnings about unused functions, please submit a bug report.

The `ma_device` structure is no longer defined as being aligned to MA_SIMD_ALIGNMENT. This resulted in a possible crash when allocating a `ma_device` object on
the heap, but not aligning it to MA_SIMD_ALIGNMENT. This crash would happen due to the compiler seeing the alignment specified on the structure and assuming it
was always aligned as such and thinking it was safe to emit alignment-dependant SIMD instructions. Since miniaudio's philosophy is for things to just work,
this has been removed from all structures.

Results codes have been overhauled. Unnecessary result codes have been removed, and some have been renumbered for organisation purposes. If you are are binding
maintainer you will need to update your result codes. Support has also been added for retrieving a human readable description of a given result code via the
`ma_result_description()` API.

ALSA: The automatic format conversion, channel conversion and resampling performed by ALSA is now disabled by default as they were causing some compatibility
issues with certain devices and configurations. These can be individually enabled via the device config:

    ```c
    deviceConfig.alsa.noAutoFormat   = MA_TRUE;
    deviceConfig.alsa.noAutoChannels = MA_TRUE;
    deviceConfig.alsa.noAutoResample = MA_TRUE;
    ```
*/

/*
RELEASE NOTES - VERSION 0.9.x
=============================
Version 0.9 includes major API changes, centered mostly around full-duplex and the rebrand to "miniaudio". Before I go into detail about the major changes I
would like to apologize. I know it's annoying dealing with breaking API changes, but I think it's best to get these changes out of the way now while the
library is still relatively young and unknown.

There's been a lot of refactoring with this release so there's a good chance a few bugs have been introduced. I apologize in advance for this. You may want to
hold off on upgrading for the short term if you're worried. If mini_al v0.8.14 works for you, and you don't need full-duplex support, you can avoid upgrading
(though you won't be getting future bug fixes).


Rebranding to "miniaudio"
-------------------------
The decision was made to rename mini_al to miniaudio. Don't worry, it's the same project. The reason for this is simple:

1) Having the word "audio" in the title makes it immediately clear that the library is related to audio; and
2) I don't like the look of the underscore.

This rebrand has necessitated a change in namespace from "mal" to "ma". I know this is annoying, and I apologize, but it's better to get this out of the road
now rather than later. Also, since there are necessary API changes for full-duplex support I think it's better to just get the namespace change over and done
with at the same time as the full-duplex changes. I'm hoping this will be the last of the major API changes. Fingers crossed!

The implementation define is now "#define MINIAUDIO_IMPLEMENTATION". You can also use "#define MA_IMPLEMENTATION" if that's your preference.


Full-Duplex Support
-------------------
The major feature added to version 0.9 is full-duplex. This has necessitated a few API changes.

1) The data callback has now changed. Previously there was one type of callback for playback and another for capture. I wanted to avoid a third callback just
   for full-duplex so the decision was made to break this API and unify the callbacks. Now, there is just one callback which is the same for all three modes
   (playback, capture, duplex). The new callback looks like the following:

       void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);

   This callback allows you to move data straight out of the input buffer and into the output buffer in full-duplex mode. In playback-only mode, pInput will be
   null. Likewise, pOutput will be null in capture-only mode. The sample count is no longer returned from the callback since it's not necessary for miniaudio
   anymore.

2) The device config needed to change in order to support full-duplex. Full-duplex requires the ability to allow the client to choose a different PCM format
   for the playback and capture sides. The old ma_device_config object simply did not allow this and needed to change. With these changes you now specify the
   device ID, format, channels, channel map and share mode on a per-playback and per-capture basis (see example below). The sample rate must be the same for
   playback and capture.

   Since the device config API has changed I have also decided to take the opportunity to simplify device initialization. Now, the device ID, device type and
   callback user data are set in the config. ma_device_init() is now simplified down to taking just the context, device config and a pointer to the device
   object being initialized. The rationale for this change is that it just makes more sense to me that these are set as part of the config like everything
   else.

   Example device initialization:

       ma_device_config config = ma_device_config_init(ma_device_type_duplex);   // Or ma_device_type_playback or ma_device_type_capture.
       config.playback.pDeviceID = &myPlaybackDeviceID; // Or NULL for the default playback device.
       config.playback.format    = ma_format_f32;
       config.playback.channels  = 2;
       config.capture.pDeviceID  = &myCaptureDeviceID;  // Or NULL for the default capture device.
       config.capture.format     = ma_format_s16;
       config.capture.channels   = 1;
       config.sampleRate         = 44100;
       config.dataCallback       = data_callback;
       config.pUserData          = &myUserData;

       result = ma_device_init(&myContext, &config, &device);
       if (result != MA_SUCCESS) {
           ... handle error ...
       }

   Note that the "onDataCallback" member of ma_device_config has been renamed to "dataCallback". Also, "onStopCallback" has been renamed to "stopCallback".

This is the first pass for full-duplex and there is a known bug. You will hear crackling on the following backends when sample rate conversion is required for
the playback device:
  - Core Audio
  - JACK
  - AAudio
  - OpenSL
  - WebAudio

In addition to the above, not all platforms have been absolutely thoroughly tested simply because I lack the hardware for such thorough testing. If you
experience a bug, an issue report on GitHub or an email would be greatly appreciated (and a sample program that reproduces the issue if possible).


Other API Changes
-----------------
In addition to the above, the following API changes have been made:

- The log callback is no longer passed to ma_context_config_init(). Instead you need to set it manually after initialization.
- The onLogCallback member of ma_context_config has been renamed to "logCallback".
- The log callback now takes a logLevel parameter. The new callback looks like: void log_callback(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
  - You can use ma_log_level_to_string() to convert the logLevel to human readable text if you want to log it.
- Some APIs have been renamed:
  - mal_decoder_read()          -> ma_decoder_read_pcm_frames()
  - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame()
  - mal_sine_wave_read()        -> ma_sine_wave_read_f32()
  - mal_sine_wave_read_ex()     -> ma_sine_wave_read_f32_ex()
- Some APIs have been removed:
  - mal_device_get_buffer_size_in_bytes()
  - mal_device_set_recv_callback()
  - mal_device_set_send_callback()
  - mal_src_set_input_sample_rate()
  - mal_src_set_output_sample_rate()
- Error codes have been rearranged. If you're a binding maintainer you will need to update.
- The ma_backend enums have been rearranged to priority order. The rationale for this is to simplify automatic backend selection and to make it easier to see
  the priority. If you're a binding maintainer you will need to update.
- ma_dsp has been renamed to ma_pcm_converter. The rationale for this change is that I'm expecting "ma_dsp" to conflict with some future planned high-level
  APIs.
- For functions that take a pointer/count combo, such as ma_decoder_read_pcm_frames(), the parameter order has changed so that the pointer comes before the
  count. The rationale for this is to keep it consistent with things like memcpy().


Miscellaneous Changes
---------------------
The following miscellaneous changes have also been made.

- The AAudio backend has been added for Android 8 and above. This is Android's new "High-Performance Audio" API. (For the record, this is one of the nicest
  audio APIs out there, just behind the BSD audio APIs).
- The WebAudio backend has been added. This is based on ScriptProcessorNode. This removes the need for SDL.
- The SDL and OpenAL backends have been removed. These were originally implemented to add support for platforms for which miniaudio was not explicitly
  supported. These are no longer needed and have therefore been removed.
- Device initialization now fails if the requested share mode is not supported. If you ask for exclusive mode, you either get an exclusive mode device, or an
  error. The rationale for this change is to give the client more control over how to handle cases when the desired shared mode is unavailable.
- A lock-free ring buffer API has been added. There are two varients of this. "ma_rb" operates on bytes, whereas "ma_pcm_rb" operates on PCM frames.
- The library is now licensed as a choice of Public Domain (Unlicense) _or_ MIT-0 (No Attribution) which is the same as MIT, but removes the attribution
  requirement. The rationale for this is to support countries that don't recognize public domain.
*/

/*
REVISION HISTORY
================
v0.10.21 - 2020-10-30
  - Add ma_is_backend_enabled() and ma_get_enabled_backends() for retrieving enabled backends at run-time.
  - WASAPI: Fix a copy and paste bug relating to loopback mode.
  - Core Audio: Fix a bug when using multiple contexts.
  - Core Audio: Fix a compilation warning.
  - Core Audio: Improvements to sample rate selection.
  - Core Audio: Improvements to format/channels/rate selection when requesting defaults.
  - Core Audio: Add notes regarding the Apple notarization process.
  - Fix some bugs due to null pointer dereferences.

v0.10.20 - 2020-10-06
  - Fix build errors with UWP.
  - Minor documentation updates.

v0.10.19 - 2020-09-22
  - WASAPI: Return an error when exclusive mode is requested, but the native format is not supported by miniaudio.
  - Fix a bug where ma_decoder_seek_to_pcm_frames() never returns MA_SUCCESS even though it was successful.
  - Store the sample rate in the `ma_lpf` and `ma_hpf` structures.

v0.10.18 - 2020-08-30
  - Fix build errors with VC6.
  - Fix a bug in channel converter for s32 format.
  - Change channel converter configs to use the default channel map instead of a blank channel map when no channel map is specified when initializing the
    config. This fixes an issue where the optimized mono expansion path would never get used.
  - Use a more appropriate default format for FLAC decoders. This will now use ma_format_s16 when the FLAC is encoded as 16-bit.
  - Update FLAC decoder.
  - Update links to point to the new repository location (https://github.com/mackron/miniaudio).

v0.10.17 - 2020-08-28
  - Fix an error where the WAV codec is incorrectly excluded from the build depending on which compile time options are set.
  - Fix a bug in ma_audio_buffer_read_pcm_frames() where it isn't returning the correct number of frames processed.
  - Fix compilation error on Android.
  - Core Audio: Fix a bug with full-duplex mode.
  - Add ma_decoder_get_cursor_in_pcm_frames().
  - Update WAV codec.

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

v0.9.5 - 2019-05-21
  - Add logging to ma_dlopen() and ma_dlsym().
  - Add ma_decoder_get_length_in_pcm_frames().
  - Fix a bug with capture on the OpenSL|ES backend.
  - Fix a bug with the ALSA backend where a device would not restart after being stopped.

v0.9.4 - 2019-05-06
  - Add support for C89. With this change, miniaudio should compile clean with GCC/Clang with "-std=c89 -ansi -pedantic" and
    Microsoft compilers back to VC6. Other compilers should also work, but have not been tested.

v0.9.3 - 2019-04-19
  - Fix compiler errors on GCC when compiling with -std=c99.

v0.9.2 - 2019-04-08
  - Add support for per-context user data.
  - Fix a potential bug with context configs.
  - Fix some bugs with PulseAudio.

v0.9.1 - 2019-03-17
  - Fix a bug where the output buffer is not getting zeroed out before calling the data callback. This happens when
    the device is running in passthrough mode (not doing any data conversion).
  - Fix an issue where the data callback is getting called too frequently on the WASAPI and DirectSound backends.
  - Fix error on the UWP build.
  - Fix a build error on Apple platforms.

v0.9 - 2019-03-06
  - Rebranded to "miniaudio". All namespaces have been renamed from "mal" to "ma".
  - API CHANGE: ma_device_init() and ma_device_config_init() have changed significantly:
    - The device type, device ID and user data pointer have moved from ma_device_init() to the config.
    - All variations of ma_device_config_init_*() have been removed in favor of just ma_device_config_init().
    - ma_device_config_init() now takes only one parameter which is the device type. All other properties need
      to be set on the returned object directly.
    - The onDataCallback and onStopCallback members of ma_device_config have been renamed to "dataCallback"
      and "stopCallback".
    - The ID of the physical device is now split into two: one for the playback device and the other for the
      capture device. This is required for full-duplex. These are named "pPlaybackDeviceID" and "pCaptureDeviceID".
  - API CHANGE: The data callback has changed. It now uses a unified callback for all device types rather than
    being separate for each. It now takes two pointers - one containing input data and the other output data. This
    design in required for full-duplex. The return value is now void instead of the number of frames written. The
    new callback looks like the following:
        void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
  - API CHANGE: Remove the log callback parameter from ma_context_config_init(). With this change,
    ma_context_config_init() now takes no parameters and the log callback is set via the structure directly. The
    new policy for config initialization is that only mandatory settings are passed in to *_config_init(). The
    "onLog" member of ma_context_config has been renamed to "logCallback".
  - API CHANGE: Remove ma_device_get_buffer_size_in_bytes().
  - API CHANGE: Rename decoding APIs to "pcm_frames" convention.
    - mal_decoder_read()          -> ma_decoder_read_pcm_frames()
    - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame()
  - API CHANGE: Rename sine wave reading APIs to f32 convention.
    - mal_sine_wave_read()    -> ma_sine_wave_read_f32()
    - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex()
  - API CHANGE: Remove some deprecated APIs
    - mal_device_set_recv_callback()
    - mal_device_set_send_callback()
    - mal_src_set_input_sample_rate()
    - mal_src_set_output_sample_rate()
  - API CHANGE: Add log level to the log callback. New signature:
    - void on_log(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
  - API CHANGE: Changes to result codes. Constants have changed and unused codes have been removed. If you're
    a binding mainainer you will need to update your result code constants.
  - API CHANGE: Change the order of the ma_backend enums to priority order. If you are a binding maintainer, you
    will need to update.
  - API CHANGE: Rename mal_dsp to ma_pcm_converter. All functions have been renamed from mal_dsp_*() to
    ma_pcm_converter_*(). All structures have been renamed from mal_dsp* to ma_pcm_converter*.
  - API CHANGE: Reorder parameters of ma_decoder_read_pcm_frames() to be consistent with the new parameter order scheme.
  - The resampling algorithm has been changed from sinc to linear. The rationale for this is that the sinc implementation
    is too inefficient right now. This will hopefully be improved at a later date.
  - Device initialization will no longer fall back to shared mode if exclusive mode is requested but is unusable.
    With this change, if you request an device in exclusive mode, but exclusive mode is not supported, it will not
    automatically fall back to shared mode. The client will need to reinitialize the device in shared mode if that's
    what they want.
  - Add ring buffer API. This is ma_rb and ma_pcm_rb, the difference being that ma_rb operates on bytes and
    ma_pcm_rb operates on PCM frames.
  - Add Web Audio backend. This is used when compiling with Emscripten. The SDL backend, which was previously
    used for web support, will be removed in a future version.
  - Add AAudio backend (Android Audio). This is the new priority backend for Android. Support for AAudio starts
    with Android 8. OpenSL|ES is used as a fallback for older versions of Android.
  - Remove OpenAL and SDL backends.
  - Fix a possible deadlock when rapidly stopping the device after it has started.
  - Update documentation.
  - Change licensing to a choice of public domain _or_ MIT-0 (No Attribution).

v0.8.14 - 2018-12-16
  - Core Audio: Fix a bug where the device state is not set correctly after stopping.
  - Add support for custom weights to the channel router.
  - Update decoders to use updated APIs in dr_flac, dr_mp3 and dr_wav.

v0.8.13 - 2018-12-04
  - Core Audio: Fix a bug with channel mapping.
  - Fix a bug with channel routing where the back/left and back/right channels have the wrong weight.

v0.8.12 - 2018-11-27
  - Drop support for SDL 1.2. The Emscripten build now requires "-s USE_SDL=2".
  - Fix a linking error with ALSA.
  - Fix a bug on iOS where the device name is not set correctly.

v0.8.11 - 2018-11-21
  - iOS bug fixes.
  - Minor tweaks to PulseAudio.

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.

v0.8.7 - 2018-09-12
  - Fix a bug with UWP detection.

v0.8.6 - 2018-08-26
  - Automatically switch the internal device when the default device is unplugged. Note that this is still in the
    early stages and not all backends handle this the same way. As of this version, this will not detect a default
    device switch when changed from the operating system's audio preferences (unless the backend itself handles
    this automatically). This is not supported in exclusive mode.
  - WASAPI and Core Audio: Add support for stream routing. When the application is using a default device and the
    user switches the default device via the operating system's audio preferences, miniaudio will automatically switch



( run in 0.555 second using v1.01-cache-2.11-cpan-2398b32b56e )