App-MHFS

 view release on metacpan or  search on metacpan

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

            #define MA_SUPPORT_AVX512
        #endif
    #endif

    #if defined(MA_SUPPORT_AVX512)
        #include <immintrin.h>  /* Not a mistake. Intentionally including <immintrin.h> instead of <zmmintrin.h> because otherwise the compiler will complain. */
    #elif defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX)
        #include <immintrin.h>
    #elif defined(MA_SUPPORT_SSE2)
        #include <emmintrin.h>
    #endif
#endif

#if defined(MA_ARM)
    #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
        #define MA_SUPPORT_NEON
    #endif

    /* Fall back to looking for the #include file. */
    #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
        #if !defined(MA_SUPPORT_NEON) && !defined(MA_NO_NEON) && __has_include(<arm_neon.h>)
            #define MA_SUPPORT_NEON
        #endif
    #endif

    #if defined(MA_SUPPORT_NEON)
        #include <arm_neon.h>
    #endif
#endif

/* Begin globally disabled warnings. */
#if defined(_MSC_VER)
    #pragma warning(push)
    #pragma warning(disable:4752)   /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */
#endif

#if defined(MA_X64) || defined(MA_X86)
    #if defined(_MSC_VER) && !defined(__clang__)
        #if _MSC_VER >= 1400
            #include <intrin.h>
            static MA_INLINE void ma_cpuid(int info[4], int fid)
            {
                __cpuid(info, fid);
            }
        #else
            #define MA_NO_CPUID
        #endif

        #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219)
            static MA_INLINE unsigned __int64 ma_xgetbv(int reg)
            {
                return _xgetbv(reg);
            }
        #else
            #define MA_NO_XGETBV
        #endif
    #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID)
        static MA_INLINE void ma_cpuid(int info[4], int fid)
        {
            /*
            It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the
            specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for
            supporting different assembly dialects.
            
            What's basically happening is that we're saving and restoring the ebx register manually.
            */
            #if defined(DRFLAC_X86) && defined(__PIC__)
                __asm__ __volatile__ (
                    "xchg{l} {%%}ebx, %k1;"
                    "cpuid;"
                    "xchg{l} {%%}ebx, %k1;"
                    : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
                );
            #else
                __asm__ __volatile__ (
                    "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
                );
            #endif
        }

        static MA_INLINE ma_uint64 ma_xgetbv(int reg)
        {
            unsigned int hi;
            unsigned int lo;

            __asm__ __volatile__ (
                "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg)
            );

            return ((ma_uint64)hi << 32) | (ma_uint64)lo;
        }
    #else
        #define MA_NO_CPUID
        #define MA_NO_XGETBV
    #endif
#else
    #define MA_NO_CPUID
    #define MA_NO_XGETBV
#endif

static MA_INLINE ma_bool32 ma_has_sse2(void)
{
#if defined(MA_SUPPORT_SSE2)
    #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2)
        #if defined(MA_X64)
            return MA_TRUE;    /* 64-bit targets always support SSE2. */
        #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
            return MA_TRUE;    /* If the compiler is allowed to freely generate SSE2 code we can assume support. */
        #else
            #if defined(MA_NO_CPUID)
                return MA_FALSE;
            #else
                int info[4];
                ma_cpuid(info, 1);
                return (info[3] & (1 << 26)) != 0;
            #endif
        #endif
    #else
        return MA_FALSE;       /* SSE2 is only supported on x86 and x64 architectures. */
    #endif
#else

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

#ifdef MA_ENABLE_JACK
    #define MA_HAS_JACK
    #ifdef MA_NO_RUNTIME_LINKING
        #ifdef __has_include
            #if !__has_include(<jack/jack.h>)
                #undef MA_HAS_JACK
            #endif
        #endif
    #endif
#endif
#ifdef MA_ENABLE_COREAUDIO
    #define MA_HAS_COREAUDIO
#endif
#ifdef MA_ENABLE_SNDIO
    #define MA_HAS_SNDIO
#endif
#ifdef MA_ENABLE_AUDIO4
    #define MA_HAS_AUDIO4
#endif
#ifdef MA_ENABLE_OSS
    #define MA_HAS_OSS
#endif
#ifdef MA_ENABLE_AAUDIO
    #define MA_HAS_AAUDIO
#endif
#ifdef MA_ENABLE_OPENSL
    #define MA_HAS_OPENSL
#endif
#ifdef MA_ENABLE_WEBAUDIO
    #define MA_HAS_WEBAUDIO
#endif
#ifdef MA_ENABLE_NULL
    #define MA_HAS_NULL    /* Everything supports the null backend. */
#endif

MA_API const char* ma_get_backend_name(ma_backend backend)
{
    switch (backend)
    {
        case ma_backend_wasapi:     return "WASAPI";
        case ma_backend_dsound:     return "DirectSound";
        case ma_backend_winmm:      return "WinMM";
        case ma_backend_coreaudio:  return "Core Audio";
        case ma_backend_sndio:      return "sndio";
        case ma_backend_audio4:     return "audio(4)";
        case ma_backend_oss:        return "OSS";
        case ma_backend_pulseaudio: return "PulseAudio";
        case ma_backend_alsa:       return "ALSA";
        case ma_backend_jack:       return "JACK";
        case ma_backend_aaudio:     return "AAudio";
        case ma_backend_opensl:     return "OpenSL|ES";
        case ma_backend_webaudio:   return "Web Audio";
        case ma_backend_null:       return "Null";
        default:                    return "Unknown";
    }
}

MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend)
{
    /*
    This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers
    about some enums not being handled by the switch statement.
    */
    switch (backend)
    {
        case ma_backend_wasapi:
        #if defined(MA_HAS_WASAPI)
            return MA_TRUE;
        #else
            return MA_FALSE;
        #endif
        case ma_backend_dsound:
        #if defined(MA_HAS_DSOUND)
            return MA_TRUE;
        #else
            return MA_FALSE;
        #endif
        case ma_backend_winmm:
        #if defined(MA_HAS_WINMM)
            return MA_TRUE;
        #else
            return MA_FALSE;
        #endif
        case ma_backend_coreaudio:
        #if defined(MA_HAS_COREAUDIO)
            return MA_TRUE;
        #else
            return MA_FALSE;
        #endif
        case ma_backend_sndio:
        #if defined(MA_HAS_SNDIO)
            return MA_TRUE;
        #else
            return MA_FALSE;
        #endif
        case ma_backend_audio4:
        #if defined(MA_HAS_AUDIO4)
            return MA_TRUE;
        #else
            return MA_FALSE;
        #endif
        case ma_backend_oss:
        #if defined(MA_HAS_OSS)
            return MA_TRUE;
        #else
            return MA_FALSE;
        #endif
        case ma_backend_pulseaudio:
        #if defined(MA_HAS_PULSEAUDIO)
            return MA_TRUE;
        #else
            return MA_FALSE;
        #endif
        case ma_backend_alsa:
        #if defined(MA_HAS_ALSA)
            return MA_TRUE;
        #else
            return MA_FALSE;
        #endif
        case ma_backend_jack:
        #if defined(MA_HAS_JACK)

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


    *ppPCM = NULL;
    pPCM = NULL;

    stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE;

    if (pDeviceID == NULL) {
        ma_bool32 isDeviceOpen;
        size_t i;

        /*
        We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes
        me feel better to try as hard as we can get to get _something_ working.
        */
        const char* defaultDeviceNames[] = {
            "default",
            NULL,
            NULL,
            NULL,
            NULL,
            NULL,
            NULL
        };

        if (shareMode == ma_share_mode_exclusive) {
            defaultDeviceNames[1] = "hw";
            defaultDeviceNames[2] = "hw:0";
            defaultDeviceNames[3] = "hw:0,0";
        } else {
            if (deviceType == ma_device_type_playback) {
                defaultDeviceNames[1] = "dmix";
                defaultDeviceNames[2] = "dmix:0";
                defaultDeviceNames[3] = "dmix:0,0";
            } else {
                defaultDeviceNames[1] = "dsnoop";
                defaultDeviceNames[2] = "dsnoop:0";
                defaultDeviceNames[3] = "dsnoop:0,0";
            }
            defaultDeviceNames[4] = "hw";
            defaultDeviceNames[5] = "hw:0";
            defaultDeviceNames[6] = "hw:0,0";
        }

        isDeviceOpen = MA_FALSE;
        for (i = 0; i < ma_countof(defaultDeviceNames); ++i) {
            if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') {
                if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) {
                    isDeviceOpen = MA_TRUE;
                    break;
                }
            }
        }

        if (!isDeviceOpen) {
            return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
        }
    } else {
        /*
        We're trying to open a specific device. There's a few things to consider here:
        
        miniaudio recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When
        an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it
        finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw").
        */

        /* May end up needing to make small adjustments to the ID, so make a copy. */
        ma_device_id deviceID = *pDeviceID;
        int resultALSA = -ENODEV;

        if (deviceID.alsa[0] != ':') {
            /* The ID is not in ":0,0" format. Use the ID exactly as-is. */
            resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode);
        } else {
            char hwid[256];

            /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */
            if (deviceID.alsa[1] == '\0') {
                deviceID.alsa[0] = '\0';  /* An ID of ":" should be converted to "". */
            }

            if (shareMode == ma_share_mode_shared) {
                if (deviceType == ma_device_type_playback) {
                    ma_strcpy_s(hwid, sizeof(hwid), "dmix");
                } else {
                    ma_strcpy_s(hwid, sizeof(hwid), "dsnoop");
                }

                if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
                    resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
                }
            }

            /* If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed. */
            if (resultALSA != 0) {
                ma_strcpy_s(hwid, sizeof(hwid), "hw");
                if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
                    resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
                }
            }
        }

        if (resultALSA < 0) {
            return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed.", ma_result_from_errno(-resultALSA));
        }
    }

    *ppPCM = pPCM;
    return MA_SUCCESS;
}


static ma_bool32 ma_context_is_device_id_equal__alsa(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
{
    MA_ASSERT(pContext != NULL);
    MA_ASSERT(pID0 != NULL);
    MA_ASSERT(pID1 != NULL);
    (void)pContext;

    return ma_strcmp(pID0->alsa, pID1->alsa) == 0;
}

static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
{
    int resultALSA;
    ma_bool32 cbResult = MA_TRUE;
    char** ppDeviceHints;
    ma_device_id* pUniqueIDs = NULL;
    ma_uint32 uniqueIDCount = 0;
    char** ppNextDeviceHint;

    MA_ASSERT(pContext != NULL);
    MA_ASSERT(callback != NULL);

    ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock);

    resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints);
    if (resultALSA < 0) {
        ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
        return ma_result_from_errno(-resultALSA);
    }

    ppNextDeviceHint = ppDeviceHints;
    while (*ppNextDeviceHint != NULL) {
        char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
        char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
        char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
        ma_device_type deviceType = ma_device_type_playback;
        ma_bool32 stopEnumeration = MA_FALSE;
        char hwid[sizeof(pUniqueIDs->alsa)];
        ma_device_info deviceInfo;

        if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) {
            deviceType = ma_device_type_playback;
        }
        if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) {
            deviceType = ma_device_type_capture;
        }

        if (NAME != NULL) {
            if (pContext->alsa.useVerboseDeviceEnumeration) {
                /* Verbose mode. Use the name exactly as-is. */
                ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
            } else {
                /* Simplified mode. Use ":%d,%d" format. */
                if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) {
                    /*
                    At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the
                    plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device
                    initialization time and is used as an indicator to try and use the most appropriate plugin depending on the
                    device type and sharing mode.
                    */
                    char* dst = hwid;
                    char* src = hwid+2;
                    while ((*dst++ = *src++));
                } else {
                    /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */
                    ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
                }

                if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) {
                    goto next_device;   /* The device has already been enumerated. Move on to the next one. */
                } else {
                    /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */
                    size_t oldCapacity = sizeof(*pUniqueIDs) *  uniqueIDCount;
                    size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1);
                    ma_device_id* pNewUniqueIDs = (ma_device_id*)ma__realloc_from_callbacks(pUniqueIDs, newCapacity, oldCapacity, &pContext->allocationCallbacks);
                    if (pNewUniqueIDs == NULL) {
                        goto next_device;   /* Failed to allocate memory. */
                    }

                    pUniqueIDs = pNewUniqueIDs;
                    MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid));
                    uniqueIDCount += 1;
                }
            }
        } else {
            MA_ZERO_MEMORY(hwid, sizeof(hwid));
        }

        MA_ZERO_OBJECT(&deviceInfo);
        ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1);

        /*
        DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose
        device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish
        between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the
        description.
        
        The value in DESC seems to be split into two lines, with the first line being the name of the device and the
        second line being a description of the device. I don't like having the description be across two lines because
        it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line
        being put into parentheses. In simplified mode I'm just stripping the second line entirely.
        */
        if (DESC != NULL) {
            int lfPos;
            const char* line2 = ma_find_char(DESC, '\n', &lfPos);
            if (line2 != NULL) {
                line2 += 1; /* Skip past the new-line character. */

                if (pContext->alsa.useVerboseDeviceEnumeration) {
                    /* Verbose mode. Put the second line in brackets. */
                    ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
                    ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " (");
                    ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2);
                    ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")");
                } else {
                    /* Simplified mode. Strip the second line entirely. */
                    ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);

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

                preferredFormatsALSA[1] = MA_SND_PCM_FORMAT_FLOAT_BE;
                preferredFormatsALSA[2] = MA_SND_PCM_FORMAT_S32_BE;
                preferredFormatsALSA[3] = MA_SND_PCM_FORMAT_S24_3BE;
                preferredFormatsALSA[4] = MA_SND_PCM_FORMAT_U8;
            }

            formatALSA = MA_SND_PCM_FORMAT_UNKNOWN;
            for (i = 0; i < (sizeof(preferredFormatsALSA) / sizeof(preferredFormatsALSA[0])); ++i) {
                if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, preferredFormatsALSA[i])) {
                    formatALSA = preferredFormatsALSA[i];
                    break;
                }
            }

            if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) {
                ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
                ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
                return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats.", MA_FORMAT_NOT_SUPPORTED);
            }
        }

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

        resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA);
        if (resultALSA < 0) {
            ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
            ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.", ma_result_from_errno(-resultALSA));
        }
        
        internalFormat = ma_format_from_alsa(formatALSA);
        if (internalFormat == ma_format_unknown) {
            ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
            ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
        }
    }

    /* Channels. */
    {
        unsigned int channels = (deviceType == ma_device_type_capture) ? pConfig->capture.channels : pConfig->playback.channels;
        resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels);
        if (resultALSA < 0) {
            ma__free_from_callbacks(pHWParams, &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 channel count. snd_pcm_hw_params_set_channels_near() failed.", ma_result_from_errno(-resultALSA));
        }
        internalChannels = (ma_uint32)channels;
    }

    /* Sample Rate */
    {
        unsigned int sampleRate;

        /*
        It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling enabled causes
        problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable
        resampling.
        
        To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a
        sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling
        doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly
        faster rate.
        
        miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine
        for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very
        good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion.
        
        I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce
        this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins.
        */
        ((ma_snd_pcm_hw_params_set_rate_resample_proc)pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0);

        sampleRate = pConfig->sampleRate;
        resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0);
        if (resultALSA < 0) {
            ma__free_from_callbacks(pHWParams, &pContext->allocationCallbacks);
            ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed.", ma_result_from_errno(-resultALSA));
        }
        internalSampleRate = (ma_uint32)sampleRate;
    }

    /* Periods. */
    {
        ma_uint32 periods = pConfig->periods;
        resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL);
        if (resultALSA < 0) {
            ma__free_from_callbacks(pHWParams, &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 period count. snd_pcm_hw_params_set_periods_near() failed.", ma_result_from_errno(-resultALSA));
        }
        internalPeriods = periods;
    }

    /* Buffer Size */
    {
        ma_snd_pcm_uframes_t actualBufferSizeInFrames = pConfig->periodSizeInFrames * internalPeriods;
        if (actualBufferSizeInFrames == 0) {
            actualBufferSizeInFrames = ma_scale_buffer_size(ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, internalSampleRate), bufferSizeScaleFactor) * internalPeriods;
        }

        resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames);
        if (resultALSA < 0) {
            ma__free_from_callbacks(pHWParams, &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 buffer size for device. snd_pcm_hw_params_set_buffer_size() failed.", ma_result_from_errno(-resultALSA));
        }
        internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods;
    }

    /* Apply hardware parameters. */
    resultALSA = ((ma_snd_pcm_hw_params_proc)pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams);
    if (resultALSA < 0) {
        ma__free_from_callbacks(pHWParams, &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 hardware parameters. snd_pcm_hw_params() failed.", ma_result_from_errno(-resultALSA));
    }

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

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

                }
            }
            
            if (!found) {
                return MA_DOES_NOT_EXIST;
            }
        } else {
            if (deviceType == ma_device_type_playback) {
                ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
            } else {
                ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
            }
        }
        
    
        /*
        Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
        reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to
        retrieve from the AVAudioSession shared instance.
        */
        desc.componentType = kAudioUnitType_Output;
        desc.componentSubType = kAudioUnitSubType_RemoteIO;
        desc.componentManufacturer = kAudioUnitManufacturer_Apple;
        desc.componentFlags = 0;
        desc.componentFlagsMask = 0;
    
        component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
        if (component == NULL) {
            return MA_FAILED_TO_INIT_BACKEND;
        }
    
        status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit);
        if (status != noErr) {
            return ma_result_from_OSStatus(status);
        }
    
        formatScope   = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
        formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
    
        propSize = sizeof(bestFormat);
        status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
        if (status != noErr) {
            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
            return ma_result_from_OSStatus(status);
        }
    
        ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
        audioUnit = NULL;
    
    
        pDeviceInfo->minChannels = bestFormat.mChannelsPerFrame;
        pDeviceInfo->maxChannels = bestFormat.mChannelsPerFrame;
    
        pDeviceInfo->formatCount = 1;
        result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->formats[0]);
        if (result != MA_SUCCESS) {
            return result;
        }
    
        /*
        It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do
        this we just get the shared instance and inspect.
        */
        @autoreleasepool {
            AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
            MA_ASSERT(pAudioSession != NULL);

            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)

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

                
            #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);
        }
        
        ma_event_signal(&pDevice->coreaudio.stopEvent);
    } else {
        UInt32 isRunning;
        UInt32 isRunningSize = sizeof(isRunning);
        OSStatus status = ((ma_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) {
            ma_stop_proc onStop;

            /*
            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 (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) ||
                ((audioUnit == pDevice->coreaudio.audioUnitCapture)  && pDevice->coreaudio.isDefaultCaptureDevice)) {
                /*
                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 (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
                    ((audioUnit == pDevice->coreaudio.audioUnitCapture)  && pDevice->coreaudio.isSwitchingCaptureDevice)) {
                    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. */
            onStop = pDevice->onStop;
            if (onStop) {
                onStop(pDevice);
            }
        }
    }

    (void)propertyID; /* Unused. */
}

#if defined(MA_APPLE_DESKTOP)
static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0;  /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */
static ma_uint32   g_DeviceTrackingInitCounter_CoreAudio = 0;
static ma_mutex    g_DeviceTrackingMutex_CoreAudio;
static ma_device** g_ppTrackedDevices_CoreAudio = NULL;
static ma_uint32   g_TrackedDeviceCap_CoreAudio = 0;
static ma_uint32   g_TrackedDeviceCount_CoreAudio = 0;

static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData)
{
    ma_device_type deviceType;
    
    /* Not sure if I really need to check this, but it makes me feel better. */
    if (addressCount == 0) {
        return noErr;
    }
    
    if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) {
        deviceType = ma_device_type_playback;
    } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) {
        deviceType = ma_device_type_capture;
    } else {
        return noErr;   /* Should never hit this. */
    }
    
    ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
    {
        ma_uint32 iDevice;
        for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
            ma_result reinitResult;
            ma_device* pDevice;
            
            pDevice = g_ppTrackedDevices_CoreAudio[iDevice];
            if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) {
                if (deviceType == ma_device_type_playback) {
                    pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE;
                    reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
                    pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE;
                } else {
                    pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE;
                    reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);

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

#endif
    pData->component = NULL;
    pData->audioUnit = NULL;
    pData->pAudioBufferList = NULL;
    
#if defined(MA_APPLE_DESKTOP)
    result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
    if (result != MA_SUCCESS) {
        return result;
    }
    
    pData->deviceObjectID = deviceObjectID;
#endif
    
    /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */
    pData->periodsOut = pData->periodsIn;
    if (pData->periodsOut == 0) {
        pData->periodsOut = MA_DEFAULT_PERIODS;
    }
    if (pData->periodsOut > 16) {
        pData->periodsOut = 16;
    }
    
    
    /* Audio unit. */
    status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit);
    if (status != noErr) {
        return ma_result_from_OSStatus(status);
    }
    
    
    /* The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. */
    enableIOFlag = 1;
    if (deviceType == ma_device_type_capture) {
        enableIOFlag = 0;
    }
    
    status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
    if (status != noErr) {
        ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
        return ma_result_from_OSStatus(status);
    }
    
    enableIOFlag = (enableIOFlag == 0) ? 1 : 0;
    status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
    if (status != noErr) {
        ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
        return ma_result_from_OSStatus(status);
    }
    
    
    /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */
#if defined(MA_APPLE_DESKTOP)
    status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_I...
    if (status != noErr) {
        ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
        return ma_result_from_OSStatus(result);
    }
#else
    /*
    For some reason it looks like Apple is only allowing selection of the input device. There does not appear to be any way to change
    the default output route. I have no idea why this is like this, but for now we'll only be able to configure capture devices.
    */
    if (pDeviceID != NULL) {
        if (deviceType == ma_device_type_capture) {
            ma_bool32 found = MA_FALSE;
            NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
            for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
                if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
                    [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil];
                    found = MA_TRUE;
                    break;
                }
            }
            
            if (found == MA_FALSE) {
                return MA_DOES_NOT_EXIST;
            }
        }
    }
#endif
    
    /*
    Format. This is the hardest part of initialization because there's a few variables to take into account.
      1) The format must be supported by the device.
      2) The format must be supported miniaudio.
      3) There's a priority that miniaudio prefers.
    
    Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The
    most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same
    for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely.
    
    On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.
    */
    {
        AudioUnitScope   formatScope   = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
        AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;

    #if defined(MA_APPLE_DESKTOP)
        AudioStreamBasicDescription origFormat;
        UInt32 origFormatSize;
        
        origFormatSize = sizeof(origFormat);
        status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &origFormat, &origFormatSize);
        if (status != noErr) {
            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
            return result;
        }

        result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, pData->usingDefaultFormat, pData->usingDefaultChannels, pData->usingDefaultSampleRate, &origFormat, &bestFo...
        if (result != MA_SUCCESS) {
            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
            return result;
        }
        
        /*
        Update 2020-10-10:
        
        I cannot remember where I read this in the documentation and I cannot find it again. For now I'm going to remove this
        and see what the feedback from the community is like. If this results in issues we can add it back in again. The idea
        is that the closest sample rate natively supported by the backend to the requested sample rate should be used if possible.
        */
    #if 0
        /* From what I can see, Apple's documentation implies that we should keep the sample rate consistent. */
        bestFormat.mSampleRate = origFormat.mSampleRate;
    #endif
        
        status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
        if (status != noErr) {
            /* We failed to set the format, so fall back to the current format of the audio unit. */
            bestFormat = origFormat;
        }
    #else
        UInt32 propSize = sizeof(bestFormat);
        status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
        if (status != noErr) {
            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
            return ma_result_from_OSStatus(status);
        }
        
        /*
        Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try
        setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since
        it depends on Apple's AVAudioSession API. To do this we just get the shared AVAudioSession instance and then set it. Note that from what I
        can tell, it looks like the sample rate is shared between playback and capture for everything.
        */
        @autoreleasepool {
            AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
            MA_ASSERT(pAudioSession != NULL);
            
            [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil];
            bestFormat.mSampleRate = pAudioSession.sampleRate;

            /*
            I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with
            AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead.
            */
            if (deviceType == ma_device_type_playback) {
                bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels;
            }
            if (deviceType == ma_device_type_capture) {
                bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels;
            }
        }
        
        status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
        if (status != noErr) {
            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
            return ma_result_from_OSStatus(status);
        }
    #endif
        
        result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut);
        if (result != MA_SUCCESS) {
            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
            return result;
        }
        
        if (pData->formatOut == ma_format_unknown) {
            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
            return MA_FORMAT_NOT_SUPPORTED;
        }
        
        pData->channelsOut = bestFormat.mChannelsPerFrame;
        pData->sampleRateOut = bestFormat.mSampleRate;
    }

    /* Clamp the channel count for safety. */
    if (pData->channelsOut > MA_MAX_CHANNELS) {
        pData->channelsOut = MA_MAX_CHANNELS;
    }
    
    /*
    Internal channel map. This is weird in my testing. If I use the AudioObject to get the
    channel map, the channel descriptions are set to "Unknown" for some reason. To work around
    this it looks like retrieving it from the AudioUnit will work. However, and this is where
    it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore
    I'm going to fall back to a default assumption in these cases.
    */
#if defined(MA_APPLE_DESKTOP)
    result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut);
    if (result != MA_SUCCESS) {
    #if 0
        /* Try falling back to the channel map from the AudioObject. */
        result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut);
        if (result != MA_SUCCESS) {
            return result;
        }
    #else
        /* Fall back to default assumptions. */
        ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
    #endif
    }
#else
    /* TODO: Figure out how to get the channel map using AVAudioSession. */
    ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
#endif
    

    /* Buffer size. Not allowing this to be configurable on iOS. */
    actualPeriodSizeInFrames = pData->periodSizeInFramesIn;
    
#if defined(MA_APPLE_DESKTOP)
    if (actualPeriodSizeInFrames == 0) {
        actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut);
    }
    
    result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames);
    if (result != MA_SUCCESS) {
        return result;
    }
    
    pData->periodSizeInFramesOut = actualPeriodSizeInFrames;
#else
    actualPeriodSizeInFrames = 2048;
    pData->periodSizeInFramesOut = actualPeriodSizeInFrames;
#endif


    /*
    During testing I discovered that the buffer size can be too big. You'll get an error like this:
    
      kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512
    
    Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that
    of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice.
    */
    {
        /*AudioUnitScope propScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
        AudioUnitElement propBus = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
    
        status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, propScope, propBus, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames));
        if (status != noErr) {
            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
            return ma_result_from_OSStatus(status);
        }*/

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


        MA_ASSERT(pAudioSession != NULL);

        if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) {
            /*
            I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails
            we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category.
            */
        #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH)
            options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
        #endif

            if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) {
                /* Using PlayAndRecord */
            } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) {
                /* Using Playback */
            } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) {
                /* Using Record */
            } else {
                /* Leave as default? */
            }
        } else {
            if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) {
                if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) {
                    return MA_INVALID_OPERATION;    /* Failed to set session category. */
                }
            }
        }
        
        if (!pConfig->coreaudio.noAudioSessionActivate) {
            if (![pAudioSession setActive:true error:nil]) {
                return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to activate audio session.", MA_FAILED_TO_INIT_BACKEND);
            }
        }
    }
#endif
    
#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
    pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation");
    if (pContext->coreaudio.hCoreFoundation == NULL) {
        return MA_API_NOT_FOUND;
    }
    
    pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString");
    pContext->coreaudio.CFRelease          = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFRelease");
    
    
    pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio");
    if (pContext->coreaudio.hCoreAudio == NULL) {
        ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
        return MA_API_NOT_FOUND;
    }
    
    pContext->coreaudio.AudioObjectGetPropertyData        = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData");
    pContext->coreaudio.AudioObjectGetPropertyDataSize    = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize");
    pContext->coreaudio.AudioObjectSetPropertyData        = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData");
    pContext->coreaudio.AudioObjectAddPropertyListener    = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener");
    pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener");

    /*
    It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still
    defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback.
    The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to
    AudioToolbox.
    */
    pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit");
    if (pContext->coreaudio.hAudioUnit == NULL) {
        ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
        ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
        return MA_API_NOT_FOUND;
    }
    
    if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) {
        /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */
        ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
        pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox");
        if (pContext->coreaudio.hAudioUnit == NULL) {
            ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
            ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
            return MA_API_NOT_FOUND;
        }
    }
    
    pContext->coreaudio.AudioComponentFindNext            = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext");
    pContext->coreaudio.AudioComponentInstanceDispose     = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose");
    pContext->coreaudio.AudioComponentInstanceNew         = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew");
    pContext->coreaudio.AudioOutputUnitStart              = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart");
    pContext->coreaudio.AudioOutputUnitStop               = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop");
    pContext->coreaudio.AudioUnitAddPropertyListener      = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener");
    pContext->coreaudio.AudioUnitGetPropertyInfo          = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo");
    pContext->coreaudio.AudioUnitGetProperty              = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty");
    pContext->coreaudio.AudioUnitSetProperty              = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty");
    pContext->coreaudio.AudioUnitInitialize               = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize");
    pContext->coreaudio.AudioUnitRender                   = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender");
#else
    pContext->coreaudio.CFStringGetCString                = (ma_proc)CFStringGetCString;
    pContext->coreaudio.CFRelease                         = (ma_proc)CFRelease;
    
    #if defined(MA_APPLE_DESKTOP)
    pContext->coreaudio.AudioObjectGetPropertyData        = (ma_proc)AudioObjectGetPropertyData;
    pContext->coreaudio.AudioObjectGetPropertyDataSize    = (ma_proc)AudioObjectGetPropertyDataSize;
    pContext->coreaudio.AudioObjectSetPropertyData        = (ma_proc)AudioObjectSetPropertyData;
    pContext->coreaudio.AudioObjectAddPropertyListener    = (ma_proc)AudioObjectAddPropertyListener;
    pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener;
    #endif
    
    pContext->coreaudio.AudioComponentFindNext            = (ma_proc)AudioComponentFindNext;
    pContext->coreaudio.AudioComponentInstanceDispose     = (ma_proc)AudioComponentInstanceDispose;
    pContext->coreaudio.AudioComponentInstanceNew         = (ma_proc)AudioComponentInstanceNew;
    pContext->coreaudio.AudioOutputUnitStart              = (ma_proc)AudioOutputUnitStart;
    pContext->coreaudio.AudioOutputUnitStop               = (ma_proc)AudioOutputUnitStop;
    pContext->coreaudio.AudioUnitAddPropertyListener      = (ma_proc)AudioUnitAddPropertyListener;
    pContext->coreaudio.AudioUnitGetPropertyInfo          = (ma_proc)AudioUnitGetPropertyInfo;
    pContext->coreaudio.AudioUnitGetProperty              = (ma_proc)AudioUnitGetProperty;
    pContext->coreaudio.AudioUnitSetProperty              = (ma_proc)AudioUnitSetProperty;
    pContext->coreaudio.AudioUnitInitialize               = (ma_proc)AudioUnitInitialize;
    pContext->coreaudio.AudioUnitRender                   = (ma_proc)AudioUnitRender;
#endif

    pContext->isBackendAsynchronous = MA_TRUE;
    

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

    goto return_detailed_info;
#else
    goto return_default_device;
#endif

return_default_device:
    if (pDeviceID != NULL) {
        if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) ||
            (deviceType == ma_device_type_capture  && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) {
            return MA_NO_DEVICE;   /* Don't know the device. */
        }
    }

    /* Name / Description */
    if (deviceType == ma_device_type_playback) {
        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
    } else {
        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
    }

    goto return_detailed_info;


return_detailed_info:

    /*
    For now we're just outputting a set of values that are supported by the API but not necessarily supported
    by the device natively. Later on we should work on this so that it more closely reflects the device's
    actual native format.
    */
    pDeviceInfo->minChannels = 1;
    pDeviceInfo->maxChannels = 2;
    pDeviceInfo->minSampleRate = 8000;
    pDeviceInfo->maxSampleRate = 48000;
    pDeviceInfo->formatCount = 2;
    pDeviceInfo->formats[0] = ma_format_u8;
    pDeviceInfo->formats[1] = ma_format_s16;
#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
    pDeviceInfo->formats[pDeviceInfo->formatCount] = ma_format_f32;
    pDeviceInfo->formatCount += 1;
#endif

    return MA_SUCCESS;
}


#ifdef MA_ANDROID
/*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/
static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
{
    ma_device* pDevice = (ma_device*)pUserData;
    size_t periodSizeInBytes;
    ma_uint8* pBuffer;
    SLresult resultSL;

    MA_ASSERT(pDevice != NULL);

    (void)pBufferQueue;

    /*
    For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like
    OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this,
    but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :(
    */

    /* Don't do anything if the device is not started. */
    if (pDevice->state != MA_STATE_STARTED) {
        return;
    }

    /* Don't do anything if the device is being drained. */
    if (pDevice->opensl.isDrainingCapture) {
        return;
    }

    periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
    pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes);

    if (pDevice->type == ma_device_type_duplex) {
        ma_device__handle_duplex_callback_capture(pDevice, pDevice->capture.internalPeriodSizeInFrames, pBuffer, &pDevice->opensl.duplexRB);
    } else {
        ma_device__send_frames_to_client(pDevice, pDevice->capture.internalPeriodSizeInFrames, pBuffer);
    }

    resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes);
    if (resultSL != SL_RESULT_SUCCESS) {
        return;
    }

    pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods;
}

static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
{
    ma_device* pDevice = (ma_device*)pUserData;
    size_t periodSizeInBytes;
    ma_uint8* pBuffer;
    SLresult resultSL;

    MA_ASSERT(pDevice != NULL);

    (void)pBufferQueue;

    /* Don't do anything if the device is not started. */
    if (pDevice->state != MA_STATE_STARTED) {
        return;
    }

    /* Don't do anything if the device is being drained. */
    if (pDevice->opensl.isDrainingPlayback) {
        return;
    }

    periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
    pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes);

    if (pDevice->type == ma_device_type_duplex) {
        ma_device__handle_duplex_callback_playback(pDevice, pDevice->playback.internalPeriodSizeInFrames, pBuffer, &pDevice->opensl.duplexRB);
    } else {
        ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames, pBuffer);
    }

    resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes);

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



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

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

  - Fix warnings emitted by GCC when `__inline__` is undefined or defined as nothing.

v0.9.7 - 2019-08-28
  - Add support for loopback mode (WASAPI only).
    - To use this, set the device type to ma_device_type_loopback, and then fill out the capture section of the device config.
    - If you need to capture from a specific output device, set the capture device ID to that of a playback device.
  - Fix a crash when an error is posted in ma_device_init().
  - Fix a compilation error when compiling for ARM architectures.
  - Fix a bug with the audio(4) backend where the device is incorrectly being opened in non-blocking mode.
  - Fix memory leaks in the Core Audio backend.
  - Minor refactoring to the WinMM, ALSA, PulseAudio, OSS, audio(4), sndio and null backends.

v0.9.6 - 2019-08-04
  - Add support for loading decoders using a wchar_t string for file paths.
  - Don't trigger an assert when ma_device_start() is called on a device that is already started. This will now log a warning
    and return MA_INVALID_OPERATION. The same applies for ma_device_stop().
  - Try fixing an issue with PulseAudio taking a long time to start playback.
  - Fix a bug in ma_convert_frames() and ma_convert_frames_ex().
  - Fix memory leaks in the WASAPI backend.
  - Fix a compilation error with Visual Studio 2010.

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.



( run in 0.631 second using v1.01-cache-2.11-cpan-5a3173703d6 )