App-MHFS

 view release on metacpan or  search on metacpan

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

    | MA_NO_PULSEAUDIO     | Disables the PulseAudio backend.                                                                                                 |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_JACK           | Disables the JACK backend.                                                                                                       |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_COREAUDIO      | Disables the Core Audio backend.                                                                                                 |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_SNDIO          | Disables the sndio backend.                                                                                                      |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_AUDIO4         | Disables the audio(4) backend.                                                                                                   |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_OSS            | Disables the OSS backend.                                                                                                        |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_AAUDIO         | Disables the AAudio backend.                                                                                                     |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_OPENSL         | Disables the OpenSL|ES backend.                                                                                                  |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_WEBAUDIO       | Disables the Web Audio backend.                                                                                                  |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_NULL           | Disables the null backend.                                                                                                       |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_DECODING       | Disables decoding APIs.                                                                                                          |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_ENCODING       | Disables encoding APIs.                                                                                                          |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_WAV            | Disables the built-in WAV decoder and encoder.                                                                                   |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_FLAC           | Disables the built-in FLAC decoder.                                                                                              |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_MP3            | Disables the built-in MP3 decoder.                                                                                               |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_DEVICE_IO      | Disables playback and recording. This will disable ma_context and ma_device APIs. This is useful if you only want to use         |
    |                      | miniaudio's data conversion and/or decoding APIs.                                                                                |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_THREADING      | Disables the ma_thread, ma_mutex, ma_semaphore and ma_event APIs. This option is useful if you only need to use miniaudio for    |
    |                      | data conversion, decoding and/or encoding. Some families of APIs require threading which means the following options must also   |
    |                      | be set:                                                                                                                          |
    |                      |                                                                                                                                  |
    |                      |     ```                                                                                                                          |
    |                      |     MA_NO_DEVICE_IO                                                                                                              |
    |                      |     ```                                                                                                                          |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_GENERATION     | Disables generation APIs such a ma_waveform and ma_noise.                                                                        |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_SSE2           | Disables SSE2 optimizations.                                                                                                     |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_AVX2           | Disables AVX2 optimizations.                                                                                                     |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_AVX512         | Disables AVX-512 optimizations.                                                                                                  |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_NO_NEON           | Disables NEON optimizations.                                                                                                     |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_LOG_LEVEL [level] | Sets the logging level. Set level to one of the following:                                                                       |
    |                      |                                                                                                                                  |
    |                      |     ```                                                                                                                          |
    |                      |     MA_LOG_LEVEL_VERBOSE                                                                                                         |
    |                      |     MA_LOG_LEVEL_INFO                                                                                                            |
    |                      |     MA_LOG_LEVEL_WARNING                                                                                                         |
    |                      |     MA_LOG_LEVEL_ERROR                                                                                                           |
    |                      |     ```                                                                                                                          |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_DEBUG_OUTPUT      | Enable printf() debug output.                                                                                                    |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_COINIT_VALUE      | Windows only. The value to pass to internal calls to `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`.                     |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_API               | Controls how public APIs should be decorated. Defaults to `extern`.                                                              |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+
    | MA_DLL               | If set, configures MA_API to either import or export APIs depending on whether or not the implementation is being defined. If    |
    |                      | defining the implementation, MA_API will be configured to export. Otherwise it will be configured to import. This has no effect  |
    |                      | if MA_API is defined externally.                                                                                                 |
    +----------------------+----------------------------------------------------------------------------------------------------------------------------------+


3. Definitions
==============
This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity in the use of terms throughout the audio space, so this
section is intended to clarify how miniaudio uses each term.

3.1. Sample
-----------
A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit floating point number.

3.2. Frame / PCM Frame
----------------------
A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 samples, a mono frame is 1 sample, a 5.1 surround sound frame
is 6 samples, etc. The terms "frame" and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. If ever miniaudio
needs to refer to a compressed frame, such as a FLAC frame, it will always clarify what it's referring to with something like "FLAC frame".

3.3. Channel
------------
A stream of monaural audio that is emitted from an individual speaker in a speaker system, or received from an individual microphone in a microphone system. A
stereo stream has two channels (a left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio systems refer to a channel as
a complex audio stream that's mixed with other channels to produce the final mix - this is completely different to miniaudio's use of the term "channel" and
should not be confused.

3.4. Sample Rate
----------------
The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number of PCM frames that are processed per second.

3.5. Formats
------------
Throughout miniaudio you will see references to different sample formats:

    +---------------+----------------------------------------+---------------------------+
    | Symbol        | Description                            | Range                     |
    +---------------+----------------------------------------+---------------------------+
    | ma_format_f32 | 32-bit floating point                  | [-1, 1]                   |
    | ma_format_s16 | 16-bit signed integer                  | [-32768, 32767]           |
    | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607]       |
    | ma_format_s32 | 32-bit signed integer                  | [-2147483648, 2147483647] |
    | ma_format_u8  | 8-bit unsigned integer                 | [0, 255]                  |
    +---------------+----------------------------------------+---------------------------+

All formats are native-endian.



4. Decoding
===========
The `ma_decoder` API is used for reading audio files. The following formats are supported:

    +---------+------------------+----------+

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

            ma_proc sio_revents;
            ma_proc sio_eof;
            ma_proc sio_setvol;
            ma_proc sio_onvol;
            ma_proc sio_initpar;
        } sndio;
#endif
#ifdef MA_SUPPORT_AUDIO4
        struct
        {
            int _unused;
        } audio4;
#endif
#ifdef MA_SUPPORT_OSS
        struct
        {
            int versionMajor;
            int versionMinor;
        } oss;
#endif
#ifdef MA_SUPPORT_AAUDIO
        struct
        {
            ma_handle hAAudio; /* libaaudio.so */
            ma_proc AAudio_createStreamBuilder;
            ma_proc AAudioStreamBuilder_delete;
            ma_proc AAudioStreamBuilder_setDeviceId;
            ma_proc AAudioStreamBuilder_setDirection;
            ma_proc AAudioStreamBuilder_setSharingMode;
            ma_proc AAudioStreamBuilder_setFormat;
            ma_proc AAudioStreamBuilder_setChannelCount;
            ma_proc AAudioStreamBuilder_setSampleRate;
            ma_proc AAudioStreamBuilder_setBufferCapacityInFrames;
            ma_proc AAudioStreamBuilder_setFramesPerDataCallback;
            ma_proc AAudioStreamBuilder_setDataCallback;
            ma_proc AAudioStreamBuilder_setErrorCallback;
            ma_proc AAudioStreamBuilder_setPerformanceMode;
            ma_proc AAudioStreamBuilder_openStream;
            ma_proc AAudioStream_close;
            ma_proc AAudioStream_getState;
            ma_proc AAudioStream_waitForStateChange;
            ma_proc AAudioStream_getFormat;
            ma_proc AAudioStream_getChannelCount;
            ma_proc AAudioStream_getSampleRate;
            ma_proc AAudioStream_getBufferCapacityInFrames;
            ma_proc AAudioStream_getFramesPerDataCallback;
            ma_proc AAudioStream_getFramesPerBurst;
            ma_proc AAudioStream_requestStart;
            ma_proc AAudioStream_requestStop;
        } aaudio;
#endif
#ifdef MA_SUPPORT_OPENSL
        struct
        {
            ma_handle libOpenSLES;
            ma_handle SL_IID_ENGINE;
            ma_handle SL_IID_AUDIOIODEVICECAPABILITIES;
            ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
            ma_handle SL_IID_RECORD;
            ma_handle SL_IID_PLAY;
            ma_handle SL_IID_OUTPUTMIX;
            ma_proc   slCreateEngine;
        } opensl;
#endif
#ifdef MA_SUPPORT_WEBAUDIO
        struct
        {
            int _unused;
        } webaudio;
#endif
#ifdef MA_SUPPORT_NULL
        struct
        {
            int _unused;
        } null_backend;
#endif
    };

    union
    {
#ifdef MA_WIN32
        struct
        {
            /*HMODULE*/ ma_handle hOle32DLL;
            ma_proc CoInitializeEx;
            ma_proc CoUninitialize;
            ma_proc CoCreateInstance;
            ma_proc CoTaskMemFree;
            ma_proc PropVariantClear;
            ma_proc StringFromGUID2;

            /*HMODULE*/ ma_handle hUser32DLL;
            ma_proc GetForegroundWindow;
            ma_proc GetDesktopWindow;

            /*HMODULE*/ ma_handle hAdvapi32DLL;
            ma_proc RegOpenKeyExA;
            ma_proc RegCloseKey;
            ma_proc RegQueryValueExA;
        } win32;
#endif
#ifdef MA_POSIX
        struct
        {
            ma_handle pthreadSO;
            ma_proc pthread_create;
            ma_proc pthread_join;
            ma_proc pthread_mutex_init;
            ma_proc pthread_mutex_destroy;
            ma_proc pthread_mutex_lock;
            ma_proc pthread_mutex_unlock;
            ma_proc pthread_cond_init;
            ma_proc pthread_cond_destroy;
            ma_proc pthread_cond_wait;
            ma_proc pthread_cond_signal;
            ma_proc pthread_attr_init;
            ma_proc pthread_attr_destroy;
            ma_proc pthread_attr_setschedpolicy;
            ma_proc pthread_attr_getschedparam;
            ma_proc pthread_attr_setschedparam;
        } posix;

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

        case MA_DSERR_BUFFERTOOSMALL:                   return MA_NO_SPACE;
        case MA_DSERR_DS8_REQUIRED:                     return MA_INVALID_OPERATION;
        case MA_DSERR_SENDLOOP:                         return MA_DEADLOCK;
        case MA_DSERR_BADSENDBUFFERGUID:                return MA_INVALID_ARGS;
        case MA_DSERR_OBJECTNOTFOUND:                   return MA_NO_DEVICE;
        case MA_DSERR_FXUNAVAILABLE:                    return MA_UNAVAILABLE;

        default:                                        return MA_ERROR;
    }
}

typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD  dwCoInit);
typedef void    (WINAPI * MA_PFN_CoUninitialize)(void);
typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv);
typedef void    (WINAPI * MA_PFN_CoTaskMemFree)(LPVOID pv);
typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(PROPVARIANT *pvar);
typedef int     (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax);

typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void);
typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void);

/* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */
typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult);
typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);
typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
#endif


#define MA_STATE_UNINITIALIZED     0
#define MA_STATE_STOPPED           1   /* The device's default state after initialization. */
#define MA_STATE_STARTED           2   /* The worker thread is in it's main loop waiting for the driver to request or deliver audio data. */
#define MA_STATE_STARTING          3   /* Transitioning from a stopped state to started. */
#define MA_STATE_STOPPING          4   /* Transitioning from a started state to stopped. */

#define MA_DEFAULT_PLAYBACK_DEVICE_NAME    "Default Playback Device"
#define MA_DEFAULT_CAPTURE_DEVICE_NAME     "Default Capture Device"


MA_API const char* ma_log_level_to_string(ma_uint32 logLevel)
{
    switch (logLevel)
    {
        case MA_LOG_LEVEL_VERBOSE: return "";
        case MA_LOG_LEVEL_INFO:    return "INFO";
        case MA_LOG_LEVEL_WARNING: return "WARNING";
        case MA_LOG_LEVEL_ERROR:   return "ERROR";
        default:                   return "ERROR";
    }
}

/* Posts a log message. */
static void ma_post_log_message(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
{
    if (pContext == NULL) {
        if (pDevice != NULL) {
            pContext = pDevice->pContext;
        }
    }

    /* All logs must be output when debug output is enabled. */
#if defined(MA_DEBUG_OUTPUT)
    printf("%s: %s\n", ma_log_level_to_string(logLevel), message);
#endif

    if (pContext == NULL) {
        return;
    }
    
#if defined(MA_LOG_LEVEL)
    if (logLevel <= MA_LOG_LEVEL) {
        ma_log_proc onLog;

        onLog = pContext->logCallback;
        if (onLog) {
            onLog(pContext, pDevice, logLevel, message);
        }
    }
#endif
}

/*
We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a
logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf().
*/
#if defined(_MSC_VER) && _MSC_VER < 1900
int ma_vscprintf(const char* format, va_list args)
{
#if _MSC_VER > 1200
    return _vscprintf(format, args);
#else
    int result;
    char* pTempBuffer = NULL;
    size_t tempBufferCap = 1024;

    if (format == NULL) {
        errno = EINVAL;
        return -1;
    }

	for (;;) {
        char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, NULL);    /* TODO: Add support for custom memory allocators? */
        if (pNewTempBuffer == NULL) {
            ma_free(pTempBuffer, NULL);
            errno = ENOMEM;
            return -1;  /* Out of memory. */
        }

        pTempBuffer = pNewTempBuffer;

        result = _vsnprintf(pTempBuffer, tempBufferCap, format, args);
        ma_free(pTempBuffer, NULL);
        
        if (result != -1) {
            break;  /* Got it. */
        }

        /* Buffer wasn't big enough. Ideally it'd be nice to use an error code to know the reason for sure, but this is reliable enough. */
        tempBufferCap *= 2;
	}

    return result;

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

    pHandler->counter = 1;
    pHandler->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
    if (pHandler->hEvent == NULL) {
        return ma_result_from_GetLastError(GetLastError());
    }

    return MA_SUCCESS;
}

static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler)
{
    if (pHandler->hEvent != NULL) {
        CloseHandle(pHandler->hEvent);
    }
}

static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler)
{
    WaitForSingleObject(pHandler->hEvent, INFINITE);
}
#endif  /* !MA_WIN32_DESKTOP */

/* We need a virtual table for our notification client object that's used for detecting changes to the default device. */
#ifdef MA_WIN32_DESKTOP
static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject)
{
    /*
    We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else
    we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK.
    */
    if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) {
        *ppObject = NULL;
        return E_NOINTERFACE;
    }

    /* Getting here means the IID is IUnknown or IMMNotificationClient. */
    *ppObject = (void*)pThis;
    ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis);
    return S_OK;
}

static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis)
{
    return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1;
}

static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis)
{
    ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1;
    if (newRefCount == 0) {
        return 0;   /* We don't free anything here because we never allocate the object on the heap. */
    }

    return (ULONG)newRefCount;
}

static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState)
{
    ma_bool32 isThisDevice = MA_FALSE;

#ifdef MA_DEBUG_OUTPUT
    /*printf("IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);*/
#endif

    if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) {
        return S_OK;
    }

    /*
    There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect
    that the device is disabled or has been unplugged.
    */
    if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) {
        if (wcscmp(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) {
            isThisDevice = MA_TRUE;
        }
    }

    if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) {
        if (wcscmp(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) {
            isThisDevice = MA_TRUE;
        }
    }

    if (isThisDevice) {
        ma_device_stop(pThis->pDevice);
    }
    
    return S_OK;
}

static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
{
#ifdef MA_DEBUG_OUTPUT
    /*printf("IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
#endif

    /* We don't need to worry about this event for our purposes. */
    (void)pThis;
    (void)pDeviceID;
    return S_OK;
}

static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
{
#ifdef MA_DEBUG_OUTPUT
    /*printf("IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
#endif

    /* We don't need to worry about this event for our purposes. */
    (void)pThis;
    (void)pDeviceID;
    return S_OK;
}

static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID)
{
#ifdef MA_DEBUG_OUTPUT
    /*printf("IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/
#endif

    /* We only ever use the eConsole role in miniaudio. */
    if (role != ma_eConsole) {
        return S_OK;
    }

    /* We only care about devices with the same data flow and role as the current device. */
    if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) ||
        (pThis->pDevice->type == ma_device_type_capture  && dataFlow != ma_eCapture)) {
        return S_OK;
    }

    /* Don't do automatic stream routing if we're not allowed. */
    if ((dataFlow == ma_eRender  && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) ||
        (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting  == MA_FALSE)) {
        return S_OK;
    }

    /*
    Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to
    AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once
    it's fixed.
    */
    if ((dataFlow == ma_eRender  && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) ||
        (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode  == ma_share_mode_exclusive)) {
        return S_OK;
    }

    /*
    We don't change the device here - we change it in the worker thread to keep synchronization simple. To do this I'm just setting a flag to
    indicate that the default device has changed. Loopback devices are treated as capture devices so we need to do a bit of a dance to handle
    that properly.
    */
    if (dataFlow == ma_eRender  && pThis->pDevice->type != ma_device_type_loopback) {
        c89atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultPlaybackDeviceChanged, MA_TRUE);
    }
    if (dataFlow == ma_eCapture || pThis->pDevice->type == ma_device_type_loopback) {
        c89atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultCaptureDeviceChanged,  MA_TRUE);
    }

    (void)pDefaultDeviceID;
    return S_OK;
}

static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key)
{
#ifdef MA_DEBUG_OUTPUT
    /*printf("IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
#endif

    (void)pThis;
    (void)pDeviceID;
    (void)key;
    return S_OK;
}

static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = {
    ma_IMMNotificationClient_QueryInterface,
    ma_IMMNotificationClient_AddRef,
    ma_IMMNotificationClient_Release,
    ma_IMMNotificationClient_OnDeviceStateChanged,
    ma_IMMNotificationClient_OnDeviceAdded,
    ma_IMMNotificationClient_OnDeviceRemoved,
    ma_IMMNotificationClient_OnDefaultDeviceChanged,
    ma_IMMNotificationClient_OnPropertyValueChanged
};
#endif  /* MA_WIN32_DESKTOP */

#ifdef MA_WIN32_DESKTOP
typedef ma_IMMDevice ma_WASAPIDeviceInterface;
#else
typedef ma_IUnknown ma_WASAPIDeviceInterface;
#endif



static ma_bool32 ma_context_is_device_id_equal__wasapi(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 memcmp(pID0->wasapi, pID1->wasapi, sizeof(pID0->wasapi)) == 0;
}

static void ma_set_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, ma_device_info* pInfo)
{
    MA_ASSERT(pWF != NULL);
    MA_ASSERT(pInfo != NULL);

    pInfo->formatCount   = 1;
    pInfo->formats[0]    = ma_format_from_WAVEFORMATEX(pWF);
    pInfo->minChannels   = pWF->nChannels;
    pInfo->maxChannels   = pWF->nChannels;
    pInfo->minSampleRate = pWF->nSamplesPerSec;
    pInfo->maxSampleRate = pWF->nSamplesPerSec;
}

static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_share_mode shareMode, ma_device_info* pInfo)
{
    MA_ASSERT(pAudioClient != NULL);
    MA_ASSERT(pInfo != NULL);

    /* We use a different technique to retrieve the device information depending on whether or not we are using shared or exclusive mode. */
    if (shareMode == ma_share_mode_shared) {
        /* Shared Mode. We use GetMixFormat() here. */

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

                /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */
                ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);

            #ifdef MA_WIN32_DESKTOP
                hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient);
            #else
                hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient);
            #endif

                if (SUCCEEDED(hr)) {
                    hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
                }
            }
        }

        if (FAILED(hr)) {
            /* Failed to initialize in exclusive mode. Don't fall back to shared mode - instead tell the client about it. They can reinitialize in shared mode if they want. */
            if (hr == E_ACCESSDENIED) {
                errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED;
            } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
                errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY;
            } else {
                errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr);
            }
            goto done;
        }
    }

    if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) {
        /*
        Low latency shared mode via IAudioClient3.

        NOTE
        ====
        Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the
        use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using
        any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to
        that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM.
        */
#ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE
        if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.Format.nSamplesPerSec) {
            ma_IAudioClient3* pAudioClient3 = NULL;
            hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3);
            if (SUCCEEDED(hr)) {
                UINT32 defaultPeriodInFrames;
                UINT32 fundamentalPeriodInFrames;
                UINT32 minPeriodInFrames;
                UINT32 maxPeriodInFrames;
                hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames);
                if (SUCCEEDED(hr)) {
                    UINT32 desiredPeriodInFrames = pData->periodSizeInFramesOut;
                    UINT32 actualPeriodInFrames  = desiredPeriodInFrames;

                    /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */
                    actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames;
                    actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames;

                    /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */
                    actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames);

                #if defined(MA_DEBUG_OUTPUT)
                    printf("[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames);
                    printf("    defaultPeriodInFrames=%d\n", defaultPeriodInFrames);
                    printf("    fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames);
                    printf("    minPeriodInFrames=%d\n", minPeriodInFrames);
                    printf("    maxPeriodInFrames=%d\n", maxPeriodInFrames);
                #endif

                    /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */
                    if (actualPeriodInFrames >= desiredPeriodInFrames) {
                        /*
                        MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified,
                        IAudioClient3_InitializeSharedAudioStream() will fail.
                        */
                        hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (WAVEFORMATEX*)&wf, NULL);
                        if (SUCCEEDED(hr)) {
                            wasInitializedUsingIAudioClient3 = MA_TRUE;
                            pData->periodSizeInFramesOut = actualPeriodInFrames;
                        #if defined(MA_DEBUG_OUTPUT)
                            printf("[WASAPI] Using IAudioClient3\n");
                            printf("    periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut);
                        #endif
                        } else {
                        #if defined(MA_DEBUG_OUTPUT)
                            printf("[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n");
                        #endif    
                        }
                    } else {
                    #if defined(MA_DEBUG_OUTPUT)
                        printf("[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n");
                    #endif
                    }
                } else {
                #if defined(MA_DEBUG_OUTPUT)
                    printf("[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n");
                #endif
                }

                ma_IAudioClient3_Release(pAudioClient3);
                pAudioClient3 = NULL;
            }
        }
#else
    #if defined(MA_DEBUG_OUTPUT)
        printf("[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n");
    #endif
#endif

        /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */
        if (!wasInitializedUsingIAudioClient3) {
            MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10;   /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */
            hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL);
            if (FAILED(hr)) {
                if (hr == E_ACCESSDENIED) {
                    errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED;
                } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
                    errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY;
                } else {
                    errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr);
                }

                goto done;
            }
        }
    }

    if (!wasInitializedUsingIAudioClient3) {
        ma_uint32 bufferSizeInFrames;
        hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
        if (FAILED(hr)) {
            errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr);
            goto done;
        }

        pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut;
    }

    pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;

    if (deviceType == ma_device_type_playback) {
        hr = ma_IAudioClient_GetService((ma_IAudioClient*)pData->pAudioClient, &MA_IID_IAudioRenderClient, (void**)&pData->pRenderClient);
    } else {
        hr = ma_IAudioClient_GetService((ma_IAudioClient*)pData->pAudioClient, &MA_IID_IAudioCaptureClient, (void**)&pData->pCaptureClient);
    }

    if (FAILED(hr)) {
        errorMsg = "[WASAPI] Failed to get audio client service.", result = ma_result_from_HRESULT(hr);
        goto done;
    }


    /* Grab the name of the device. */
#ifdef MA_WIN32_DESKTOP
    {
        ma_IPropertyStore *pProperties;
        hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties);
        if (SUCCEEDED(hr)) {
            PROPVARIANT varName;
            ma_PropVariantInit(&varName);
            hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName);
            if (SUCCEEDED(hr)) {
                WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE);
                ma_PropVariantClear(pContext, &varName);
            }

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

    MA_ASSERT(pDevice != NULL);
    MA_ASSERT(pFrameCount != NULL);
    
    *pFrameCount = 0;

    if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) {
        return MA_INVALID_OPERATION;
    }

    hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount);
    if (FAILED(hr)) {
        return ma_result_from_HRESULT(hr);
    }

    /* Slightly different rules for exclusive and shared modes. */
    shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode;
    if (shareMode == ma_share_mode_exclusive) {
        *pFrameCount = paddingFramesCount;
    } else {
        if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
            *pFrameCount = pDevice->wasapi.actualPeriodSizeInFramesPlayback - paddingFramesCount;
        } else {
            *pFrameCount = paddingFramesCount;
        }
    }

    return MA_SUCCESS;
}

static ma_bool32 ma_device_is_reroute_required__wasapi(ma_device* pDevice, ma_device_type deviceType)
{
    MA_ASSERT(pDevice != NULL);

    if (deviceType == ma_device_type_playback) {
        return pDevice->wasapi.hasDefaultPlaybackDeviceChanged;
    }

    if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
        return pDevice->wasapi.hasDefaultCaptureDeviceChanged;
    }
    
    return MA_FALSE;
}

static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType)
{
    ma_result result;

    if (deviceType == ma_device_type_duplex) {
        return MA_INVALID_ARGS;
    }

    if (deviceType == ma_device_type_playback) {
        c89atomic_exchange_32(&pDevice->wasapi.hasDefaultPlaybackDeviceChanged, MA_FALSE);
    }
    if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
        c89atomic_exchange_32(&pDevice->wasapi.hasDefaultCaptureDeviceChanged,  MA_FALSE);
    }
    

    #ifdef MA_DEBUG_OUTPUT
        printf("=== CHANGING DEVICE ===\n");
    #endif

    result = ma_device_reinit__wasapi(pDevice, deviceType);
    if (result != MA_SUCCESS) {
        return result;
    }

    ma_device__post_init_setup(pDevice, deviceType);

    return MA_SUCCESS;
}


static ma_result ma_device_stop__wasapi(ma_device* pDevice)
{
    MA_ASSERT(pDevice != NULL);

    /*
    It's possible for the main loop to get stuck if the device is disconnected.
    
    In loopback mode it's possible for WaitForSingleObject() to get stuck in a deadlock when nothing is being played. When nothing
    is being played, the event is never signalled internally by WASAPI which means we will deadlock when stopping the device.
    */
    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
        SetEvent((HANDLE)pDevice->wasapi.hEventCapture);
    }

    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
        SetEvent((HANDLE)pDevice->wasapi.hEventPlayback);
    }

    return MA_SUCCESS;
}


static ma_result ma_device_main_loop__wasapi(ma_device* pDevice)
{
    ma_result result;
    HRESULT hr;
    ma_bool32 exitLoop = MA_FALSE;
    ma_uint32 framesWrittenToPlaybackDevice = 0;
    ma_uint32 mappedDeviceBufferSizeInFramesCapture = 0;
    ma_uint32 mappedDeviceBufferSizeInFramesPlayback = 0;
    ma_uint32 mappedDeviceBufferFramesRemainingCapture = 0;
    ma_uint32 mappedDeviceBufferFramesRemainingPlayback = 0;
    BYTE* pMappedDeviceBufferCapture = NULL;
    BYTE* pMappedDeviceBufferPlayback = NULL;
    ma_uint32 bpfCaptureDevice = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
    ma_uint32 bpfPlaybackDevice = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
    ma_uint32 bpfCaptureClient = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
    ma_uint32 bpfPlaybackClient = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
    ma_uint8  inputDataInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
    ma_uint32 inputDataInClientFormatCap = sizeof(inputDataInClientFormat) / bpfCaptureClient;
    ma_uint8  outputDataInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
    ma_uint32 outputDataInClientFormatCap = sizeof(outputDataInClientFormat) / bpfPlaybackClient;
    ma_uint32 outputDataInClientFormatCount = 0;
    ma_uint32 outputDataInClientFormatConsumed = 0;
    ma_uint32 periodSizeInFramesCapture = 0;

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

                        }

                        continue;
                    }

                    /* We're ready to map the playback device's buffer. We don't release this until it's been entirely filled. */
                    hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedDeviceBufferPlayback);
                    if (FAILED(hr)) {
                        ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
                        exitLoop = MA_TRUE;
                        break;
                    }

                    mappedDeviceBufferSizeInFramesPlayback    = framesAvailablePlayback;
                    mappedDeviceBufferFramesRemainingPlayback = framesAvailablePlayback;
                }

                /* At this point we should have a buffer available for output. We need to keep writing input samples to it. */
                for (;;) {
                    /* Try grabbing some captured data if we haven't already got a mapped buffer. */
                    if (pMappedDeviceBufferCapture == NULL) {
                        if (pDevice->capture.shareMode == ma_share_mode_shared) {
                            if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
                                return MA_ERROR;   /* Wait failed. */
                            }
                        }

                        result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
                        if (result != MA_SUCCESS) {
                            exitLoop = MA_TRUE;
                            break;
                        }

                        /*printf("TRACE 2: framesAvailableCapture=%d\n", framesAvailableCapture);*/

                        /* Wait for more if nothing is available. */
                        if (framesAvailableCapture == 0) {
                            /* In exclusive mode we waited at the top. */
                            if (pDevice->capture.shareMode != ma_share_mode_shared) {
                                if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
                                    return MA_ERROR;   /* Wait failed. */
                                }
                            }

                            continue;
                        }

                        /* Getting here means there's data available for writing to the output device. */
                        mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
                        hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
                        if (FAILED(hr)) {
                            ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
                            exitLoop = MA_TRUE;
                            break;
                        }


                        /* Overrun detection. */
                        if ((flagsCapture & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
                            /* Glitched. Probably due to an overrun. */
                        #ifdef MA_DEBUG_OUTPUT
                            printf("[WASAPI] Data discontinuity (possible overrun). framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
                        #endif

                            /*
                            Exeriment: If we get an overrun it probably means we're straddling the end of the buffer. In order to prevent a never-ending sequence of glitches let's experiment
                            by dropping every frame until we're left with only a single period. To do this we just keep retrieving and immediately releasing buffers until we're down to the
                            last period.
                            */
                            if (framesAvailableCapture >= pDevice->wasapi.actualPeriodSizeInFramesCapture) {
                            #ifdef MA_DEBUG_OUTPUT
                                printf("[WASAPI] Synchronizing capture stream. ");
                            #endif
                                do
                                {
                                    hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
                                    if (FAILED(hr)) {
                                        break;
                                    }

                                    framesAvailableCapture -= mappedDeviceBufferSizeInFramesCapture;
                                    
                                    if (framesAvailableCapture > 0) {
                                        mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
                                        hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
                                        if (FAILED(hr)) {
                                            ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
                                            exitLoop = MA_TRUE;
                                            break;
                                        }
                                    } else {
                                        pMappedDeviceBufferCapture = NULL;
                                        mappedDeviceBufferSizeInFramesCapture = 0;
                                    }
                                } while (framesAvailableCapture > periodSizeInFramesCapture);
                            #ifdef MA_DEBUG_OUTPUT
                                printf("framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
                            #endif
                            }
                        } else {
                        #ifdef MA_DEBUG_OUTPUT
                            if (flagsCapture != 0) {
                                printf("[WASAPI] Capture Flags: %ld\n", flagsCapture);
                            }
                        #endif
                        }

                        mappedDeviceBufferFramesRemainingCapture = mappedDeviceBufferSizeInFramesCapture;
                    }


                    /* At this point we should have both input and output data available. We now need to convert the data and post it to the client. */
                    for (;;) {
                        BYTE* pRunningDeviceBufferCapture;
                        BYTE* pRunningDeviceBufferPlayback;
                        ma_uint32 framesToProcess;
                        ma_uint32 framesProcessed;

                        pRunningDeviceBufferCapture  = pMappedDeviceBufferCapture  + ((mappedDeviceBufferSizeInFramesCapture  - mappedDeviceBufferFramesRemainingCapture ) * bpfCaptureDevice);
                        pRunningDeviceBufferPlayback = pMappedDeviceBufferPlayback + ((mappedDeviceBufferSizeInFramesPlayback - mappedDeviceBufferFramesRemainingPlayback) * bpfPlaybackDevice);
                        
                        /* There may be some data sitting in the converter that needs to be processed first. Once this is exhaused, run the data callback again. */
                        if (!pDevice->playback.converter.isPassthrough && outputDataInClientFormatConsumed < outputDataInClientFormatCount) {
                            ma_uint64 convertedFrameCountClient = (outputDataInClientFormatCount - outputDataInClientFormatConsumed);
                            ma_uint64 convertedFrameCountDevice = mappedDeviceBufferFramesRemainingPlayback;
                            void* pConvertedFramesClient = outputDataInClientFormat + (outputDataInClientFormatConsumed * bpfPlaybackClient);
                            void* pConvertedFramesDevice = pRunningDeviceBufferPlayback;
                            result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesClient, &convertedFrameCountClient, pConvertedFramesDevice, &convertedFrameCountDevice);
                            if (result != MA_SUCCESS) {
                                break;
                            }

                            outputDataInClientFormatConsumed          += (ma_uint32)convertedFrameCountClient;  /* Safe cast. */
                            mappedDeviceBufferFramesRemainingPlayback -= (ma_uint32)convertedFrameCountDevice;  /* Safe cast. */

                            if (mappedDeviceBufferFramesRemainingPlayback == 0) {
                                break;
                            }
                        }

                        /*
                        Getting here means we need to fire the callback. If format conversion is unnecessary, we can optimize this by passing the pointers to the internal
                        buffers directly to the callback.
                        */
                        if (pDevice->capture.converter.isPassthrough && pDevice->playback.converter.isPassthrough) {
                            /* Optimal path. We can pass mapped pointers directly to the callback. */
                            framesToProcess = ma_min(mappedDeviceBufferFramesRemainingCapture, mappedDeviceBufferFramesRemainingPlayback);
                            framesProcessed = framesToProcess;

                            ma_device__on_data(pDevice, pRunningDeviceBufferPlayback, pRunningDeviceBufferCapture, framesToProcess);

                            mappedDeviceBufferFramesRemainingCapture  -= framesProcessed;
                            mappedDeviceBufferFramesRemainingPlayback -= framesProcessed;

                            if (mappedDeviceBufferFramesRemainingCapture == 0) {
                                break;  /* Exhausted input data. */
                            }
                            if (mappedDeviceBufferFramesRemainingPlayback == 0) {
                                break;  /* Exhausted output data. */
                            }
                        } else if (pDevice->capture.converter.isPassthrough) {

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

                    mappedDeviceBufferSizeInFramesPlayback    = 0;
                }

                if (!pDevice->wasapi.isStartedPlayback) {
                    ma_uint32 startThreshold = pDevice->playback.internalPeriodSizeInFrames * 1;

                    /* Prevent a deadlock. If we don't clamp against the actual buffer size we'll never end up starting the playback device which will result in a deadlock. */
                    if (startThreshold > pDevice->wasapi.actualPeriodSizeInFramesPlayback) {
                        startThreshold = pDevice->wasapi.actualPeriodSizeInFramesPlayback;
                    }

                    if (pDevice->playback.shareMode == ma_share_mode_exclusive || framesWrittenToPlaybackDevice >= startThreshold) {
                        hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
                        if (FAILED(hr)) {
                            ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
                            ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
                            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", ma_result_from_HRESULT(hr));
                        }
                        c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
                    }
                }
            } break;



            case ma_device_type_capture:
            case ma_device_type_loopback:
            {
                ma_uint32 framesAvailableCapture;
                DWORD flagsCapture;    /* Passed to IAudioCaptureClient_GetBuffer(). */

                /* Wait for data to become available first. */
                if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
                    exitLoop = MA_TRUE;
                    break;   /* Wait failed. */
                }

                /* See how many frames are available. Since we waited at the top, I don't think this should ever return 0. I'm checking for this anyway. */
                result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
                if (result != MA_SUCCESS) {
                    exitLoop = MA_TRUE;
                    break;
                }

                if (framesAvailableCapture < pDevice->wasapi.periodSizeInFramesCapture) {
                    continue;   /* Nothing available. Keep waiting. */
                }

                /* Map the data buffer in preparation for sending to the client. */
                mappedDeviceBufferSizeInFramesCapture = framesAvailableCapture;
                hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
                if (FAILED(hr)) {
                    ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
                    exitLoop = MA_TRUE;
                    break;
                }

                /* Overrun detection. */
                if ((flagsCapture & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
                    /* Glitched. Probably due to an overrun. */
                #ifdef MA_DEBUG_OUTPUT
                    printf("[WASAPI] Data discontinuity (possible overrun). framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
                #endif

                    /*
                    Exeriment: If we get an overrun it probably means we're straddling the end of the buffer. In order to prevent a never-ending sequence of glitches let's experiment
                    by dropping every frame until we're left with only a single period. To do this we just keep retrieving and immediately releasing buffers until we're down to the
                    last period.
                    */
                    if (framesAvailableCapture >= pDevice->wasapi.actualPeriodSizeInFramesCapture) {
                    #ifdef MA_DEBUG_OUTPUT
                        printf("[WASAPI] Synchronizing capture stream. ");
                    #endif
                        do
                        {
                            hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
                            if (FAILED(hr)) {
                                break;
                            }

                            framesAvailableCapture -= mappedDeviceBufferSizeInFramesCapture;
                                    
                            if (framesAvailableCapture > 0) {
                                mappedDeviceBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
                                hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedDeviceBufferCapture, &mappedDeviceBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
                                if (FAILED(hr)) {
                                    ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
                                    exitLoop = MA_TRUE;
                                    break;
                                }
                            } else {
                                pMappedDeviceBufferCapture = NULL;
                                mappedDeviceBufferSizeInFramesCapture = 0;
                            }
                        } while (framesAvailableCapture > periodSizeInFramesCapture);
                    #ifdef MA_DEBUG_OUTPUT
                        printf("framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedDeviceBufferSizeInFramesCapture);
                    #endif
                    }
                } else {
                #ifdef MA_DEBUG_OUTPUT
                    if (flagsCapture != 0) {
                        printf("[WASAPI] Capture Flags: %ld\n", flagsCapture);
                    }
                #endif
                }

                /* We should have a buffer at this point, but let's just do a sanity check anyway. */
                if (mappedDeviceBufferSizeInFramesCapture > 0 && pMappedDeviceBufferCapture != NULL) {
                    ma_device__send_frames_to_client(pDevice, mappedDeviceBufferSizeInFramesCapture, pMappedDeviceBufferCapture);

                    /* At this point we're done with the buffer. */
                    hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedDeviceBufferSizeInFramesCapture);
                    pMappedDeviceBufferCapture = NULL;    /* <-- Important. Not doing this can result in an error once we leave this loop because it will use this to know whether or not a final ReleaseBuffer() needs to be called. */
                    mappedDeviceBufferSizeInFramesCapture = 0;
                    if (FAILED(hr)) {
                        ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from capture device after reading from the device.", ma_result_from_HRESULT(hr));
                        exitLoop = MA_TRUE;
                        break;
                    }
                }
            } break;



            case ma_device_type_playback:
            {
                ma_uint32 framesAvailablePlayback;

                /* Wait for space to become available first. */
                if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
                    exitLoop = MA_TRUE;
                    break;   /* Wait failed. */
                }

                /* Check how much space is available. If this returns 0 we just keep waiting. */
                result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
                if (result != MA_SUCCESS) {
                    exitLoop = MA_TRUE;
                    break;
                }

                if (framesAvailablePlayback < pDevice->wasapi.periodSizeInFramesPlayback) {
                    continue;   /* No space available. */
                }

                /* Map a the data buffer in preparation for the callback. */
                hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedDeviceBufferPlayback);
                if (FAILED(hr)) {
                    ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
                    exitLoop = MA_TRUE;
                    break;
                }

                /* We should have a buffer at this point. */
                ma_device__read_frames_from_client(pDevice, framesAvailablePlayback, pMappedDeviceBufferPlayback);

                /* At this point we're done writing to the device and we just need to release the buffer. */
                hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, 0);
                pMappedDeviceBufferPlayback = NULL;    /* <-- Important. Not doing this can result in an error once we leave this loop because it will use this to know whether or not a final ReleaseBuffer() needs to be called. */
                mappedDeviceBufferSizeInFramesPlayback = 0;

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

                    ma_sleep(waitTimeInMilliseconds);
                    continue; /* Nothing is available in the capture buffer. */
                }

                hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
                if (FAILED(hr)) {
                    return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
                }


                /* At this point we have some input data that we need to output. We do not return until every mapped frame of the input data is written to the playback device. */
                mappedDeviceFramesProcessedCapture = 0;

                for (;;) {  /* Keep writing to the playback device. */
                    ma_uint8  inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
                    ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
                    ma_uint8  outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
                    ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
                    ma_uint32 outputFramesInClientFormatCount;
                    ma_uint32 outputFramesInClientFormatConsumed = 0;
                    ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap);
                    ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture;
                    void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture);

                    result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess);
                    if (result != MA_SUCCESS) {
                        break;
                    }

                    outputFramesInClientFormatCount     = (ma_uint32)clientCapturedFramesToProcess;
                    mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess;

                    ma_device__on_data(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess);

                    /* At this point we have input and output data in client format. All we need to do now is convert it to the output device format. This may take a few passes. */
                    for (;;) {
                        ma_uint32 framesWrittenThisIteration;
                        DWORD physicalPlayCursorInBytes;
                        DWORD physicalWriteCursorInBytes;
                        DWORD availableBytesPlayback;
                        DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */

                        /* We need the physical play and write cursors. */
                        if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
                            break;
                        }

                        if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
                            physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
                        }
                        prevPlayCursorInBytesPlayback  = physicalPlayCursorInBytes;

                        /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
                        if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
                            /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
                            if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
                                availableBytesPlayback  = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
                                availableBytesPlayback += physicalPlayCursorInBytes;    /* Wrap around. */
                            } else {
                                /* This is an error. */
                            #ifdef MA_DEBUG_OUTPUT
                                printf("[DirectSound] (Duplex/Playback) WARNING: Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, v...
                            #endif
                                availableBytesPlayback = 0;
                            }
                        } else {
                            /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
                            if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
                                availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
                            } else {
                                /* This is an error. */
                            #ifdef MA_DEBUG_OUTPUT
                                printf("[DirectSound] (Duplex/Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, v...
                            #endif
                                availableBytesPlayback = 0;
                            }
                        }

                    #ifdef MA_DEBUG_OUTPUT
                        /*printf("[DirectSound] (Duplex/Playback) physicalPlayCursorInBytes=%d, availableBytesPlayback=%d\n", physicalPlayCursorInBytes, availableBytesPlayback);*/
                    #endif

                        /* If there's no room available for writing we need to wait for more. */
                        if (availableBytesPlayback == 0) {
                            /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
                            if (!isPlaybackDeviceStarted) {
                                hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
                                if (FAILED(hr)) {
                                    ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
                                    return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
                                }
                                isPlaybackDeviceStarted = MA_TRUE;
                            } else {
                                ma_sleep(waitTimeInMilliseconds);
                                continue;
                            }
                        }


                        /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */
                        lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
                        if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
                            /* Same loop iteration. Go up to the end of the buffer. */
                            lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
                        } else {
                            /* Different loop iterations. Go up to the physical play cursor. */
                            lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
                        }

                        hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
                        if (FAILED(hr)) {
                            result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
                            break;
                        }

                        /*
                        Experiment: If the playback buffer is being starved, pad it with some silence to get it back in sync. This will cause a glitch, but it may prevent
                        endless glitching due to it constantly running out of data.
                        */
                        if (isPlaybackDeviceStarted) {
                            DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback;
                            if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) {
                                silentPaddingInBytes   = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback;
                                if (silentPaddingInBytes > lockSizeInBytesPlayback) {
                                    silentPaddingInBytes = lockSizeInBytesPlayback;
                                }

                        #ifdef MA_DEBUG_OUTPUT
                                printf("[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes);
                        #endif
                            }
                        }

                        /* At this point we have a buffer for output. */
                        if (silentPaddingInBytes > 0) {
                            MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes);
                            framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback;
                        } else {
                            ma_uint64 convertedFrameCountIn  = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed);
                            ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback;
                            void* pConvertedFramesIn  = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback);
                            void* pConvertedFramesOut = pMappedDeviceBufferPlayback;

                            result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut);
                            if (result != MA_SUCCESS) {
                                break;
                            }

                            outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut;
                            framesWrittenThisIteration          = (ma_uint32)convertedFrameCountOut;
                        }
                        

                        hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0);
                        if (FAILED(hr)) {
                            result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", ma_result_from_HRESULT(hr));
                            break;
                        }

                        virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback;
                        if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) {
                            virtualWriteCursorInBytesPlayback  = 0;
                            virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
                        }
                        
                        /*
                        We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds
                        a bit of a buffer to prevent the playback buffer from getting starved.
                        */
                        framesWrittenToPlaybackDevice += framesWrittenThisIteration;
                        if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) {
                            hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
                            if (FAILED(hr)) {
                                ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
                                return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
                            }
                            isPlaybackDeviceStarted = MA_TRUE;
                        }

                        if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) {
                            break;  /* We're finished with the output data.*/
                        }
                    }

                    if (clientCapturedFramesToProcess == 0) {
                        break;  /* We just consumed every input sample. */
                    }
                }


                /* At this point we're done with the mapped portion of the capture buffer. */
                hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
                if (FAILED(hr)) {
                    return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", ma_result_from_HRESULT(hr));
                }
                prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture);
            } break;



            case ma_device_type_capture:
            {
                DWORD physicalCaptureCursorInBytes;
                DWORD physicalReadCursorInBytes;
                hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
                if (FAILED(hr)) {
                    return MA_ERROR;
                }

                /* If the previous capture position is the same as the current position we need to wait a bit longer. */
                if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) {
                    ma_sleep(waitTimeInMilliseconds);
                    continue;
                }

                /* Getting here means we have capture data available. */
                if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
                    /* The capture position has not looped. This is the simple case. */
                    lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
                    lockSizeInBytesCapture   = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
                } else {
                    /*
                    The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
                    do it again from the start.
                    */
                    if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
                        /* Lock up to the end of the buffer. */
                        lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
                        lockSizeInBytesCapture   = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
                    } else {
                        /* Lock starting from the start of the buffer. */
                        lockOffsetInBytesCapture = 0;
                        lockSizeInBytesCapture   = physicalReadCursorInBytes;
                    }
                }

            #ifdef MA_DEBUG_OUTPUT
                /*printf("[DirectSound] (Capture) physicalCaptureCursorInBytes=%d, physicalReadCursorInBytes=%d\n", physicalCaptureCursorInBytes, physicalReadCursorInBytes);*/
                /*printf("[DirectSound] (Capture) lockOffsetInBytesCapture=%d, lockSizeInBytesCapture=%d\n", lockOffsetInBytesCapture, lockSizeInBytesCapture);*/
            #endif

                if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) {
                    ma_sleep(waitTimeInMilliseconds);
                    continue; /* Nothing is available in the capture buffer. */
                }

                hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
                if (FAILED(hr)) {
                    return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
                }

            #ifdef MA_DEBUG_OUTPUT
                if (lockSizeInBytesCapture != mappedSizeInBytesCapture) {
                    printf("[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture);
                }
            #endif

                ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture);

                hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
                if (FAILED(hr)) {
                    return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", ma_result_from_HRESULT(hr));
                }
                prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture;

                if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) {
                    prevReadCursorInBytesCapture = 0;
                }
            } break;



            case ma_device_type_playback:
            {
                DWORD availableBytesPlayback;
                DWORD physicalPlayCursorInBytes;
                DWORD physicalWriteCursorInBytes;
                hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
                if (FAILED(hr)) {
                    break;
                }

                if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
                    physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
                }
                prevPlayCursorInBytesPlayback  = physicalPlayCursorInBytes;

                /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
                if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
                    /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
                    if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
                        availableBytesPlayback  = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
                        availableBytesPlayback += physicalPlayCursorInBytes;    /* Wrap around. */
                    } else {
                        /* This is an error. */
                    #ifdef MA_DEBUG_OUTPUT
                        printf("[DirectSound] (Playback) WARNING: Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCurs...
                    #endif
                        availableBytesPlayback = 0;
                    }
                } else {
                    /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
                    if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
                        availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
                    } else {
                        /* This is an error. */
                    #ifdef MA_DEBUG_OUTPUT
                        printf("[DirectSound] (Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCurs...
                    #endif
                        availableBytesPlayback = 0;
                    }
                }

            #ifdef MA_DEBUG_OUTPUT
                /*printf("[DirectSound] (Playback) physicalPlayCursorInBytes=%d, availableBytesPlayback=%d\n", physicalPlayCursorInBytes, availableBytesPlayback);*/
            #endif

                /* If there's no room available for writing we need to wait for more. */
                if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) {
                    /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
                    if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) {
                        hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
                        if (FAILED(hr)) {
                            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
                        }
                        isPlaybackDeviceStarted = MA_TRUE;
                    } else {
                        ma_sleep(waitTimeInMilliseconds);
                        continue;
                    }
                }

                /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */
                lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
                if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
                    /* Same loop iteration. Go up to the end of the buffer. */
                    lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
                } else {
                    /* Different loop iterations. Go up to the physical play cursor. */
                    lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
                }

                hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
                if (FAILED(hr)) {
                    result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.", ma_result_from_HRESULT(hr));
                    break;
                }

                /* At this point we have a buffer for output. */
                ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback);

                hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0);
                if (FAILED(hr)) {
                    result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", ma_result_from_HRESULT(hr));
                    break;
                }

                virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback;
                if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) {
                    virtualWriteCursorInBytesPlayback  = 0;
                    virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
                }
                        
                /*
                We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds
                a bit of a buffer to prevent the playback buffer from getting starved.
                */
                framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback;
                if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) {
                    hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
                    if (FAILED(hr)) {
                        return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", ma_result_from_HRESULT(hr));
                    }
                    isPlaybackDeviceStarted = MA_TRUE;

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

        pDevice->alsa.pPCMPlayback                   = (ma_ptr)pPCM;
        pDevice->alsa.isUsingMMapPlayback            = isUsingMMap;
        pDevice->playback.internalFormat             = internalFormat;
        pDevice->playback.internalChannels           = internalChannels;
        pDevice->playback.internalSampleRate         = internalSampleRate;
        ma_channel_map_copy(pDevice->playback.internalChannelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS));
        pDevice->playback.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
        pDevice->playback.internalPeriods            = internalPeriods;
    }

    return MA_SUCCESS;
}

static ma_result ma_device_init__alsa(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
{
    MA_ASSERT(pDevice != NULL);

    MA_ZERO_OBJECT(&pDevice->alsa);

    if (pConfig->deviceType == ma_device_type_loopback) {
        return MA_DEVICE_TYPE_NOT_SUPPORTED;
    }

    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
        ma_result result = ma_device_init_by_type__alsa(pContext, pConfig, ma_device_type_capture, pDevice);
        if (result != MA_SUCCESS) {
            return result;
        }
    }

    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
        ma_result result = ma_device_init_by_type__alsa(pContext, pConfig, ma_device_type_playback, pDevice);
        if (result != MA_SUCCESS) {
            return result;
        }
    }

    return MA_SUCCESS;
}

static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead)
{
    ma_snd_pcm_sframes_t resultALSA;

    MA_ASSERT(pDevice != NULL);
    MA_ASSERT(pFramesOut != NULL);

    if (pFramesRead != NULL) {
        *pFramesRead = 0;
    }

    for (;;) {
        resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
        if (resultALSA >= 0) {
            break;  /* Success. */
        } else {
            if (resultALSA == -EAGAIN) {
                /*printf("TRACE: EGAIN (read)\n");*/
                continue;   /* Try again. */
            } else if (resultALSA == -EPIPE) {
            #if defined(MA_DEBUG_OUTPUT)
                printf("TRACE: EPIPE (read)\n");
            #endif

                /* Overrun. Recover and try again. If this fails we need to return an error. */
                resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE);
                if (resultALSA < 0) {
                    return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.", ma_result_from_errno((int)-resultALSA));
                }

                resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
                if (resultALSA < 0) {
                    return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", ma_result_from_errno((int)-resultALSA));
                }

                resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
                if (resultALSA < 0) {
                    return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to read data from the internal device.", ma_result_from_errno((int)-resultALSA));
                }
            }
        }
    }

    if (pFramesRead != NULL) {
        *pFramesRead = resultALSA;
    }

    return MA_SUCCESS;
}

static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
{
    ma_snd_pcm_sframes_t resultALSA;

    MA_ASSERT(pDevice != NULL);
    MA_ASSERT(pFrames != NULL);

    if (pFramesWritten != NULL) {
        *pFramesWritten = 0;
    }

    for (;;) {
        resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
        if (resultALSA >= 0) {
            break;  /* Success. */
        } else {
            if (resultALSA == -EAGAIN) {
                /*printf("TRACE: EGAIN (write)\n");*/
                continue;   /* Try again. */
            } else if (resultALSA == -EPIPE) {
            #if defined(MA_DEBUG_OUTPUT)
                printf("TRACE: EPIPE (write)\n");
            #endif

                /* Underrun. Recover and try again. If this fails we need to return an error. */
                resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE);
                if (resultALSA < 0) { /* MA_TRUE=silent (don't print anything on error). */
                    return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", ma_result_from_errno((int)-resultALSA));
                }

                /*
                In my testing I have had a situation where writei() does not automatically restart the device even though I've set it
                up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of
                frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure
                if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't
                quite right here.
                */
                resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
                if (resultALSA < 0) {
                    return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", ma_result_from_errno((int)-resultALSA));
                }

                resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
                if (resultALSA < 0) {
                    return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to write data to device after underrun.", ma_result_from_errno((int)-resultALSA));
                }
            }
        }
    }

    if (pFramesWritten != NULL) {
        *pFramesWritten = resultALSA;
    }

    return MA_SUCCESS;
}

static ma_result ma_device_main_loop__alsa(ma_device* pDevice)
{
    ma_result result = MA_SUCCESS;
    int resultALSA;
    ma_bool32 exitLoop = MA_FALSE;

    MA_ASSERT(pDevice != NULL);

    /* Capture devices need to be started immediately. */
    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
        resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
        if (resultALSA < 0) {
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device in preparation for reading.", ma_result_from_errno(-resultALSA));
        }
    }

    while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
        switch (pDevice->type)
        {
            case ma_device_type_duplex:
            {
                if (pDevice->alsa.isUsingMMapCapture || pDevice->alsa.isUsingMMapPlayback) {
                    /* MMAP */
                    return MA_INVALID_OPERATION;    /* Not yet implemented. */

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


                        result = ma_device_read__alsa(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
                        if (result != MA_SUCCESS) {
                            exitLoop = MA_TRUE;
                            break;
                        }

                        ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);

                        framesReadThisPeriod += framesProcessed;
                    }
                }
            } break;

            case ma_device_type_playback:
            {
                if (pDevice->alsa.isUsingMMapPlayback) {
                    /* MMAP */
                    return MA_INVALID_OPERATION;    /* Not yet implemented. */
                } else {
                    /* writei() */

                    /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
                    ma_uint8 intermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
                    ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
                    ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
                    ma_uint32 framesWrittenThisPeriod = 0;
                    while (framesWrittenThisPeriod < periodSizeInFrames) {
                        ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
                        ma_uint32 framesProcessed;
                        ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
                        if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
                            framesToWriteThisIteration = intermediaryBufferSizeInFrames;
                        }

                        ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);

                        result = ma_device_write__alsa(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
                        if (result != MA_SUCCESS) {
                            exitLoop = MA_TRUE;
                            break;
                        }

                        framesWrittenThisPeriod += framesProcessed;
                    }
                }
            } break;

            /* To silence a warning. Will never hit this. */
            case ma_device_type_loopback:
            default: break;
        } 
    }

    /* Here is where the device needs to be stopped. */
    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
        ((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);

        /* We need to prepare the device again, otherwise we won't be able to restart the device. */
        if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
    #ifdef MA_DEBUG_OUTPUT
            printf("[ALSA] Failed to prepare capture device after stopping.\n");
    #endif
        }
    }

    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
        ((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);

        /* We need to prepare the device again, otherwise we won't be able to restart the device. */
        if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
    #ifdef MA_DEBUG_OUTPUT
            printf("[ALSA] Failed to prepare playback device after stopping.\n");
    #endif
        }
    }

    return result;
}

static ma_result ma_context_uninit__alsa(ma_context* pContext)
{
    MA_ASSERT(pContext != NULL);
    MA_ASSERT(pContext->backend == ma_backend_alsa);

    /* Clean up memory for memory leak checkers. */
    ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)();

#ifndef MA_NO_RUNTIME_LINKING
    ma_dlclose(pContext, pContext->alsa.asoundSO);
#endif

    ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock);

    return MA_SUCCESS;
}

static ma_result ma_context_init__alsa(const ma_context_config* pConfig, ma_context* pContext)
{
#ifndef MA_NO_RUNTIME_LINKING
    const char* libasoundNames[] = {
        "libasound.so.2",
        "libasound.so"
    };
    size_t i;

    for (i = 0; i < ma_countof(libasoundNames); ++i) {
        pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]);
        if (pContext->alsa.asoundSO != NULL) {
            break;
        }
    }

    if (pContext->alsa.asoundSO == NULL) {
#ifdef MA_DEBUG_OUTPUT
        printf("[ALSA] Failed to open shared object.\n");
#endif
        return MA_NO_BACKEND;
    }

    pContext->alsa.snd_pcm_open                           = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open");
    pContext->alsa.snd_pcm_close                          = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close");
    pContext->alsa.snd_pcm_hw_params_sizeof               = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof");
    pContext->alsa.snd_pcm_hw_params_any                  = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any");
    pContext->alsa.snd_pcm_hw_params_set_format           = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format");
    pContext->alsa.snd_pcm_hw_params_set_format_first     = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first");
    pContext->alsa.snd_pcm_hw_params_get_format_mask      = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask");
    pContext->alsa.snd_pcm_hw_params_set_channels_near    = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near");
    pContext->alsa.snd_pcm_hw_params_set_rate_resample    = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample");
    pContext->alsa.snd_pcm_hw_params_set_rate_near        = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near");
    pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near");
    pContext->alsa.snd_pcm_hw_params_set_periods_near     = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near");
    pContext->alsa.snd_pcm_hw_params_set_access           = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access");
    pContext->alsa.snd_pcm_hw_params_get_format           = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format");
    pContext->alsa.snd_pcm_hw_params_get_channels         = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels");
    pContext->alsa.snd_pcm_hw_params_get_channels_min     = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min");
    pContext->alsa.snd_pcm_hw_params_get_channels_max     = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max");
    pContext->alsa.snd_pcm_hw_params_get_rate             = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate");
    pContext->alsa.snd_pcm_hw_params_get_rate_min         = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min");
    pContext->alsa.snd_pcm_hw_params_get_rate_max         = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max");
    pContext->alsa.snd_pcm_hw_params_get_buffer_size      = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size");
    pContext->alsa.snd_pcm_hw_params_get_periods          = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods");
    pContext->alsa.snd_pcm_hw_params_get_access           = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access");
    pContext->alsa.snd_pcm_hw_params                      = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params");
    pContext->alsa.snd_pcm_sw_params_sizeof               = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof");
    pContext->alsa.snd_pcm_sw_params_current              = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current");
    pContext->alsa.snd_pcm_sw_params_get_boundary         = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary");
    pContext->alsa.snd_pcm_sw_params_set_avail_min        = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min");
    pContext->alsa.snd_pcm_sw_params_set_start_threshold  = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold");
    pContext->alsa.snd_pcm_sw_params_set_stop_threshold   = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold");
    pContext->alsa.snd_pcm_sw_params                      = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params");
    pContext->alsa.snd_pcm_format_mask_sizeof             = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof");
    pContext->alsa.snd_pcm_format_mask_test               = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test");
    pContext->alsa.snd_pcm_get_chmap                      = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap");
    pContext->alsa.snd_pcm_state                          = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state");
    pContext->alsa.snd_pcm_prepare                        = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare");
    pContext->alsa.snd_pcm_start                          = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start");
    pContext->alsa.snd_pcm_drop                           = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop");
    pContext->alsa.snd_pcm_drain                          = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain");
    pContext->alsa.snd_device_name_hint                   = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint");
    pContext->alsa.snd_device_name_get_hint               = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint");
    pContext->alsa.snd_card_get_index                     = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index");
    pContext->alsa.snd_device_name_free_hint              = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint");
    pContext->alsa.snd_pcm_mmap_begin                     = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin");
    pContext->alsa.snd_pcm_mmap_commit                    = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit");
    pContext->alsa.snd_pcm_recover                        = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover");
    pContext->alsa.snd_pcm_readi                          = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi");
    pContext->alsa.snd_pcm_writei                         = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei");
    pContext->alsa.snd_pcm_avail                          = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail");
    pContext->alsa.snd_pcm_avail_update                   = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update");
    pContext->alsa.snd_pcm_wait                           = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait");
    pContext->alsa.snd_pcm_info                           = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info");
    pContext->alsa.snd_pcm_info_sizeof                    = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof");
    pContext->alsa.snd_pcm_info_get_name                  = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name");
    pContext->alsa.snd_config_update_free_global          = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global");

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


    pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData);

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

static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
{
    ma_result result = MA_SUCCESS;
    ma_context_enumerate_devices_callback_data__pulse callbackData;
    ma_pa_operation* pOP = NULL;
    ma_pa_mainloop* pMainLoop;
    ma_pa_mainloop_api* pAPI;
    ma_pa_context* pPulseContext;
    int error;

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

    callbackData.pContext = pContext;
    callbackData.callback = callback;
    callbackData.pUserData = pUserData;
    callbackData.isTerminated = MA_FALSE;

    pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
    if (pMainLoop == NULL) {
        return MA_FAILED_TO_INIT_BACKEND;
    }

    pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop);
    if (pAPI == NULL) {
        ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
        return MA_FAILED_TO_INIT_BACKEND;
    }

    pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->pulse.pApplicationName);
    if (pPulseContext == NULL) {
        ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
        return MA_FAILED_TO_INIT_BACKEND;
    }

    error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->pulse.pServerName, (pContext->pulse.tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL);
    if (error != MA_PA_OK) {
        ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
        ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
        return ma_result_from_pulse(error);
    }

    for (;;) {
        ma_pa_context_state_t state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
        if (state == MA_PA_CONTEXT_READY) {
            break;  /* Success. */
        }
        if (state == MA_PA_CONTEXT_CONNECTING || state == MA_PA_CONTEXT_AUTHORIZING || state == MA_PA_CONTEXT_SETTING_NAME) {
            error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
            if (error < 0) {
                result = ma_result_from_pulse(error);
                goto done;
            }

#ifdef MA_DEBUG_OUTPUT
            printf("[PulseAudio] pa_context_get_state() returned %d. Waiting.\n", state);
#endif
            continue;   /* Keep trying. */
        }
        if (state == MA_PA_CONTEXT_UNCONNECTED || state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
#ifdef MA_DEBUG_OUTPUT
            printf("[PulseAudio] pa_context_get_state() returned %d. Failed.\n", state);
#endif
            goto done;  /* Failed. */
        }
    }


    /* Playback. */
    if (!callbackData.isTerminated) {
        pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)(pPulseContext, ma_context_enumerate_devices_sink_callback__pulse, &callbackData);
        if (pOP == NULL) {
            result = MA_ERROR;
            goto done;
        }

        result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
        ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
        if (result != MA_SUCCESS) {
            goto done;
        }
    }


    /* Capture. */
    if (!callbackData.isTerminated) {
        pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)(pPulseContext, ma_context_enumerate_devices_source_callback__pulse, &callbackData);
        if (pOP == NULL) {
            result = MA_ERROR;
            goto done;
        }

        result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
        ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
        if (result != MA_SUCCESS) {
            goto done;
        }
    }

done:
    ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext);
    ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
    ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
    return result;
}


typedef struct
{
    ma_device_info* pDeviceInfo;
    ma_bool32 foundDevice;
} ma_context_get_device_info_callback_data__pulse;

static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
{
    ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;

    if (endOfList > 0) {
        return;
    }

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


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

static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
{
    ma_result result = MA_SUCCESS;
    ma_context_get_device_info_callback_data__pulse callbackData;
    ma_pa_operation* pOP = NULL;
    ma_pa_mainloop* pMainLoop;
    ma_pa_mainloop_api* pAPI;
    ma_pa_context* pPulseContext;
    int error;

    MA_ASSERT(pContext != NULL);

    /* No exclusive mode with the PulseAudio backend. */
    if (shareMode == ma_share_mode_exclusive) {
        return MA_SHARE_MODE_NOT_SUPPORTED;
    }

    callbackData.pDeviceInfo = pDeviceInfo;
    callbackData.foundDevice = MA_FALSE;

    pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
    if (pMainLoop == NULL) {
        return MA_FAILED_TO_INIT_BACKEND;
    }

    pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop);
    if (pAPI == NULL) {
        ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
        return MA_FAILED_TO_INIT_BACKEND;
    }

    pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->pulse.pApplicationName);
    if (pPulseContext == NULL) {
        ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
        return MA_FAILED_TO_INIT_BACKEND;
    }

    error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->pulse.pServerName, 0, NULL);
    if (error != MA_PA_OK) {
        ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
        ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
        return ma_result_from_pulse(error);
    }

    for (;;) {
        ma_pa_context_state_t state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
        if (state == MA_PA_CONTEXT_READY) {
            break;  /* Success. */
        }
        if (state == MA_PA_CONTEXT_CONNECTING || state == MA_PA_CONTEXT_AUTHORIZING || state == MA_PA_CONTEXT_SETTING_NAME) {
            error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
            if (error < 0) {
                result = ma_result_from_pulse(error);
                goto done;
            }

#ifdef MA_DEBUG_OUTPUT
            printf("[PulseAudio] pa_context_get_state() returned %d. Waiting.\n", state);
#endif
            continue;   /* Keep trying. */
        }
        if (state == MA_PA_CONTEXT_UNCONNECTED || state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
#ifdef MA_DEBUG_OUTPUT
            printf("[PulseAudio] pa_context_get_state() returned %d. Failed.\n", state);
#endif
            goto done;  /* Failed. */
        }
    }

    if (deviceType == ma_device_type_playback) {
        pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)(pPulseContext, pDeviceID->pulse, ma_context_get_device_info_sink_callback__pulse, &callbackData);
    } else {
        pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)(pPulseContext, pDeviceID->pulse, ma_context_get_device_info_source_callback__pulse, &callbackData);
    }

    if (pOP != NULL) {
        ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
        ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
    } else {
        result = MA_ERROR;
        goto done;
    }

    if (!callbackData.foundDevice) {
        result = MA_NO_DEVICE;
        goto done;
    }


done:
    ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext);
    ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
    ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
    return result;
}


static void ma_pulse_device_state_callback(ma_pa_context* pPulseContext, void* pUserData)
{
    ma_device* pDevice;
    ma_context* pContext;

    pDevice = (ma_device*)pUserData;
    MA_ASSERT(pDevice != NULL);

    pContext = pDevice->pContext;
    MA_ASSERT(pContext != NULL);

    pDevice->pulse.pulseContextState = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
}

static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
{
    ma_pa_sink_info* pInfoOut;

    if (endOfList > 0) {
        return;
    }

    pInfoOut = (ma_pa_sink_info*)pUserData;
    MA_ASSERT(pInfoOut != NULL);

    *pInfoOut = *pInfo;

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

    }

    pDevice->pulse.pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
    if (pDevice->pulse.pAPI == NULL) {
        result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve PulseAudio main loop.", MA_FAILED_TO_INIT_BACKEND);
        goto on_error1;
    }

    pDevice->pulse.pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)((ma_pa_mainloop_api*)pDevice->pulse.pAPI, pContext->pulse.pApplicationName);
    if (pDevice->pulse.pPulseContext == NULL) {
        result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context for device.", MA_FAILED_TO_INIT_BACKEND);
        goto on_error1;
    }

    error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pDevice->pulse.pPulseContext, pContext->pulse.pServerName, (pContext->pulse.tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL);
    if (error != MA_PA_OK) {
        result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.", ma_result_from_pulse(error));
        goto on_error2;
    }


    pDevice->pulse.pulseContextState = MA_PA_CONTEXT_UNCONNECTED;
    ((ma_pa_context_set_state_callback_proc)pContext->pulse.pa_context_set_state_callback)((ma_pa_context*)pDevice->pulse.pPulseContext, ma_pulse_device_state_callback, pDevice);

    /* Wait for PulseAudio to get itself ready before returning. */
    for (;;) {
        if (pDevice->pulse.pulseContextState == MA_PA_CONTEXT_READY) {
            break;
        }

        /* An error may have occurred. */
        if (pDevice->pulse.pulseContextState == MA_PA_CONTEXT_FAILED || pDevice->pulse.pulseContextState == MA_PA_CONTEXT_TERMINATED) {
            result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.", MA_ERROR);
            goto on_error3;
        }

        error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
        if (error < 0) {
            result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio context.", ma_result_from_pulse(error));
            goto on_error3;
        }
    }

    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
        pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devCapture, ma_device_source_info_callback, &sourceInfo);
        if (pOP != NULL) {
            ma_device__wait_for_operation__pulse(pDevice, pOP);
            ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
        } else {
            result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device.", ma_result_from_pulse(error));
            goto on_error3;
        }

        ss = sourceInfo.sample_spec;
        cmap = sourceInfo.channel_map;

        pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, ss.rate);
        pDevice->capture.internalPeriods            = pConfig->periods;

        attr = ma_device__pa_buffer_attr_new(pDevice->capture.internalPeriodSizeInFrames, pConfig->periods, &ss);
    #ifdef MA_DEBUG_OUTPUT
        printf("[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->capture.internalPeriodSizeInFram...
    #endif

        pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap);
        if (pDevice->pulse.pStreamCapture == NULL) {
            result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
            goto on_error3;
        }

        streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
        if (devCapture != NULL) {
            streamFlags |= MA_PA_STREAM_DONT_MOVE;
        }

        error = ((ma_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags);
        if (error != MA_PA_OK) {
            result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.", ma_result_from_pulse(error));
            goto on_error4;
        }

        while (((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pDevice->pulse.pStreamCapture) != MA_PA_STREAM_READY) {
            error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
            if (error < 0) {
                result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio capture stream.", ma_result_from_pulse(error));
                goto on_error5;
            }
        }

        /* Internal format. */
        pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
        if (pActualSS != NULL) {
            /* If anything has changed between the requested and the actual sample spec, we need to update the buffer. */
            if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
                attr = ma_device__pa_buffer_attr_new(pDevice->capture.internalPeriodSizeInFrames, pConfig->periods, pActualSS);

                pOP = ((ma_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture, &attr, NULL, NULL);
                if (pOP != NULL) {
                    ma_device__wait_for_operation__pulse(pDevice, pOP);
                    ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
                }
            }

            ss = *pActualSS;
        }

        pDevice->capture.internalFormat     = ma_format_from_pulse(ss.format);
        pDevice->capture.internalChannels   = ss.channels;
        pDevice->capture.internalSampleRate = ss.rate;

        /* Internal channel map. */
        pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
        if (pActualCMap != NULL) {
            cmap = *pActualCMap;
        }
        for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
            pDevice->capture.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
        }

        /* Buffer. */
        pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
        if (pActualAttr != NULL) {
            attr = *pActualAttr;
        }
        pDevice->capture.internalPeriods            = attr.maxlength / attr.fragsize;
        pDevice->capture.internalPeriodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels) / pDevice->capture.internalPeriods;
    #ifdef MA_DEBUG_OUTPUT
        printf("[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->capture.internalPeriodSiz...
    #endif

        /* Name. */
        devCapture = ((ma_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
        if (devCapture != NULL) {
            ma_pa_operation* pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devCapture, ma_device_source_name_callback, pDevice);
            if (pOP != NULL) {
                ma_device__wait_for_operation__pulse(pDevice, pOP);
                ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
            }
        }
    }

    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
        pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devPlayback, ma_device_sink_info_callback, &sinkInfo);
        if (pOP != NULL) {
            ma_device__wait_for_operation__pulse(pDevice, pOP);
            ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
        } else {
            result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.", ma_result_from_pulse(error));
            goto on_error3;
        }

        ss = sinkInfo.sample_spec;
        cmap = sinkInfo.channel_map;

        pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, ss.rate);
        pDevice->playback.internalPeriods            = pConfig->periods;

        attr = ma_device__pa_buffer_attr_new(pDevice->playback.internalPeriodSizeInFrames, pConfig->periods, &ss);
    #ifdef MA_DEBUG_OUTPUT
        printf("[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->playback.internalPeriodSizeInFr...
    #endif

        pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);
        if (pDevice->pulse.pStreamPlayback == NULL) {
            result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
            goto on_error3;
        }

        streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
        if (devPlayback != NULL) {
            streamFlags |= MA_PA_STREAM_DONT_MOVE;
        }

        error = ((ma_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL);
        if (error != MA_PA_OK) {
            result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.", ma_result_from_pulse(error));
            goto on_error6;
        }

        while (((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pDevice->pulse.pStreamPlayback) != MA_PA_STREAM_READY) {
            error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
            if (error < 0) {
                result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio playback stream.", ma_result_from_pulse(error));
                goto on_error7;
            }
        }

        /* Internal format. */
        pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
        if (pActualSS != NULL) {
            /* If anything has changed between the requested and the actual sample spec, we need to update the buffer. */
            if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
                attr = ma_device__pa_buffer_attr_new(pDevice->playback.internalPeriodSizeInFrames, pConfig->periods, pActualSS);

                pOP = ((ma_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, &attr, NULL, NULL);
                if (pOP != NULL) {
                    ma_device__wait_for_operation__pulse(pDevice, pOP);
                    ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
                }
            }

            ss = *pActualSS;
        }

        pDevice->playback.internalFormat     = ma_format_from_pulse(ss.format);
        pDevice->playback.internalChannels   = ss.channels;
        pDevice->playback.internalSampleRate = ss.rate;

        /* Internal channel map. */
        pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
        if (pActualCMap != NULL) {
            cmap = *pActualCMap;
        }
        for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
            pDevice->playback.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
        }

        /* Buffer. */
        pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
        if (pActualAttr != NULL) {
            attr = *pActualAttr;
        }
        pDevice->playback.internalPeriods            = attr.maxlength / attr.tlength;
        pDevice->playback.internalPeriodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels) / pDevice->playback.internalPeriods;
    #ifdef MA_DEBUG_OUTPUT
        printf("[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->playback.internalPeriodS...
    #endif

        /* Name. */
        devPlayback = ((ma_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
        if (devPlayback != NULL) {
            ma_pa_operation* pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devPlayback, ma_device_sink_name_callback, pDevice);
            if (pOP != NULL) {
                ma_device__wait_for_operation__pulse(pDevice, pOP);
                ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
            }
        }
    }

    return MA_SUCCESS;


on_error7:
    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
        ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
    }
on_error6:
    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
        ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
    }
on_error5:
    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
        ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
    }
on_error4:
    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
        ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
    }
on_error3: ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
on_error2: ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
on_error1: ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
on_error0:
    return result;
}


static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData)
{
    ma_bool32* pIsSuccessful = (ma_bool32*)pUserData;
    MA_ASSERT(pIsSuccessful != NULL);

    *pIsSuccessful = (ma_bool32)success;

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

static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork)
{
    ma_context* pContext = pDevice->pContext;
    ma_bool32 wasSuccessful;
    ma_pa_stream* pStream;
    ma_pa_operation* pOP;
    ma_result result;

    /* This should not be called with a duplex device type. */

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

                } else {
                    /* No data available. Need to wait for more. */
                    int error = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
                    if (error < 0) {
                        return ma_result_from_pulse(error);
                    }

                    continue;
                }
            } else {
                return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to query the stream's writable size.", MA_ERROR);
            }
        }
    }

    if (pFramesWritten != NULL) {
        *pFramesWritten = totalFramesWritten;
    }

    return MA_SUCCESS;
}

static ma_result ma_device_read__pulse(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
{
    ma_uint32 totalFramesRead;

    MA_ASSERT(pDevice != NULL);
    MA_ASSERT(pPCMFrames != NULL);
    MA_ASSERT(frameCount > 0);

    if (pFramesRead != NULL) {
        *pFramesRead = 0;
    }

    totalFramesRead = 0;
    while (totalFramesRead < frameCount) {
        if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
            return MA_DEVICE_NOT_STARTED;
        }

        /*
        If a buffer is mapped we need to read from that first. Once it's consumed we need to drop it. Note that pDevice->pulse.pMappedBufferCapture can be null in which
        case it could be a hole. In this case we just write zeros into the output buffer.
        */
        if (pDevice->pulse.mappedBufferFramesRemainingCapture > 0) {
            ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
            ma_uint32 mappedBufferFramesConsumed = pDevice->pulse.mappedBufferFramesCapacityCapture - pDevice->pulse.mappedBufferFramesRemainingCapture;

            ma_uint32  framesToCopy = ma_min(pDevice->pulse.mappedBufferFramesRemainingCapture, (frameCount - totalFramesRead));
            void* pDst = (ma_uint8*)pPCMFrames + (totalFramesRead * bpf);

            /*
            This little bit of logic here is specifically for PulseAudio and it's hole management. The buffer pointer will be set to NULL
            when the current fragment is a hole. For a hole we just output silence.
            */
            if (pDevice->pulse.pMappedBufferCapture != NULL) {
                const void* pSrc = (const ma_uint8*)pDevice->pulse.pMappedBufferCapture + (mappedBufferFramesConsumed * bpf);
                MA_COPY_MEMORY(pDst, pSrc, framesToCopy * bpf);
            } else {
                MA_ZERO_MEMORY(pDst, framesToCopy * bpf);
            #if defined(MA_DEBUG_OUTPUT)
                printf("[PulseAudio] ma_device_read__pulse: Filling hole with silence.\n");
            #endif
            }

            pDevice->pulse.mappedBufferFramesRemainingCapture -= framesToCopy;
            totalFramesRead += framesToCopy;
        }

        /*
        Getting here means we've run out of data in the currently mapped chunk. We need to drop this from the device and then try
        mapping another chunk. If this fails we need to wait for data to become available.
        */
        if (pDevice->pulse.mappedBufferFramesCapacityCapture > 0 && pDevice->pulse.mappedBufferFramesRemainingCapture == 0) {
            int error;

        #if defined(MA_DEBUG_OUTPUT)
            printf("[PulseAudio] ma_device_read__pulse: Call pa_stream_drop()\n");
        #endif

            error = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
            if (error != 0) {
                return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to drop fragment.", ma_result_from_pulse(error));
            }

            pDevice->pulse.pMappedBufferCapture = NULL;
            pDevice->pulse.mappedBufferFramesRemainingCapture = 0;
            pDevice->pulse.mappedBufferFramesCapacityCapture = 0;
        }

        MA_ASSERT(totalFramesRead <= frameCount);
        if (totalFramesRead == frameCount) {
            break;
        }

        /* Getting here means we need to map a new buffer. If we don't have enough data we wait for more. */
        for (;;) {
            int error;
            size_t bytesMapped;

            if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
                break;
            }

            /* If the device has been corked, don't try to continue. */
            if (((ma_pa_stream_is_corked_proc)pDevice->pContext->pulse.pa_stream_is_corked)((ma_pa_stream*)pDevice->pulse.pStreamCapture)) {
            #if defined(MA_DEBUG_OUTPUT)
                printf("[PulseAudio] ma_device_read__pulse: Corked.\n");
            #endif
                break;
            }

            MA_ASSERT(pDevice->pulse.pMappedBufferCapture == NULL); /* <-- We're about to map a buffer which means we shouldn't have an existing mapping. */

            error = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)((ma_pa_stream*)pDevice->pulse.pStreamCapture, &pDevice->pulse.pMappedBufferCapture, &bytesMapped);
            if (error < 0) {
                return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to peek capture buffer.", ma_result_from_pulse(error));
            }

            if (bytesMapped > 0) {
                pDevice->pulse.mappedBufferFramesCapacityCapture  = bytesMapped / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
                pDevice->pulse.mappedBufferFramesRemainingCapture = pDevice->pulse.mappedBufferFramesCapacityCapture;

                #if defined(MA_DEBUG_OUTPUT)
                    printf("[PulseAudio] ma_device_read__pulse: Mapped. mappedBufferFramesCapacityCapture=%d, mappedBufferFramesRemainingCapture=%d\n", pDevice->pulse.mappedBufferFramesCapacityCapture, pDevice->pulse.mappedBufferFramesRemainingCaptur...
                #endif

                if (pDevice->pulse.pMappedBufferCapture == NULL) {
                    /* It's a hole. */
                    #if defined(MA_DEBUG_OUTPUT)
                        printf("[PulseAudio] ma_device_read__pulse: Call pa_stream_peek(). Hole.\n");
                    #endif
                }

                break;
            } else {
                if (pDevice->pulse.pMappedBufferCapture == NULL) {
                    /* Nothing available yet. Need to wait for more. */

                    /*
                    I have had reports of a deadlock in this part of the code. I have reproduced this when using the "Built-in Audio Analogue Stereo" device without
                    an actual microphone connected. I'm experimenting here by not blocking in pa_mainloop_iterate() and instead sleep for a bit when there are no
                    dispatches.
                    */
                    error = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 0, NULL);
                    if (error < 0) {
                        return ma_result_from_pulse(error);
                    }

                    /* Sleep for a bit if nothing was dispatched. */
                    if (error == 0) {
                        ma_sleep(1);
                    }

                #if defined(MA_DEBUG_OUTPUT)
                    printf("[PulseAudio] ma_device_read__pulse: No data available. Waiting. mappedBufferFramesCapacityCapture=%d, mappedBufferFramesRemainingCapture=%d\n", pDevice->pulse.mappedBufferFramesCapacityCapture, pDevice->pulse.mappedBufferF...
                #endif
                } else {
                    /* Getting here means we mapped 0 bytes, but have a non-NULL buffer. I don't think this should ever happen. */
                    MA_ASSERT(MA_FALSE);
                }
            }
        }
    }

    if (pFramesRead != NULL) {
        *pFramesRead = totalFramesRead;
    }

    return MA_SUCCESS;
}

static ma_result ma_device_main_loop__pulse(ma_device* pDevice)
{
    ma_result result = MA_SUCCESS;
    ma_bool32 exitLoop = MA_FALSE;

    MA_ASSERT(pDevice != NULL);

    /* The stream needs to be uncorked first. We do this at the top for both capture and playback for PulseAudio. */
    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
        result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0);
        if (result != MA_SUCCESS) {
            return result;
        }
    }
    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
        result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0);
        if (result != MA_SUCCESS) {
            return result;
        }
    }


    while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
        switch (pDevice->type)
        {
            case ma_device_type_duplex:
            {
                /* The process is: device_read -> convert -> callback -> convert -> device_write */
                ma_uint32 totalCapturedDeviceFramesProcessed = 0;
                ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
                    
                while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
                    ma_uint8  capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
                    ma_uint8  playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
                    ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat,  pDevice->capture.internalChannels);
                    ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
                    ma_uint32 capturedDeviceFramesRemaining;
                    ma_uint32 capturedDeviceFramesProcessed;
                    ma_uint32 capturedDeviceFramesToProcess;
                    ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
                    if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
                        capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
                    }

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

}
#endif  /* JACK */



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

Core Audio Backend

******************************************************************************/
#ifdef MA_HAS_COREAUDIO
#include <TargetConditionals.h>

#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1
    #define MA_APPLE_MOBILE
    #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
        #define MA_APPLE_TV
    #endif
    #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
        #define MA_APPLE_WATCH
    #endif
#else
    #define MA_APPLE_DESKTOP
#endif

#if defined(MA_APPLE_DESKTOP)
#include <CoreAudio/CoreAudio.h>
#else
#include <AVFoundation/AVFoundation.h>
#endif

#include <AudioToolbox/AudioToolbox.h>

/* CoreFoundation */
typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding);
typedef void (* ma_CFRelease_proc)(CFTypeRef cf);

/* CoreAudio */
#if defined(MA_APPLE_DESKTOP)
typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData);
typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
#endif

/* AudioToolbox */
typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc);
typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance);
typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance);
typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit);
typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit);
typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);
typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);
typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);
typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit);
typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);


#define MA_COREAUDIO_OUTPUT_BUS    0
#define MA_COREAUDIO_INPUT_BUS     1

#if defined(MA_APPLE_DESKTOP)
static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit);
#endif

/*
Core Audio

So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation
apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose
needing to figure out how this darn thing works, I'm going to outline a few things here.

Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be
able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen
that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent
and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the
distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API.

Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When
retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific
data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the
devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be
the central APIs for retrieving information about the system and specific devices.

To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a
structure with three variables and is used to identify which property you are getting or setting. The first is the "selector"
which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is
typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and
kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to
kAudioObjectPropertyElementMaster in miniaudio's case. I don't know of any cases where this would be set to anything different.

Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size
of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property
address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the
size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of
AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count.
*/

static ma_result ma_result_from_OSStatus(OSStatus status)
{
    switch (status)
    {
        case noErr:                                   return MA_SUCCESS;
    #if defined(MA_APPLE_DESKTOP)
        case kAudioHardwareNotRunningError:           return MA_DEVICE_NOT_STARTED;
        case kAudioHardwareUnspecifiedError:          return MA_ERROR;
        case kAudioHardwareUnknownPropertyError:      return MA_INVALID_ARGS;
        case kAudioHardwareBadPropertySizeError:      return MA_INVALID_OPERATION;
        case kAudioHardwareIllegalOperationError:     return MA_INVALID_OPERATION;
        case kAudioHardwareBadObjectError:            return MA_INVALID_ARGS;
        case kAudioHardwareBadDeviceError:            return MA_INVALID_ARGS;
        case kAudioHardwareBadStreamError:            return MA_INVALID_ARGS;
        case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION;
        case kAudioDeviceUnsupportedFormatError:      return MA_FORMAT_NOT_SUPPORTED;
        case kAudioDevicePermissionsError:            return MA_ACCESS_DENIED;
    #endif
        default:                                      return MA_ERROR;
    }
}

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

                    } else {
                        /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */
                        if (thisSampleFormat == desiredFormat) {
                            bestDeviceFormatSoFar = thisDeviceFormat;
                            break;  /* Found the exact match. */
                        } else {
                            /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */
                            if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
                                bestDeviceFormatSoFar = thisDeviceFormat;
                                continue;
                            } else {
                                continue;   /* No change to the best format for now. */
                            }
                        }
                    }
                } else {
                    /*
                    In this case the channel count is different to what the client has requested. If the best so far has the same channel
                    count as the requested count then it remains the best.
                    */
                    if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
                        continue;
                    } else {
                        /*
                        This is the case where both have the same sample rate (good) but different channel counts. Right now both have about
                        the same priority, but we need to compare the format now.
                        */
                        if (thisSampleFormat == bestSampleFormatSoFar) {
                            if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
                                bestDeviceFormatSoFar = thisDeviceFormat;
                                continue;
                            } else {
                                continue;   /* No change to the best format for now. */
                            }
                        }
                    }
                }
            }
        }
    }
    
    *pFormat = bestDeviceFormatSoFar;

    ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
    return MA_SUCCESS;
}

static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
{
    AudioUnitScope deviceScope;
    AudioUnitElement deviceBus;
    UInt32 channelLayoutSize;
    OSStatus status;
    AudioChannelLayout* pChannelLayout;
    ma_result result;

    MA_ASSERT(pContext != NULL);
    
    if (deviceType == ma_device_type_playback) {
        deviceScope = kAudioUnitScope_Output;
        deviceBus = MA_COREAUDIO_OUTPUT_BUS;
    } else {
        deviceScope = kAudioUnitScope_Input;
        deviceBus = MA_COREAUDIO_INPUT_BUS;
    }
    
    status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);
    if (status != noErr) {
        return ma_result_from_OSStatus(status);
    }
    
    pChannelLayout = (AudioChannelLayout*)ma__malloc_from_callbacks(channelLayoutSize, &pContext->allocationCallbacks);
    if (pChannelLayout == NULL) {
        return MA_OUT_OF_MEMORY;
    }
    
    status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);
    if (status != noErr) {
        ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
        return ma_result_from_OSStatus(status);
    }
    
    result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
    if (result != MA_SUCCESS) {
        ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
        return result;
    }

    ma__free_from_callbacks(pChannelLayout, &pContext->allocationCallbacks);
    return MA_SUCCESS;
}
#endif /* MA_APPLE_DESKTOP */

static ma_bool32 ma_context_is_device_id_equal__coreaudio(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 strcmp(pID0->coreaudio, pID1->coreaudio) == 0;
}

#if !defined(MA_APPLE_DESKTOP)
static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo)
{
    MA_ZERO_OBJECT(pInfo);
    ma_strncpy_s(pInfo->name,         sizeof(pInfo->name),         [pPortDesc.portName UTF8String], (size_t)-1);
    ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID      UTF8String], (size_t)-1);
}
#endif

static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
{
#if defined(MA_APPLE_DESKTOP)
    UInt32 deviceCount;
    AudioObjectID* pDeviceObjectIDs;
    ma_result result;
    UInt32 iDevice;

    result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
    if (result != MA_SUCCESS) {
        return result;
    }

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

        AudioStreamBasicDescription bestFormat;
        UInt32 propSize;
        
        /* We want to ensure we use a consistent device name to device enumeration. */
        if (pDeviceID != NULL) {
            ma_bool32 found = MA_FALSE;
            if (deviceType == ma_device_type_playback) {
                NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
                for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
                    if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
                        ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
                        found = MA_TRUE;
                        break;
                    }
                }
            } else {
                NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
                for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
                    if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
                        ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
                        found = MA_TRUE;
                        break;
                    }
                }
            }
            
            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)
                printf("  WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
            #endif
            }
        }
    } else {
        /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */
        MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS);   /* This should heve been validated at initialization time. */
        
        /*
        For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
        very strange has happened and we're not going to support it.
        */
        if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) {
            ma_uint8 tempBuffer[4096];
            UInt32 iBuffer;
        
            for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {
                ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);
                ma_uint32 framesRemaining = frameCountPerBuffer;

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

    return noErr;
}

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

    MA_ASSERT(pDevice != NULL);
    
    pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
    MA_ASSERT(pRenderedBufferList);
    
    /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */
    layout = ma_stream_layout_interleaved;
    if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) {
        layout = ma_stream_layout_deinterleaved;
    }
    
#if defined(MA_DEBUG_OUTPUT)
    printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers);
#endif
    
    status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
    if (status != noErr) {
    #if defined(MA_DEBUG_OUTPUT)
        printf("  ERROR: AudioUnitRender() failed with %d\n", status);
    #endif
        return status;
    }
    
    if (layout == ma_stream_layout_interleaved) {
        UInt32 iBuffer;
        for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
            if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {
                if (pDevice->type == ma_device_type_duplex) {
                    ma_device__handle_duplex_callback_capture(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData, &pDevice->coreaudio.duplexRB);
                } else {
                    ma_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
                }
            #if defined(MA_DEBUG_OUTPUT)
                printf("  mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
            #endif
            } else {
                /*
                This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
                not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams.
                */
                ma_uint8 silentBuffer[4096];
                ma_uint32 framesRemaining;
                
                MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer));
                
                framesRemaining = frameCount;
                while (framesRemaining > 0) {
                    ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
                    if (framesToSend > framesRemaining) {
                        framesToSend = framesRemaining;
                    }
                    
                    if (pDevice->type == ma_device_type_duplex) {
                        ma_device__handle_duplex_callback_capture(pDevice, framesToSend, silentBuffer, &pDevice->coreaudio.duplexRB);
                    } else {
                        ma_device__send_frames_to_client(pDevice, framesToSend, silentBuffer);
                    }
                    
                    framesRemaining -= framesToSend;
                }
                
            #if defined(MA_DEBUG_OUTPUT)
                printf("  WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
            #endif
            }
        }
    } else {
        /* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */
        MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS);    /* This should have been validated at initialization time. */
        
        /*
        For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
        very strange has happened and we're not going to support it.
        */
        if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) {
            ma_uint8 tempBuffer[4096];
            UInt32 iBuffer;
            for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {
                ma_uint32 framesRemaining = frameCount;
                while (framesRemaining > 0) {
                    void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
                    ma_uint32 iChannel;
                    ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_sample(pDevice->capture.internalFormat);
                    if (framesToSend > framesRemaining) {
                        framesToSend = framesRemaining;
                    }
                    
                    for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
                        ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
                    }
                    
                    ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);

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

                    framesRemaining -= framesToSend;
                }
            }
        }
    }

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

    return noErr;
}

static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)
{
    ma_device* pDevice = (ma_device*)pUserData;
    MA_ASSERT(pDevice != NULL);
    
    /*
    There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like
    AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit)

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

                ma_uint32 jDevice;
                for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) {
                    g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1];
                }
                
                g_TrackedDeviceCount_CoreAudio -= 1;
                
                /* If there's nothing else in the list we need to free memory. */
                if (g_TrackedDeviceCount_CoreAudio == 0) {
                    ma__free_from_callbacks(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks);
                    g_ppTrackedDevices_CoreAudio = NULL;
                    g_TrackedDeviceCap_CoreAudio = 0;
                }
            
                break;
            }
        }
    }
    ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);

    return MA_SUCCESS;
}
#endif

#if defined(MA_APPLE_MOBILE)
@interface ma_router_change_handler:NSObject {
    ma_device* m_pDevice;
}
@end

@implementation ma_router_change_handler
-(id)init:(ma_device*)pDevice
{
    self = [super init];
    m_pDevice = pDevice;

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];

    return self;
}

-(void)dealloc
{
    [self remove_handler];
}

-(void)remove_handler
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVAudioSessionRouteChangeNotification" object:nil];
}

-(void)handle_route_change:(NSNotification*)pNotification
{
    AVAudioSession* pSession = [AVAudioSession sharedInstance];

    NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
    switch (reason)
    {
        case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
        {
        #if defined(MA_DEBUG_OUTPUT)
            printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n");
        #endif
        } break;

        case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
        {
        #if defined(MA_DEBUG_OUTPUT)
            printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n");
        #endif
        } break;

        case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
        {
        #if defined(MA_DEBUG_OUTPUT)
            printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n");
        #endif
        } break;

        case AVAudioSessionRouteChangeReasonWakeFromSleep:
        {
        #if defined(MA_DEBUG_OUTPUT)
            printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n");
        #endif
        } break;

        case AVAudioSessionRouteChangeReasonOverride:
        {
        #if defined(MA_DEBUG_OUTPUT)
            printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n");
        #endif
        } break;

        case AVAudioSessionRouteChangeReasonCategoryChange:
        {
        #if defined(MA_DEBUG_OUTPUT)
            printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n");
        #endif
        } break;

        case AVAudioSessionRouteChangeReasonUnknown:
        default:
        {
        #if defined(MA_DEBUG_OUTPUT)
            printf("[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n");
        #endif
        } break;
    }

    m_pDevice->sampleRate = (ma_uint32)pSession.sampleRate;

    if (m_pDevice->type == ma_device_type_capture || m_pDevice->type == ma_device_type_duplex) {
        m_pDevice->capture.channels = (ma_uint32)pSession.inputNumberOfChannels;
        ma_device__post_init_setup(m_pDevice, ma_device_type_capture);
    }
    if (m_pDevice->type == ma_device_type_playback || m_pDevice->type == ma_device_type_duplex) {
        m_pDevice->playback.channels = (ma_uint32)pSession.outputNumberOfChannels;
        ma_device__post_init_setup(m_pDevice, ma_device_type_playback);
    }
}
@end
#endif

static void ma_device_uninit__coreaudio(ma_device* pDevice)
{
    MA_ASSERT(pDevice != NULL);
    MA_ASSERT(ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED);
    
#if defined(MA_APPLE_DESKTOP)
    /*
    Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll
    just gracefully ignore it.
    */
    ma_device__untrack__coreaudio(pDevice);
#endif
#if defined(MA_APPLE_MOBILE)
    if (pDevice->coreaudio.pRouteChangeHandler != NULL) {
        ma_router_change_handler* pRouteChangeHandler = (__bridge_transfer ma_router_change_handler*)pDevice->coreaudio.pRouteChangeHandler;
        [pRouteChangeHandler remove_handler];
    }
#endif
    
    if (pDevice->coreaudio.audioUnitCapture != NULL) {
        ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
    }
    if (pDevice->coreaudio.audioUnitPlayback != NULL) {
        ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
    }
    
    if (pDevice->coreaudio.pAudioBufferList) {
        ma__free_from_callbacks(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
    }

    if (pDevice->type == ma_device_type_duplex) {
        ma_pcm_rb_uninit(&pDevice->coreaudio.duplexRB);
    }
}

typedef struct
{
    /* Input. */
    ma_format formatIn;
    ma_uint32 channelsIn;
    ma_uint32 sampleRateIn;

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


static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference)   /* <-- pDevice is typed as void* inten...
{
    ma_result result;
    OSStatus status;
    UInt32 enableIOFlag;
    AudioStreamBasicDescription bestFormat;
    ma_uint32 actualPeriodSizeInFrames;
    AURenderCallbackStruct callbackInfo;
#if defined(MA_APPLE_DESKTOP)
    AudioObjectID deviceObjectID;
#endif

    /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */
    if (deviceType == ma_device_type_duplex) {
        return MA_INVALID_ARGS;
    }

    MA_ASSERT(pContext != NULL);
    MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture);

#if defined(MA_APPLE_DESKTOP)
    pData->deviceObjectID = 0;
#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.
            */

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

        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);
        }*/
        
        status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames));
        if (status != noErr) {
            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
            return ma_result_from_OSStatus(status);
        }
    }
    
    /* We need a buffer list if this is an input device. We render into this in the input callback. */
    if (deviceType == ma_device_type_capture) {
        ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
        size_t allocationSize;
        AudioBufferList* pBufferList;

        allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer);  /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */
        if (isInterleaved) {
            /* Interleaved case. This is the simple case because we just have one buffer. */
            allocationSize += sizeof(AudioBuffer) * 1;
            allocationSize += actualPeriodSizeInFrames * ma_get_bytes_per_frame(pData->formatOut, pData->channelsOut);
        } else {
            /* Non-interleaved case. This is the more complex case because there's more than one buffer. */
            allocationSize += sizeof(AudioBuffer) * pData->channelsOut;
            allocationSize += actualPeriodSizeInFrames * ma_get_bytes_per_sample(pData->formatOut) * pData->channelsOut;
        }
        
        pBufferList = (AudioBufferList*)ma__malloc_from_callbacks(allocationSize, &pContext->allocationCallbacks);
        if (pBufferList == NULL) {
            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
            return MA_OUT_OF_MEMORY;
        }
        
        if (isInterleaved) {
            pBufferList->mNumberBuffers = 1;
            pBufferList->mBuffers[0].mNumberChannels = pData->channelsOut;
            pBufferList->mBuffers[0].mDataByteSize   = actualPeriodSizeInFrames * ma_get_bytes_per_frame(pData->formatOut, pData->channelsOut);
            pBufferList->mBuffers[0].mData           = (ma_uint8*)pBufferList + sizeof(AudioBufferList);
        } else {
            ma_uint32 iBuffer;
            pBufferList->mNumberBuffers = pData->channelsOut;
            for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
                pBufferList->mBuffers[iBuffer].mNumberChannels = 1;
                pBufferList->mBuffers[iBuffer].mDataByteSize   = actualPeriodSizeInFrames * ma_get_bytes_per_sample(pData->formatOut);
                pBufferList->mBuffers[iBuffer].mData           = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * pData->channelsOut)) + (actualPeriodSizeInFrames * ma_get_bytes_per_sample(pData->form...
            }
        }
        
        pData->pAudioBufferList = pBufferList;
    }
    
    /* Callbacks. */
    callbackInfo.inputProcRefCon = pDevice_DoNotReference;
    if (deviceType == ma_device_type_playback) {
        callbackInfo.inputProc = ma_on_output__coreaudio;
        status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, MA_COREAUDIO_OUTPUT_BUS, &callbackInfo, sizeof(callbackInfo));
        if (status != noErr) {
            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
            return ma_result_from_OSStatus(status);
        }
    } else {
        callbackInfo.inputProc = ma_on_input__coreaudio;
        status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, MA_COREAUDIO_INPUT_BUS, &callbackInfo, sizeof(callbackInfo));
        if (status != noErr) {
            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
            return ma_result_from_OSStatus(status);
        }
    }
    
    /* We need to listen for stop events. */
    if (pData->registerStopEvent) {
        status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference);
        if (status != noErr) {
            ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
            return ma_result_from_OSStatus(status);
        }
    }
    
    /* Initialize the audio unit. */
    status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit);
    if (status != noErr) {
        ma__free_from_callbacks(pData->pAudioBufferList, &pContext->allocationCallbacks);
        pData->pAudioBufferList = NULL;
        ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
        return ma_result_from_OSStatus(status);
    }
    
    /* Grab the name. */
#if defined(MA_APPLE_DESKTOP)
    ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName);
#else
    if (deviceType == ma_device_type_playback) {
        ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
    } else {
        ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME);
    }
#endif
    
    return result;
}

#if defined(MA_APPLE_DESKTOP)
static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit)
{
    ma_device_init_internal_data__coreaudio data;
    ma_result result;

    /* This should only be called for playback or capture, not duplex. */
    if (deviceType == ma_device_type_duplex) {
        return MA_INVALID_ARGS;
    }

    if (deviceType == ma_device_type_capture) {
        data.formatIn               = pDevice->capture.format;
        data.channelsIn             = pDevice->capture.channels;
        data.sampleRateIn           = pDevice->sampleRate;
        MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
        data.usingDefaultFormat     = pDevice->capture.usingDefaultFormat;
        data.usingDefaultChannels   = pDevice->capture.usingDefaultChannels;
        data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
        data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
        data.shareMode              = pDevice->capture.shareMode;
        data.registerStopEvent      = MA_TRUE;

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

        
        case ma_format_s16:
        case ma_format_f32:
        default:
        {
            par.bits = 16;
            par.bps  = 2;
            par.sig  = 1;
        } break;
    }
    
    if (deviceType == ma_device_type_capture) {
        par.rchan = channels;
    } else {
        par.pchan = channels;
    }

    par.rate = sampleRate;

    internalPeriodSizeInFrames = pConfig->periodSizeInFrames;
    if (internalPeriodSizeInFrames == 0) {
        internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, par.rate);
    }

    par.round    = internalPeriodSizeInFrames;
    par.appbufsz = par.round * pConfig->periods;
    
    if (((ma_sio_setpar_proc)pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) {
        ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
        return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.", MA_FORMAT_NOT_SUPPORTED);
    }
    if (((ma_sio_getpar_proc)pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) {
        ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
        return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.", MA_FORMAT_NOT_SUPPORTED);
    }

    internalFormat             = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb);
    internalChannels           = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan;
    internalSampleRate         = par.rate;
    internalPeriods            = par.appbufsz / par.round;
    internalPeriodSizeInFrames = par.round;

    if (deviceType == ma_device_type_capture) {
        pDevice->sndio.handleCapture                 = handle;
        pDevice->capture.internalFormat              = internalFormat;
        pDevice->capture.internalChannels            = internalChannels;
        pDevice->capture.internalSampleRate          = internalSampleRate;
        ma_get_standard_channel_map(ma_standard_channel_map_sndio, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
        pDevice->capture.internalPeriodSizeInFrames  = internalPeriodSizeInFrames;
        pDevice->capture.internalPeriods             = internalPeriods;
    } else {
        pDevice->sndio.handlePlayback                = handle;
        pDevice->playback.internalFormat             = internalFormat;
        pDevice->playback.internalChannels           = internalChannels;
        pDevice->playback.internalSampleRate         = internalSampleRate;
        ma_get_standard_channel_map(ma_standard_channel_map_sndio, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
        pDevice->playback.internalPeriodSizeInFrames = internalPeriodSizeInFrames;
        pDevice->playback.internalPeriods            = internalPeriods;
    }

#ifdef MA_DEBUG_OUTPUT
    printf("DEVICE INFO\n");
    printf("    Format:      %s\n", ma_get_format_name(internalFormat));
    printf("    Channels:    %d\n", internalChannels);
    printf("    Sample Rate: %d\n", internalSampleRate);
    printf("    Period Size: %d\n", internalPeriodSizeInFrames);
    printf("    Periods:     %d\n", internalPeriods);
    printf("    appbufsz:    %d\n", par.appbufsz);
    printf("    round:       %d\n", par.round);
#endif

    return MA_SUCCESS;
}

static ma_result ma_device_init__sndio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
{
    MA_ASSERT(pDevice != NULL);

    MA_ZERO_OBJECT(&pDevice->sndio);

    if (pConfig->deviceType == ma_device_type_loopback) {
        return MA_DEVICE_TYPE_NOT_SUPPORTED;
    }

    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
        ma_result result = ma_device_init_handle__sndio(pContext, pConfig, ma_device_type_capture, pDevice);
        if (result != MA_SUCCESS) {
            return result;
        }
    }

    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
        ma_result result = ma_device_init_handle__sndio(pContext, pConfig, ma_device_type_playback, pDevice);
        if (result != MA_SUCCESS) {
            return result;
        }
    }

    return MA_SUCCESS;
}

static ma_result ma_device_stop__sndio(ma_device* pDevice)
{
    MA_ASSERT(pDevice != NULL);

    /*
    From the documentation:

        The sio_stop() function puts the audio subsystem in the same state as before sio_start() is called. It stops recording, drains the play buffer and then
        stops playback. If samples to play are queued but playback hasn't started yet then playback is forced immediately; playback will actually stop once the
        buffer is drained. In no case are samples in the play buffer discarded.

    Therefore, sio_stop() performs all of the necessary draining for us.
    */

    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
        ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
    }

    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
        ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);

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

    if (*pfd == -1) {
        return ma_result_from_errno(errno);
    }

    return MA_SUCCESS;
}

static ma_bool32 ma_context_is_device_id_equal__oss(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->oss, pID1->oss) == 0;
}

static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
{
    int fd;
    oss_sysinfo si;
    int result;

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

    fd = ma_open_temp_device__oss();
    if (fd == -1) {
        return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.", MA_NO_BACKEND);
    }

    result = ioctl(fd, SNDCTL_SYSINFO, &si);
    if (result != -1) {
        int iAudioDevice;
        for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
            oss_audioinfo ai;
            ai.dev = iAudioDevice;
            result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);
            if (result != -1) {
                if (ai.devnode[0] != '\0') {    /* <-- Can be blank, according to documentation. */
                    ma_device_info deviceInfo;
                    ma_bool32 isTerminating = MA_FALSE;

                    MA_ZERO_OBJECT(&deviceInfo);

                    /* ID */
                    ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1);

                    /*
                    The human readable device name should be in the "ai.handle" variable, but it can
                    sometimes be empty in which case we just fall back to "ai.name" which is less user
                    friendly, but usually has a value.
                    */
                    if (ai.handle[0] != '\0') {
                        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1);
                    } else {
                        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1);
                    }

                    /* The device can be both playback and capture. */
                    if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) {
                        isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
                    }
                    if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) {
                        isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
                    }

                    if (isTerminating) {
                        break;
                    }
                }
            }
        }
    } else {
        close(fd);
        return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND);
    }

    close(fd);
    return MA_SUCCESS;
}

static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
{
    ma_bool32 foundDevice;
    int fdTemp;
    oss_sysinfo si;
    int result;

    MA_ASSERT(pContext != NULL);
    (void)shareMode;

    /* Handle the default device a little differently. */
    if (pDeviceID == NULL) {
        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);
        }

        return MA_SUCCESS;
    }


    /* If we get here it means we are _not_ using the default device. */
    foundDevice = MA_FALSE;

    fdTemp = ma_open_temp_device__oss();
    if (fdTemp == -1) {
        return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.", MA_NO_BACKEND);
    }

    result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);
    if (result != -1) {
        int iAudioDevice;
        for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
            oss_audioinfo ai;
            ai.dev = iAudioDevice;
            result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);
            if (result != -1) {
                if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) {
                    /* It has the same name, so now just confirm the type. */
                    if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) ||
                        (deviceType == ma_device_type_capture  && ((ai.caps & PCM_CAP_INPUT)  != 0))) {
                        unsigned int formatMask;

                        /* ID */
                        ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1);

                        /*
                        The human readable device name should be in the "ai.handle" variable, but it can
                        sometimes be empty in which case we just fall back to "ai.name" which is less user
                        friendly, but usually has a value.
                        */
                        if (ai.handle[0] != '\0') {
                            ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1);
                        } else {
                            ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);
                        }

                        pDeviceInfo->minChannels = ai.min_channels;
                        pDeviceInfo->maxChannels = ai.max_channels;
                        pDeviceInfo->minSampleRate = ai.min_rate;
                        pDeviceInfo->maxSampleRate = ai.max_rate;
                        pDeviceInfo->formatCount = 0;

                        if (deviceType == ma_device_type_playback) {
                            formatMask = ai.oformats;
                        } else {
                            formatMask = ai.iformats;
                        }

                        if ((formatMask & AFMT_U8) != 0) {
                            pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_u8;
                        }
                        if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) {
                            pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s16;
                        }
                        if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) {
                            pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s32;
                        }

                        foundDevice = MA_TRUE;
                        break;
                    }
                }
            }
        }
    } else {
        close(fdTemp);
        return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND);
    }


    close(fdTemp);

    if (!foundDevice) {
        return MA_NO_DEVICE;
    }

    return MA_SUCCESS;
}

static void ma_device_uninit__oss(ma_device* pDevice)

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


    MA_ASSERT(pContext != NULL);

    (void)pConfig;

    /* Try opening a temporary device first so we can get version information. This is closed at the end. */
    fd = ma_open_temp_device__oss();
    if (fd == -1) {
        return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties.", MA_NO_BACKEND);   /* Looks liks OSS isn't installed, or there are no available devices. */
    }

    /* Grab the OSS version. */
    ossVersion = 0;
    result = ioctl(fd, OSS_GETVERSION, &ossVersion);
    if (result == -1) {
        close(fd);
        return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.", MA_NO_BACKEND);
    }

    pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16);
    pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8);

    pContext->onUninit         = ma_context_uninit__oss;
    pContext->onDeviceIDEqual  = ma_context_is_device_id_equal__oss;
    pContext->onEnumDevices    = ma_context_enumerate_devices__oss;
    pContext->onGetDeviceInfo  = ma_context_get_device_info__oss;
    pContext->onDeviceInit     = ma_device_init__oss;
    pContext->onDeviceUninit   = ma_device_uninit__oss;
    pContext->onDeviceStart    = NULL; /* Not required for synchronous backends. */
    pContext->onDeviceStop     = NULL; /* Not required for synchronous backends. */
    pContext->onDeviceMainLoop = ma_device_main_loop__oss;

    close(fd);
    return MA_SUCCESS;
}
#endif  /* OSS */


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

AAudio Backend

******************************************************************************/
#ifdef MA_HAS_AAUDIO
/*#include <AAudio/AAudio.h>*/

#define MA_AAUDIO_UNSPECIFIED 0

typedef int32_t ma_aaudio_result_t;
typedef int32_t ma_aaudio_direction_t;
typedef int32_t ma_aaudio_sharing_mode_t;
typedef int32_t ma_aaudio_format_t;
typedef int32_t ma_aaudio_stream_state_t;
typedef int32_t ma_aaudio_performance_mode_t;
typedef int32_t ma_aaudio_data_callback_result_t;

/* Result codes. miniaudio only cares about the success code. */
#define MA_AAUDIO_OK                               0

/* Directions. */
#define MA_AAUDIO_DIRECTION_OUTPUT                 0
#define MA_AAUDIO_DIRECTION_INPUT                  1

/* Sharing modes. */
#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE           0
#define MA_AAUDIO_SHARING_MODE_SHARED              1

/* Formats. */
#define MA_AAUDIO_FORMAT_PCM_I16                   1
#define MA_AAUDIO_FORMAT_PCM_FLOAT                 2

/* Stream states. */
#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED       0
#define MA_AAUDIO_STREAM_STATE_UNKNOWN             1
#define MA_AAUDIO_STREAM_STATE_OPEN                2
#define MA_AAUDIO_STREAM_STATE_STARTING            3
#define MA_AAUDIO_STREAM_STATE_STARTED             4
#define MA_AAUDIO_STREAM_STATE_PAUSING             5
#define MA_AAUDIO_STREAM_STATE_PAUSED              6
#define MA_AAUDIO_STREAM_STATE_FLUSHING            7
#define MA_AAUDIO_STREAM_STATE_FLUSHED             8
#define MA_AAUDIO_STREAM_STATE_STOPPING            9
#define MA_AAUDIO_STREAM_STATE_STOPPED             10
#define MA_AAUDIO_STREAM_STATE_CLOSING             11
#define MA_AAUDIO_STREAM_STATE_CLOSED              12
#define MA_AAUDIO_STREAM_STATE_DISCONNECTED        13

/* Performance modes. */
#define MA_AAUDIO_PERFORMANCE_MODE_NONE            10
#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING    11
#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY     12

/* Callback results. */
#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE         0
#define MA_AAUDIO_CALLBACK_RESULT_STOP             1

/* Objects. */
typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder;
typedef struct ma_AAudioStream_t*        ma_AAudioStream;

typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);
typedef void                             (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error);

typedef ma_aaudio_result_t       (* MA_PFN_AAudio_createStreamBuilder)                   (ma_AAudioStreamBuilder** ppBuilder);
typedef ma_aaudio_result_t       (* MA_PFN_AAudioStreamBuilder_delete)                   (ma_AAudioStreamBuilder* pBuilder);
typedef void                     (* MA_PFN_AAudioStreamBuilder_setDeviceId)              (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId);
typedef void                     (* MA_PFN_AAudioStreamBuilder_setDirection)             (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction);
typedef void                     (* MA_PFN_AAudioStreamBuilder_setSharingMode)           (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode);
typedef void                     (* MA_PFN_AAudioStreamBuilder_setFormat)                (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format);
typedef void                     (* MA_PFN_AAudioStreamBuilder_setChannelCount)          (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount);
typedef void                     (* MA_PFN_AAudioStreamBuilder_setSampleRate)            (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate);
typedef void                     (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
typedef void                     (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
typedef void                     (* MA_PFN_AAudioStreamBuilder_setDataCallback)          (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData);
typedef void                     (* MA_PFN_AAudioStreamBuilder_setErrorCallback)         (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData);
typedef void                     (* MA_PFN_AAudioStreamBuilder_setPerformanceMode)       (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode);
typedef ma_aaudio_result_t       (* MA_PFN_AAudioStreamBuilder_openStream)               (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream);
typedef ma_aaudio_result_t       (* MA_PFN_AAudioStream_close)                           (ma_AAudioStream* pStream);
typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState)                        (ma_AAudioStream* pStream);
typedef ma_aaudio_result_t       (* MA_PFN_AAudioStream_waitForStateChange)              (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds);
typedef ma_aaudio_format_t       (* MA_PFN_AAudioStream_getFormat)                       (ma_AAudioStream* pStream);
typedef int32_t                  (* MA_PFN_AAudioStream_getChannelCount)                 (ma_AAudioStream* pStream);
typedef int32_t                  (* MA_PFN_AAudioStream_getSampleRate)                   (ma_AAudioStream* pStream);
typedef int32_t                  (* MA_PFN_AAudioStream_getBufferCapacityInFrames)       (ma_AAudioStream* pStream);
typedef int32_t                  (* MA_PFN_AAudioStream_getFramesPerDataCallback)        (ma_AAudioStream* pStream);
typedef int32_t                  (* MA_PFN_AAudioStream_getFramesPerBurst)               (ma_AAudioStream* pStream);
typedef ma_aaudio_result_t       (* MA_PFN_AAudioStream_requestStart)                    (ma_AAudioStream* pStream);
typedef ma_aaudio_result_t       (* MA_PFN_AAudioStream_requestStop)                     (ma_AAudioStream* pStream);

static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA)
{
    switch (resultAA)
    {
        case MA_AAUDIO_OK: return MA_SUCCESS;
        default: break;
    }

    return MA_ERROR;
}

static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error)
{
    ma_device* pDevice = (ma_device*)pUserData;
    MA_ASSERT(pDevice != NULL);

    (void)error;

#if defined(MA_DEBUG_OUTPUT)
    printf("[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream));
#endif

    /*
    From the documentation for AAudio, when a device is disconnected all we can do is stop it. However, we cannot stop it from the callback - we need
    to do it from another thread. Therefore we are going to use an event thread for the AAudio backend to do this cleanly and safely.
    */
    if (((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream) == MA_AAUDIO_STREAM_STATE_DISCONNECTED) {
#if defined(MA_DEBUG_OUTPUT)
        printf("[AAudio] Device Disconnected.\n");
#endif
    }
}

static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
{
    ma_device* pDevice = (ma_device*)pUserData;
    MA_ASSERT(pDevice != NULL);

    if (pDevice->type == ma_device_type_duplex) {
        ma_device__handle_duplex_callback_capture(pDevice, frameCount, pAudioData, &pDevice->aaudio.duplexRB);
    } else {
        ma_device__send_frames_to_client(pDevice, frameCount, pAudioData);     /* Send directly to the client. */
    }

    (void)pStream;
    return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
}

static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
{
    ma_device* pDevice = (ma_device*)pUserData;
    MA_ASSERT(pDevice != NULL);

    if (pDevice->type == ma_device_type_duplex) {
        ma_device__handle_duplex_callback_playback(pDevice, frameCount, pAudioData, &pDevice->aaudio.duplexRB);
    } else {
        ma_device__read_frames_from_client(pDevice, frameCount, pAudioData);   /* Read directly from the client. */
    }

    (void)pStream;
    return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
}

static ma_result ma_open_stream__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, const ma_device_config* pConfig, const ma_device* pDevice, ma_AAudioStream** ppStream)
{
    ma_AAudioStreamBuilder* pBuilder;
    ma_aaudio_result_t resultAA;

    MA_ASSERT(deviceType != ma_device_type_duplex);   /* This function should not be called for a full-duplex device type. */

    *ppStream = NULL;

    resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
    if (resultAA != MA_AAUDIO_OK) {
        return ma_result_from_aaudio(resultAA);
    }

    if (pDeviceID != NULL) {
        ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
    }

    ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);
    ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);

    if (pConfig != NULL) {
        ma_uint32 bufferCapacityInFrames;

        if (pDevice == NULL || !pDevice->usingDefaultSampleRate) {
            ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pConfig->sampleRate);
        }

        if (deviceType == ma_device_type_capture) {
            if (pDevice == NULL || !pDevice->capture.usingDefaultChannels) {
                ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pConfig->capture.channels);
            }
            if (pDevice == NULL || !pDevice->capture.usingDefaultFormat) {
                ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pConfig->capture.format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
            }
        } else {
            if (pDevice == NULL || !pDevice->playback.usingDefaultChannels) {
                ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pConfig->playback.channels);
            }
            if (pDevice == NULL || !pDevice->playback.usingDefaultFormat) {
                ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pConfig->playback.format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
            }
        }

        bufferCapacityInFrames = pConfig->periodSizeInFrames * pConfig->periods;
        if (bufferCapacityInFrames == 0) {
            bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pConfig->sampleRate) * pConfig->periods;
        }

        ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames);
        ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pConfig->periods);

        if (deviceType == ma_device_type_capture) {
            ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice);
        } else {
            ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);
        }

        /* Not sure how this affects things, but since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let go ahead and set it. */
        ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFOR...
    }

    ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice);

    resultAA = ((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream);
    if (resultAA != MA_AAUDIO_OK) {
        *ppStream = NULL;
        ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
        return ma_result_from_aaudio(resultAA);
    }

    ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
    return MA_SUCCESS;
}

static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream)
{
    return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));
}

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

    pContext->aaudio.AAudioStreamBuilder_setDirection              = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection");
    pContext->aaudio.AAudioStreamBuilder_setSharingMode            = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode");
    pContext->aaudio.AAudioStreamBuilder_setFormat                 = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat");
    pContext->aaudio.AAudioStreamBuilder_setChannelCount           = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount");
    pContext->aaudio.AAudioStreamBuilder_setSampleRate             = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate");
    pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames");
    pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback  = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback");
    pContext->aaudio.AAudioStreamBuilder_setDataCallback           = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback");
    pContext->aaudio.AAudioStreamBuilder_setErrorCallback          = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback");
    pContext->aaudio.AAudioStreamBuilder_setPerformanceMode        = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode");
    pContext->aaudio.AAudioStreamBuilder_openStream                = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream");
    pContext->aaudio.AAudioStream_close                            = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close");
    pContext->aaudio.AAudioStream_getState                         = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState");
    pContext->aaudio.AAudioStream_waitForStateChange               = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange");
    pContext->aaudio.AAudioStream_getFormat                        = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat");
    pContext->aaudio.AAudioStream_getChannelCount                  = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount");
    pContext->aaudio.AAudioStream_getSampleRate                    = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate");
    pContext->aaudio.AAudioStream_getBufferCapacityInFrames        = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames");
    pContext->aaudio.AAudioStream_getFramesPerDataCallback         = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback");
    pContext->aaudio.AAudioStream_getFramesPerBurst                = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
    pContext->aaudio.AAudioStream_requestStart                     = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart");
    pContext->aaudio.AAudioStream_requestStop                      = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop");

    pContext->isBackendAsynchronous = MA_TRUE;

    pContext->onUninit        = ma_context_uninit__aaudio;
    pContext->onDeviceIDEqual = ma_context_is_device_id_equal__aaudio;
    pContext->onEnumDevices   = ma_context_enumerate_devices__aaudio;
    pContext->onGetDeviceInfo = ma_context_get_device_info__aaudio;
    pContext->onDeviceInit    = ma_device_init__aaudio;
    pContext->onDeviceUninit  = ma_device_uninit__aaudio;
    pContext->onDeviceStart   = ma_device_start__aaudio;
    pContext->onDeviceStop    = ma_device_stop__aaudio;

    (void)pConfig;
    return MA_SUCCESS;
}
#endif  /* AAudio */


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

OpenSL|ES Backend

******************************************************************************/
#ifdef MA_HAS_OPENSL
#include <SLES/OpenSLES.h>
#ifdef MA_ANDROID
#include <SLES/OpenSLES_Android.h>
#endif

typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired);

/* OpenSL|ES has one-per-application objects :( */
static SLObjectItf g_maEngineObjectSL    = NULL;
static SLEngineItf g_maEngineSL          = NULL;
static ma_uint32   g_maOpenSLInitCounter = 0;
static ma_spinlock g_maOpenSLSpinlock    = 0;   /* For init/uninit. */

#define MA_OPENSL_OBJ(p)         (*((SLObjectItf)(p)))
#define MA_OPENSL_OUTPUTMIX(p)   (*((SLOutputMixItf)(p)))
#define MA_OPENSL_PLAY(p)        (*((SLPlayItf)(p)))
#define MA_OPENSL_RECORD(p)      (*((SLRecordItf)(p)))

#ifdef MA_ANDROID
#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))
#else
#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p)))
#endif

static ma_result ma_result_from_OpenSL(SLuint32 result)
{
    switch (result)
    {
        case SL_RESULT_SUCCESS:                 return MA_SUCCESS;
        case SL_RESULT_PRECONDITIONS_VIOLATED:  return MA_ERROR;
        case SL_RESULT_PARAMETER_INVALID:       return MA_INVALID_ARGS;
        case SL_RESULT_MEMORY_FAILURE:          return MA_OUT_OF_MEMORY;
        case SL_RESULT_RESOURCE_ERROR:          return MA_INVALID_DATA;
        case SL_RESULT_RESOURCE_LOST:           return MA_ERROR;
        case SL_RESULT_IO_ERROR:                return MA_IO_ERROR;
        case SL_RESULT_BUFFER_INSUFFICIENT:     return MA_NO_SPACE;
        case SL_RESULT_CONTENT_CORRUPTED:       return MA_INVALID_DATA;
        case SL_RESULT_CONTENT_UNSUPPORTED:     return MA_FORMAT_NOT_SUPPORTED;
        case SL_RESULT_CONTENT_NOT_FOUND:       return MA_ERROR;
        case SL_RESULT_PERMISSION_DENIED:       return MA_ACCESS_DENIED;
        case SL_RESULT_FEATURE_UNSUPPORTED:     return MA_NOT_IMPLEMENTED;
        case SL_RESULT_INTERNAL_ERROR:          return MA_ERROR;
        case SL_RESULT_UNKNOWN_ERROR:           return MA_ERROR;
        case SL_RESULT_OPERATION_ABORTED:       return MA_ERROR;
        case SL_RESULT_CONTROL_LOST:            return MA_ERROR;
        default:                                return MA_ERROR;
    }
}

/* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id)
{
    switch (id)
    {
        case SL_SPEAKER_FRONT_LEFT:            return MA_CHANNEL_FRONT_LEFT;
        case SL_SPEAKER_FRONT_RIGHT:           return MA_CHANNEL_FRONT_RIGHT;
        case SL_SPEAKER_FRONT_CENTER:          return MA_CHANNEL_FRONT_CENTER;
        case SL_SPEAKER_LOW_FREQUENCY:         return MA_CHANNEL_LFE;
        case SL_SPEAKER_BACK_LEFT:             return MA_CHANNEL_BACK_LEFT;
        case SL_SPEAKER_BACK_RIGHT:            return MA_CHANNEL_BACK_RIGHT;
        case SL_SPEAKER_FRONT_LEFT_OF_CENTER:  return MA_CHANNEL_FRONT_LEFT_CENTER;
        case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
        case SL_SPEAKER_BACK_CENTER:           return MA_CHANNEL_BACK_CENTER;
        case SL_SPEAKER_SIDE_LEFT:             return MA_CHANNEL_SIDE_LEFT;
        case SL_SPEAKER_SIDE_RIGHT:            return MA_CHANNEL_SIDE_RIGHT;
        case SL_SPEAKER_TOP_CENTER:            return MA_CHANNEL_TOP_CENTER;
        case SL_SPEAKER_TOP_FRONT_LEFT:        return MA_CHANNEL_TOP_FRONT_LEFT;
        case SL_SPEAKER_TOP_FRONT_CENTER:      return MA_CHANNEL_TOP_FRONT_CENTER;
        case SL_SPEAKER_TOP_FRONT_RIGHT:       return MA_CHANNEL_TOP_FRONT_RIGHT;
        case SL_SPEAKER_TOP_BACK_LEFT:         return MA_CHANNEL_TOP_BACK_LEFT;
        case SL_SPEAKER_TOP_BACK_CENTER:       return MA_CHANNEL_TOP_BACK_CENTER;
        case SL_SPEAKER_TOP_BACK_RIGHT:        return MA_CHANNEL_TOP_BACK_RIGHT;
        default: return 0;
    }
}

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

        MA_ZERO_OBJECT(&deviceInfo);
        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
        cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
    }

    return MA_SUCCESS;
}

static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
{
    MA_ASSERT(pContext != NULL);

    MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */
    if (g_maOpenSLInitCounter == 0) {
        return MA_INVALID_OPERATION;
    }

    /* No exclusive mode with OpenSL|ES. */
    if (shareMode == ma_share_mode_exclusive) {
        return MA_SHARE_MODE_NOT_SUPPORTED;
    }

    /*
    TODO: Test Me.
    
    This is currently untested, so for now we are just returning default devices.
    */
#if 0 && !defined(MA_ANDROID)
    SLAudioIODeviceCapabilitiesItf deviceCaps;
    SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
    if (resultSL != SL_RESULT_SUCCESS) {
        /* The interface may not be supported so just report a default device. */
        goto return_default_device;
    }

    if (deviceType == ma_device_type_playback) {
        SLAudioOutputDescriptor desc;
        resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
        if (resultSL != SL_RESULT_SUCCESS) {
            return ma_result_from_OpenSL(resultSL);
        }

        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1);
    } else {
        SLAudioInputDescriptor desc;
        resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
        if (resultSL != SL_RESULT_SUCCESS) {
            return ma_result_from_OpenSL(resultSL);
        }

        ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);
    }

    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;
    }

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

    *pChannels   = pDataFormat->numChannels;
    *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;
    ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap);

    return MA_SUCCESS;
}

static ma_result ma_device_init__opensl(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
{
#ifdef MA_ANDROID
    SLDataLocator_AndroidSimpleBufferQueue queue;
    SLresult resultSL;
    ma_uint32 periodSizeInFrames;
    size_t bufferSizeInBytes;
    SLInterfaceID itfIDs1[1];
    const SLboolean itfIDsRequired1[] = {SL_BOOLEAN_TRUE};
#endif

    (void)pContext;

    MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */
    if (g_maOpenSLInitCounter == 0) {
        return MA_INVALID_OPERATION;
    }

    if (pConfig->deviceType == ma_device_type_loopback) {
        return MA_DEVICE_TYPE_NOT_SUPPORTED;
    }

    /*
    For now, only supporting Android implementations of OpenSL|ES since that's the only one I've
    been able to test with and I currently depend on Android-specific extensions (simple buffer
    queues).
    */
#ifdef MA_ANDROID
    itfIDs1[0] = (SLInterfaceID)pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE;

    /* No exclusive mode with OpenSL|ES. */
    if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
        ((pConfig->deviceType == ma_device_type_capture  || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode  == ma_share_mode_exclusive)) {
        return MA_SHARE_MODE_NOT_SUPPORTED;
    }

    /* Now we can start initializing the device properly. */
    MA_ASSERT(pDevice != NULL);
    MA_ZERO_OBJECT(&pDevice->opensl);

    queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
    queue.numBuffers = pConfig->periods;


    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
        ma_SLDataFormat_PCM pcm;
        SLDataLocator_IODevice locatorDevice;
        SLDataSource source;
        SLDataSink sink;

        ma_SLDataFormat_PCM_init__opensl(pConfig->capture.format, pConfig->capture.channels, pConfig->sampleRate, pConfig->capture.channelMap, &pcm);

        locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE;
        locatorDevice.deviceType  = SL_IODEVICE_AUDIOINPUT;
        locatorDevice.deviceID    = (pConfig->capture.pDeviceID == NULL) ? SL_DEFAULTDEVICEID_AUDIOINPUT : pConfig->capture.pDeviceID->opensl;
        locatorDevice.device      = NULL;

        source.pLocator = &locatorDevice;
        source.pFormat  = NULL;

        sink.pLocator = &queue;
        sink.pFormat  = (SLDataFormat_PCM*)&pcm;

        resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
        if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) {
            /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
            pcm.formatType    = SL_DATAFORMAT_PCM;
            pcm.numChannels   = 1;
            ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;  /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */
            pcm.bitsPerSample = 16;
            pcm.containerSize = pcm.bitsPerSample;  /* Always tightly packed for now. */
            pcm.channelMask   = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
            resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
        }

        if (resultSL != SL_RESULT_SUCCESS) {
            ma_device_uninit__opensl(pDevice);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.", ma_result_from_OpenSL(resultSL));
        }

        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE);
        if (resultSL != SL_RESULT_SUCCESS) {
            ma_device_uninit__opensl(pDevice);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.", ma_result_from_OpenSL(resultSL));
        }

        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder);
        if (resultSL != SL_RESULT_SUCCESS) {
            ma_device_uninit__opensl(pDevice);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.", ma_result_from_OpenSL(resultSL));
        }

        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture);
        if (resultSL != SL_RESULT_SUCCESS) {
            ma_device_uninit__opensl(pDevice);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", ma_result_from_OpenSL(resultSL));
        }

        resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice);
        if (resultSL != SL_RESULT_SUCCESS) {
            ma_device_uninit__opensl(pDevice);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", ma_result_from_OpenSL(resultSL));
        }

        /* The internal format is determined by the "pcm" object. */
        ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDevice->capture.internalFormat, &pDevice->capture.internalChannels, &pDevice->capture.internalSampleRate, pDevice->capture.internalChannelMap, ma_countof(pDevice->capture.internalChannelMap));

        /* Buffer. */
        periodSizeInFrames = pConfig->periodSizeInFrames;
        if (periodSizeInFrames == 0) {
            periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->capture.internalSampleRate);
        }
        pDevice->capture.internalPeriods            = pConfig->periods;
        pDevice->capture.internalPeriodSizeInFrames = periodSizeInFrames;
        pDevice->opensl.currentBufferIndexCapture   = 0;

        bufferSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels) * pDevice->capture.internalPeriods;
        pDevice->opensl.pBufferCapture = (ma_uint8*)ma__calloc_from_callbacks(bufferSizeInBytes, &pContext->allocationCallbacks);
        if (pDevice->opensl.pBufferCapture == NULL) {
            ma_device_uninit__opensl(pDevice);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY);
        }
        MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes);
    }

    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
        ma_SLDataFormat_PCM pcm;
        SLDataSource source;
        SLDataLocator_OutputMix outmixLocator;
        SLDataSink sink;

        ma_SLDataFormat_PCM_init__opensl(pConfig->playback.format, pConfig->playback.channels, pConfig->sampleRate, pConfig->playback.channelMap, &pcm);

        resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL);
        if (resultSL != SL_RESULT_SUCCESS) {
            ma_device_uninit__opensl(pDevice);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.", ma_result_from_OpenSL(resultSL));
        }

        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE);
        if (resultSL != SL_RESULT_SUCCESS) {
            ma_device_uninit__opensl(pDevice);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.", ma_result_from_OpenSL(resultSL));
        }

        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix);
        if (resultSL != SL_RESULT_SUCCESS) {
            ma_device_uninit__opensl(pDevice);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.", ma_result_from_OpenSL(resultSL));
        }

        /* Set the output device. */
        if (pConfig->playback.pDeviceID != NULL) {
            SLuint32 deviceID_OpenSL = pConfig->playback.pDeviceID->opensl;
            MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);
        }
        
        source.pLocator = &queue;
        source.pFormat  = (SLDataFormat_PCM*)&pcm;

        outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
        outmixLocator.outputMix   = (SLObjectItf)pDevice->opensl.pOutputMixObj;

        sink.pLocator = &outmixLocator;
        sink.pFormat  = NULL;

        resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
        if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) {
            /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
            pcm.formatType = SL_DATAFORMAT_PCM;
            pcm.numChannels = 2;
            ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;
            pcm.bitsPerSample = 16;
            pcm.containerSize = pcm.bitsPerSample;  /* Always tightly packed for now. */
            pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
            resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
        }

        if (resultSL != SL_RESULT_SUCCESS) {
            ma_device_uninit__opensl(pDevice);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.", ma_result_from_OpenSL(resultSL));
        }

        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE);
        if (resultSL != SL_RESULT_SUCCESS) {
            ma_device_uninit__opensl(pDevice);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.", ma_result_from_OpenSL(resultSL));
        }

        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer);
        if (resultSL != SL_RESULT_SUCCESS) {
            ma_device_uninit__opensl(pDevice);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.", ma_result_from_OpenSL(resultSL));
        }

        resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback);
        if (resultSL != SL_RESULT_SUCCESS) {
            ma_device_uninit__opensl(pDevice);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", ma_result_from_OpenSL(resultSL));
        }

        resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice);
        if (resultSL != SL_RESULT_SUCCESS) {
            ma_device_uninit__opensl(pDevice);
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", ma_result_from_OpenSL(resultSL));
        }

        /* The internal format is determined by the "pcm" object. */
        ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDevice->playback.internalFormat, &pDevice->playback.internalChannels, &pDevice->playback.internalSampleRate, pDevice->playback.internalChannelMap, ma_countof(pDevice->playback.internalChannelMap...

        /* Buffer. */
        periodSizeInFrames = pConfig->periodSizeInFrames;
        if (periodSizeInFrames == 0) {
            periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->playback.internalSampleRate);
        }
        pDevice->playback.internalPeriods            = pConfig->periods;
        pDevice->playback.internalPeriodSizeInFrames = periodSizeInFrames;
        pDevice->opensl.currentBufferIndexPlayback   = 0;

        bufferSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels) * pDevice->playback.internalPeriods;
        pDevice->opensl.pBufferPlayback = (ma_uint8*)ma__calloc_from_callbacks(bufferSizeInBytes, &pContext->allocationCallbacks);

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

        }
    }

    return MA_SUCCESS;
}

static ma_result ma_context_init__opensl(const ma_context_config* pConfig, ma_context* pContext)
{
    ma_result result;
    size_t i;
    const char* libOpenSLESNames[] = {
        "libOpenSLES.so"
    };

    MA_ASSERT(pContext != NULL);

    (void)pConfig;

    /*
    Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One
    report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime
    and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any
    references to the symbols and will hopefully skip the checks.
    */
    for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) {
        pContext->opensl.libOpenSLES = ma_dlopen(pContext, libOpenSLESNames[i]);
        if (pContext->opensl.libOpenSLES != NULL) {
            break;
        }
    }

    if (pContext->opensl.libOpenSLES == NULL) {
        return MA_NO_BACKEND;   /* Couldn't find libOpenSLES.so */
    }

    result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE);
    if (result != MA_SUCCESS) {
        return result;
    }

    result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES);
    if (result != MA_SUCCESS) {
        return result;
    }

    result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE);
    if (result != MA_SUCCESS) {
        return result;
    }

    result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD);
    if (result != MA_SUCCESS) {
        return result;
    }

    result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY);
    if (result != MA_SUCCESS) {
        return result;
    }

    result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX);
    if (result != MA_SUCCESS) {
        return result;
    }

    pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine");
    if (pContext->opensl.slCreateEngine == NULL) {
        ma_post_log_message(pContext, NULL, MA_LOG_LEVEL_INFO, "[OpenSL|ES] Cannot find symbol slCreateEngine.");
        return MA_NO_BACKEND;
    }


    /* Initialize global data first if applicable. */
    ma_spinlock_lock(&g_maOpenSLSpinlock);
    {
        result = ma_context_init_engine_nolock__opensl(pContext);
    }
    ma_spinlock_unlock(&g_maOpenSLSpinlock);

    if (result != MA_SUCCESS) {
        return result;  /* Failed to initialize the OpenSL engine. */
    }


    pContext->isBackendAsynchronous = MA_TRUE;

    pContext->onUninit        = ma_context_uninit__opensl;
    pContext->onDeviceIDEqual = ma_context_is_device_id_equal__opensl;
    pContext->onEnumDevices   = ma_context_enumerate_devices__opensl;
    pContext->onGetDeviceInfo = ma_context_get_device_info__opensl;
    pContext->onDeviceInit    = ma_device_init__opensl;
    pContext->onDeviceUninit  = ma_device_uninit__opensl;
    pContext->onDeviceStart   = ma_device_start__opensl;
    pContext->onDeviceStop    = ma_device_stop__opensl;

    return MA_SUCCESS;
}
#endif  /* OpenSL|ES */


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

Web Audio Backend

******************************************************************************/
#ifdef MA_HAS_WEBAUDIO
#include <emscripten/emscripten.h>

static ma_bool32 ma_is_capture_supported__webaudio()
{
    return EM_ASM_INT({
        return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);
    }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */
}

#ifdef __cplusplus
extern "C" {
#endif
void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
{
    if (pDevice->type == ma_device_type_duplex) {

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

                result = ma_context_init__coreaudio(&config, pContext);
            } break;
        #endif
        #ifdef MA_HAS_SNDIO
            case ma_backend_sndio:
            {
                result = ma_context_init__sndio(&config, pContext);
            } break;
        #endif
        #ifdef MA_HAS_AUDIO4
            case ma_backend_audio4:
            {
                result = ma_context_init__audio4(&config, pContext);
            } break;
        #endif
        #ifdef MA_HAS_OSS
            case ma_backend_oss:
            {
                result = ma_context_init__oss(&config, pContext);
            } break;
        #endif
        #ifdef MA_HAS_AAUDIO
            case ma_backend_aaudio:
            {
                result = ma_context_init__aaudio(&config, pContext);
            } break;
        #endif
        #ifdef MA_HAS_OPENSL
            case ma_backend_opensl:
            {
                result = ma_context_init__opensl(&config, pContext);
            } break;
        #endif
        #ifdef MA_HAS_WEBAUDIO
            case ma_backend_webaudio:
            {
                result = ma_context_init__webaudio(&config, pContext);
            } break;
        #endif
        #ifdef MA_HAS_NULL
            case ma_backend_null:
            {
                result = ma_context_init__null(&config, pContext);
            } break;
        #endif

            default: break;
        }

        /* If this iteration was successful, return. */
        if (result == MA_SUCCESS) {
            result = ma_mutex_init(&pContext->deviceEnumLock);
            if (result != MA_SUCCESS) {
                ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.", result);
            }
            result = ma_mutex_init(&pContext->deviceInfoLock);
            if (result != MA_SUCCESS) {
                ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.", result);
            }

#ifdef MA_DEBUG_OUTPUT
            printf("[miniaudio] Endian:  %s\n", ma_is_little_endian() ? "LE" : "BE");
            printf("[miniaudio] SSE2:    %s\n", ma_has_sse2()    ? "YES" : "NO");
            printf("[miniaudio] AVX2:    %s\n", ma_has_avx2()    ? "YES" : "NO");
            printf("[miniaudio] AVX512F: %s\n", ma_has_avx512f() ? "YES" : "NO");
            printf("[miniaudio] NEON:    %s\n", ma_has_neon()    ? "YES" : "NO");
#endif

            pContext->backend = backend;
            return result;
        }
    }

    /* If we get here it means an error occurred. */
    MA_ZERO_OBJECT(pContext);  /* Safety. */
    return MA_NO_BACKEND;
}

MA_API ma_result ma_context_uninit(ma_context* pContext)
{
    if (pContext == NULL) {
        return MA_INVALID_ARGS;
    }

    pContext->onUninit(pContext);

    ma_mutex_uninit(&pContext->deviceEnumLock);
    ma_mutex_uninit(&pContext->deviceInfoLock);
    ma__free_from_callbacks(pContext->pDeviceInfos, &pContext->allocationCallbacks);
    ma_context_uninit_backend_apis(pContext);

    return MA_SUCCESS;
}

MA_API size_t ma_context_sizeof()
{
    return sizeof(ma_context);
}


MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
{
    ma_result result;

    if (pContext == NULL || pContext->onEnumDevices == NULL || callback == NULL) {
        return MA_INVALID_ARGS;
    }

    ma_mutex_lock(&pContext->deviceEnumLock);
    {
        result = pContext->onEnumDevices(pContext, callback, pUserData);
    }
    ma_mutex_unlock(&pContext->deviceEnumLock);

    return result;
}


static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
{
    /*

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

    pDecoder->onUninit               = ma_decoder_internal_on_uninit__flac;
    pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__flac;
    pDecoder->pInternalDecoder       = pFlac;

    /*
    dr_flac supports reading as s32, s16 and f32. Try to do a one-to-one mapping if possible, but fall back to s32 if not. s32 is the "native" FLAC format
    since it's the only one that's truly lossless. If the internal bits per sample is <= 16 we will decode to ma_format_s16 to keep it more efficient.
    */
    if (pConfig->format == ma_format_unknown) {
        if (pFlac->bitsPerSample <= 16) {
            pDecoder->internalFormat = ma_format_s16;
        } else {
            pDecoder->internalFormat = ma_format_s32;
        }
    } else {
        if (pConfig->format == ma_format_s16 || pConfig->format == ma_format_f32) {
            pDecoder->internalFormat = pConfig->format;
        } else {
            pDecoder->internalFormat = ma_format_s32;   /* s32 as the baseline to ensure no loss of precision for 24-bit encoded files. */
        }
    }

    pDecoder->internalChannels = pFlac->channels;
    pDecoder->internalSampleRate = pFlac->sampleRate;
    ma_get_standard_channel_map(ma_standard_channel_map_flac, pDecoder->internalChannels, pDecoder->internalChannelMap);

    return MA_SUCCESS;
}
#endif  /* dr_flac_h */

/* MP3 */
#ifdef dr_mp3_h
#define MA_HAS_MP3

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

    return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
}

static drmp3_bool32 ma_decoder_internal_on_seek__mp3(void* pUserData, int offset, drmp3_seek_origin origin)
{
    ma_decoder* pDecoder = (ma_decoder*)pUserData;
    MA_ASSERT(pDecoder != NULL);

    return ma_decoder_seek_bytes(pDecoder, offset, (origin == drmp3_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
}

static ma_uint64 ma_decoder_internal_on_read_pcm_frames__mp3(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
{
    drmp3* pMP3;

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

    pMP3 = (drmp3*)pDecoder->pInternalDecoder;
    MA_ASSERT(pMP3 != NULL);

#if defined(DR_MP3_FLOAT_OUTPUT)
    MA_ASSERT(pDecoder->internalFormat == ma_format_f32);
    return drmp3_read_pcm_frames_f32(pMP3, frameCount, (float*)pFramesOut);
#else
    MA_ASSERT(pDecoder->internalFormat == ma_format_s16);
    return drmp3_read_pcm_frames_s16(pMP3, frameCount, (drmp3_int16*)pFramesOut);
#endif
}

static ma_result ma_decoder_internal_on_seek_to_pcm_frame__mp3(ma_decoder* pDecoder, ma_uint64 frameIndex)
{
    drmp3* pMP3;
    drmp3_bool32 result;

    pMP3 = (drmp3*)pDecoder->pInternalDecoder;
    MA_ASSERT(pMP3 != NULL);

    result = drmp3_seek_to_pcm_frame(pMP3, frameIndex);
    if (result) {
        return MA_SUCCESS;
    } else {
        return MA_ERROR;
    }
}

static ma_result ma_decoder_internal_on_uninit__mp3(ma_decoder* pDecoder)
{
    drmp3_uninit((drmp3*)pDecoder->pInternalDecoder);
    ma__free_from_callbacks(pDecoder->pInternalDecoder, &pDecoder->allocationCallbacks);
    return MA_SUCCESS;
}

static ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__mp3(ma_decoder* pDecoder)
{
    return drmp3_get_pcm_frame_count((drmp3*)pDecoder->pInternalDecoder);
}

static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
{
    drmp3* pMP3;
    drmp3_allocation_callbacks allocationCallbacks;

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

    pMP3 = (drmp3*)ma__malloc_from_callbacks(sizeof(*pMP3), &pDecoder->allocationCallbacks);
    if (pMP3 == NULL) {
        return MA_OUT_OF_MEMORY;
    }

    allocationCallbacks.pUserData = pDecoder->allocationCallbacks.pUserData;
    allocationCallbacks.onMalloc  = pDecoder->allocationCallbacks.onMalloc;
    allocationCallbacks.onRealloc = pDecoder->allocationCallbacks.onRealloc;
    allocationCallbacks.onFree    = pDecoder->allocationCallbacks.onFree;

    /*
    Try opening the decoder first. We always use whatever dr_mp3 reports for channel count and sample rate. The format is determined by
    the presence of DR_MP3_FLOAT_OUTPUT.
    */
    if (!drmp3_init(pMP3, ma_decoder_internal_on_read__mp3, ma_decoder_internal_on_seek__mp3, pDecoder, &allocationCallbacks)) {
        ma__free_from_callbacks(pMP3, &pDecoder->allocationCallbacks);
        return MA_ERROR;
    }

    /* If we get here it means we successfully initialized the MP3 decoder. We can now initialize the rest of the ma_decoder. */
    pDecoder->onReadPCMFrames        = ma_decoder_internal_on_read_pcm_frames__mp3;
    pDecoder->onSeekToPCMFrame       = ma_decoder_internal_on_seek_to_pcm_frame__mp3;
    pDecoder->onUninit               = ma_decoder_internal_on_uninit__mp3;
    pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__mp3;
    pDecoder->pInternalDecoder       = pMP3;

    /* Internal format. */
#if defined(DR_MP3_FLOAT_OUTPUT)
    pDecoder->internalFormat     = ma_format_f32;
#else
    pDecoder->internalFormat     = ma_format_s16;
#endif
    pDecoder->internalChannels   = pMP3->channels;
    pDecoder->internalSampleRate = pMP3->sampleRate;
    ma_get_standard_channel_map(ma_standard_channel_map_default, pDecoder->internalChannels, pDecoder->internalChannelMap);

    return MA_SUCCESS;
}
#endif  /* dr_mp3_h */

/* Vorbis */
#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
#define MA_HAS_VORBIS

/* The size in bytes of each chunk of data to read from the Vorbis stream. */
#define MA_VORBIS_DATA_CHUNK_SIZE  4096

typedef struct
{
    stb_vorbis* pInternalVorbis;
    ma_uint8* pData;
    size_t dataSize;
    size_t dataCapacity;
    ma_uint32 framesConsumed;  /* The number of frames consumed in ppPacketData. */
    ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */
    float** ppPacketData;
} ma_vorbis_decoder;

static ma_uint64 ma_vorbis_decoder_read_pcm_frames(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
{
    float* pFramesOutF;
    ma_uint64 totalFramesRead;

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

    pFramesOutF = (float*)pFramesOut;

    totalFramesRead = 0;
    while (frameCount > 0) {
        /* Read from the in-memory buffer first. */
        ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->framesRemaining, frameCount);  /* Safe cast because pVorbis->framesRemaining is 32-bit. */

        if (pFramesOut != NULL) {
            ma_uint64 iFrame;
            for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) {
                ma_uint32 iChannel;
                for (iChannel = 0; iChannel < pDecoder->internalChannels; ++iChannel) {
                    pFramesOutF[iChannel] = pVorbis->ppPacketData[iChannel][pVorbis->framesConsumed+iFrame];   
                }
                pFramesOutF += pDecoder->internalChannels;
            }
        }

        pVorbis->framesConsumed  += framesToReadFromCache;
        pVorbis->framesRemaining -= framesToReadFromCache;
        frameCount               -= framesToReadFromCache;
        totalFramesRead          += framesToReadFromCache;

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

    {}
#else
    for (; k < n; k++)
    {
        float t[4][8], *x, *y = grbuf + k;
        for (x = t[0], i = 0; i < 8; i++, x++)
        {
            float x0 = y[i*18];
            float x1 = y[(15 - i)*18];
            float x2 = y[(16 + i)*18];
            float x3 = y[(31 - i)*18];
            float t0 = x0 + x3;
            float t1 = x1 + x2;
            float t2 = (x1 - x2)*g_sec[3*i + 0];
            float t3 = (x0 - x3)*g_sec[3*i + 1];
            x[0] = t0 + t1;
            x[8] = (t0 - t1)*g_sec[3*i + 2];
            x[16] = t3 + t2;
            x[24] = (t3 - t2)*g_sec[3*i + 2];
        }
        for (x = t[0], i = 0; i < 4; i++, x += 8)
        {
            float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt;
            xt = x0 - x7; x0 += x7;
            x7 = x1 - x6; x1 += x6;
            x6 = x2 - x5; x2 += x5;
            x5 = x3 - x4; x3 += x4;
            x4 = x0 - x3; x0 += x3;
            x3 = x1 - x2; x1 += x2;
            x[0] = x0 + x1;
            x[4] = (x0 - x1)*0.70710677f;
            x5 =  x5 + x6;
            x6 = (x6 + x7)*0.70710677f;
            x7 =  x7 + xt;
            x3 = (x3 + x4)*0.70710677f;
            x5 -= x7*0.198912367f;
            x7 += x5*0.382683432f;
            x5 -= x7*0.198912367f;
            x0 = xt - x6; xt += x6;
            x[1] = (xt + x7)*0.50979561f;
            x[2] = (x4 + x3)*0.54119611f;
            x[3] = (x0 - x5)*0.60134488f;
            x[5] = (x0 + x5)*0.89997619f;
            x[6] = (x4 - x3)*1.30656302f;
            x[7] = (xt - x7)*2.56291556f;
        }
        for (i = 0; i < 7; i++, y += 4*18)
        {
            y[0*18] = t[0][i];
            y[1*18] = t[2][i] + t[3][i] + t[3][i + 1];
            y[2*18] = t[1][i] + t[1][i + 1];
            y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1];
        }
        y[0*18] = t[0][7];
        y[1*18] = t[2][7] + t[3][7];
        y[2*18] = t[1][7];
        y[3*18] = t[3][7];
    }
#endif
}
#ifndef DR_MP3_FLOAT_OUTPUT
typedef drmp3_int16 drmp3d_sample_t;
static drmp3_int16 drmp3d_scale_pcm(float sample)
{
    drmp3_int16 s;
#if DRMP3_HAVE_ARMV6
    drmp3_int32 s32 = (drmp3_int32)(sample + .5f);
    s32 -= (s32 < 0);
    s = (drmp3_int16)drmp3_clip_int16_arm(s32);
#else
    if (sample >=  32766.5) return (drmp3_int16) 32767;
    if (sample <= -32767.5) return (drmp3_int16)-32768;
    s = (drmp3_int16)(sample + .5f);
    s -= (s < 0);
#endif
    return s;
}
#else
typedef float drmp3d_sample_t;
static float drmp3d_scale_pcm(float sample)
{
    return sample*(1.f/32768.f);
}
#endif
static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z)
{
    float a;
    a  = (z[14*64] - z[    0]) * 29;
    a += (z[ 1*64] + z[13*64]) * 213;
    a += (z[12*64] - z[ 2*64]) * 459;
    a += (z[ 3*64] + z[11*64]) * 2037;
    a += (z[10*64] - z[ 4*64]) * 5153;
    a += (z[ 5*64] + z[ 9*64]) * 6574;
    a += (z[ 8*64] - z[ 6*64]) * 37489;
    a +=  z[ 7*64]             * 75038;
    pcm[0] = drmp3d_scale_pcm(a);
    z += 2;
    a  = z[14*64] * 104;
    a += z[12*64] * 1567;
    a += z[10*64] * 9727;
    a += z[ 8*64] * 64019;
    a += z[ 6*64] * -9975;
    a += z[ 4*64] * -45;
    a += z[ 2*64] * 146;
    a += z[ 0*64] * -5;
    pcm[16*nch] = drmp3d_scale_pcm(a);
}
static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins)
{
    int i;
    float *xr = xl + 576*(nch - 1);
    drmp3d_sample_t *dstr = dstl + (nch - 1);
    static const float g_win[] = {
        -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992,
        -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856,
        -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630,
        -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313,
        -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908,
        -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415,
        -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835,
        -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169,
        -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420,
        -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590,
        -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679,
        -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692,
        -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629,
        -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494,
        -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290
    };
    float *zlin = lins + 15*64;
    const float *w = g_win;
    zlin[4*15]     = xl[18*16];
    zlin[4*15 + 1] = xr[18*16];
    zlin[4*15 + 2] = xl[0];
    zlin[4*15 + 3] = xr[0];
    zlin[4*31]     = xl[1 + 18*16];
    zlin[4*31 + 1] = xr[1 + 18*16];
    zlin[4*31 + 2] = xl[1];
    zlin[4*31 + 3] = xr[1];
    drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1);
    drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1);
    drmp3d_synth_pair(dstl, nch, lins + 4*15);
    drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64);
#if DRMP3_HAVE_SIMD
    if (drmp3_have_simd()) for (i = 14; i >= 0; i--)
    {
#define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]);
#define DRMP3_V0(k) { DRMP3_VLOAD(k) b =               DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a =               DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1));  }
#define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); }
#define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); }
        drmp3_f4 a, b;
        zlin[4*i]     = xl[18*(31 - i)];
        zlin[4*i + 1] = xr[18*(31 - i)];
        zlin[4*i + 2] = xl[1 + 18*(31 - i)];
        zlin[4*i + 3] = xr[1 + 18*(31 - i)];
        zlin[4*i + 64] = xl[1 + 18*(1 + i)];
        zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)];
        zlin[4*i - 64 + 2] = xl[18*(1 + i)];
        zlin[4*i - 64 + 3] = xr[18*(1 + i)];
        DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7)
        {
#ifndef DR_MP3_FLOAT_OUTPUT
#if DRMP3_HAVE_SSE
            static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
            static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
            __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
                                           _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
            dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
            dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
            dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
            dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
            dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
            dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
            dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
            dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
#else
            int16x4_t pcma, pcmb;
            a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
            b = DRMP3_VADD(b, DRMP3_VSET(0.5f));
            pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0)))));
            pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0)))));
            vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1);
            vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1);
            vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0);
            vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0);
            vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3);
            vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3);
            vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2);
            vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);
#endif
#else
            static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
            a = DRMP3_VMUL(a, g_scale);
            b = DRMP3_VMUL(b, g_scale);
#if DRMP3_HAVE_SSE
            _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)));
            _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1)));
            _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)));
            _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0)));
            _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)));
            _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3)));
            _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)));
            _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2)));
#else
            vst1q_lane_f32(dstr + (15 - i)*nch, a, 1);
            vst1q_lane_f32(dstr + (17 + i)*nch, b, 1);
            vst1q_lane_f32(dstl + (15 - i)*nch, a, 0);
            vst1q_lane_f32(dstl + (17 + i)*nch, b, 0);
            vst1q_lane_f32(dstr + (47 - i)*nch, a, 3);
            vst1q_lane_f32(dstr + (49 + i)*nch, b, 3);
            vst1q_lane_f32(dstl + (47 - i)*nch, a, 2);
            vst1q_lane_f32(dstl + (49 + i)*nch, b, 2);
#endif
#endif
        }
    } else
#endif
#ifdef DR_MP3_ONLY_SIMD
    {}
#else
    for (i = 14; i >= 0; i--)
    {

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

        pFilePathTemp = pFilePath;
        DRMP3_ZERO_OBJECT(&mbs);
        wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
        {
            size_t i = 0;
            for (;;) {
                if (pOpenMode[i] == 0) {
                    pOpenModeMB[i] = '\0';
                    break;
                }
                pOpenModeMB[i] = (char)pOpenMode[i];
                i += 1;
            }
        }
        *ppFile = fopen(pFilePathMB, pOpenModeMB);
        drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
    }
    if (*ppFile == NULL) {
        return DRMP3_ERROR;
    }
#endif
    return DRMP3_SUCCESS;
}
static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
{
    return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
}
static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin)
{
    return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
}
DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
{
    FILE* pFile;
    if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) {
        return DRMP3_FALSE;
    }
    return drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
}
DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
{
    FILE* pFile;
    if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) {
        return DRMP3_FALSE;
    }
    return drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
}
#endif
DRMP3_API void drmp3_uninit(drmp3* pMP3)
{
    if (pMP3 == NULL) {
        return;
    }
#ifndef DR_MP3_NO_STDIO
    if (pMP3->onRead == drmp3__on_read_stdio) {
        fclose((FILE*)pMP3->pUserData);
    }
#endif
    drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
}
#if defined(DR_MP3_FLOAT_OUTPUT)
static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount)
{
    drmp3_uint64 i;
    drmp3_uint64 i4;
    drmp3_uint64 sampleCount4;
    i = 0;
    sampleCount4 = sampleCount >> 2;
    for (i4 = 0; i4 < sampleCount4; i4 += 1) {
        float x0 = src[i+0];
        float x1 = src[i+1];
        float x2 = src[i+2];
        float x3 = src[i+3];
        x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
        x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
        x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
        x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
        x0 = x0 * 32767.0f;
        x1 = x1 * 32767.0f;
        x2 = x2 * 32767.0f;
        x3 = x3 * 32767.0f;
        dst[i+0] = (drmp3_int16)x0;
        dst[i+1] = (drmp3_int16)x1;
        dst[i+2] = (drmp3_int16)x2;
        dst[i+3] = (drmp3_int16)x3;
        i += 4;
    }
    for (; i < sampleCount; i += 1) {
        float x = src[i];
        x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
        x = x * 32767.0f;
        dst[i] = (drmp3_int16)x;
    }
}
#endif
#if !defined(DR_MP3_FLOAT_OUTPUT)
static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount)
{
    drmp3_uint64 i;
    for (i = 0; i < sampleCount; i += 1) {
        float x = (float)src[i];
        x = x * 0.000030517578125f;
        dst[i] = x;
    }
}
#endif
static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut)
{
    drmp3_uint64 totalFramesRead = 0;
    DRMP3_ASSERT(pMP3 != NULL);
    DRMP3_ASSERT(pMP3->onRead != NULL);
    while (framesToRead > 0) {
        drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead);
        if (pBufferOut != NULL) {
        #if defined(DR_MP3_FLOAT_OUTPUT)
            float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut,          sizeof(float) * totalFramesRead                   * pMP3->channels);
            float* pFramesInF32  = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
            DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels);
        #else
            drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut,          sizeof(drmp3_int16) * totalFramesRead                   * pMP3->channels);
            drmp3_int16* pFramesInS16  = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
            DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels);
        #endif
        }
        pMP3->currentPCMFrame              += framesToConsume;
        pMP3->pcmFramesConsumedInMP3Frame  += framesToConsume;
        pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume;
        totalFramesRead                    += framesToConsume;
        framesToRead                       -= framesToConsume;
        if (framesToRead == 0) {
            break;
        }
        DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0);
        if (drmp3_decode_next_frame(pMP3) == 0) {
            break;
        }
    }
    return totalFramesRead;
}
DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut)
{
    if (pMP3 == NULL || pMP3->onRead == NULL) {
        return 0;
    }
#if defined(DR_MP3_FLOAT_OUTPUT)
    return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
#else
    {
        drmp3_int16 pTempS16[8192];
        drmp3_uint64 totalPCMFramesRead = 0;
        while (totalPCMFramesRead < framesToRead) {
            drmp3_uint64 framesJustRead;
            drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
            drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels;
            if (framesToReadNow > framesRemaining) {
                framesToReadNow = framesRemaining;
            }
            framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16);
            if (framesJustRead == 0) {
                break;
            }
            drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels);
            totalPCMFramesRead += framesJustRead;
        }
        return totalPCMFramesRead;
    }
#endif
}
DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut)
{
    if (pMP3 == NULL || pMP3->onRead == NULL) {
        return 0;
    }
#if !defined(DR_MP3_FLOAT_OUTPUT)
    return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
#else
    {
        float pTempF32[4096];
        drmp3_uint64 totalPCMFramesRead = 0;
        while (totalPCMFramesRead < framesToRead) {
            drmp3_uint64 framesJustRead;
            drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
            drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels;
            if (framesToReadNow > framesRemaining) {
                framesToReadNow = framesRemaining;
            }
            framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32);
            if (framesJustRead == 0) {
                break;
            }
            drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels);
            totalPCMFramesRead += framesJustRead;
        }
        return totalPCMFramesRead;
    }
#endif
}
static void drmp3_reset(drmp3* pMP3)
{
    DRMP3_ASSERT(pMP3 != NULL);
    pMP3->pcmFramesConsumedInMP3Frame = 0;
    pMP3->pcmFramesRemainingInMP3Frame = 0;
    pMP3->currentPCMFrame = 0;
    pMP3->dataSize = 0;
    pMP3->atEnd = DRMP3_FALSE;
    drmp3dec_init(&pMP3->decoder);
}
static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3)
{
    DRMP3_ASSERT(pMP3 != NULL);
    DRMP3_ASSERT(pMP3->onSeek != NULL);
    if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) {
        return DRMP3_FALSE;
    }
    drmp3_reset(pMP3);
    return DRMP3_TRUE;
}
static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset)
{
    drmp3_uint64 framesRead;
#if defined(DR_MP3_FLOAT_OUTPUT)
    framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
#else
    framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL);
#endif
    if (framesRead != frameOffset) {
        return DRMP3_FALSE;
    }
    return DRMP3_TRUE;
}
static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex)
{
    DRMP3_ASSERT(pMP3 != NULL);
    if (frameIndex == pMP3->currentPCMFrame) {
        return DRMP3_TRUE;
    }
    if (frameIndex < pMP3->currentPCMFrame) {
        if (!drmp3_seek_to_start_of_stream(pMP3)) {
            return DRMP3_FALSE;
        }
    }
    DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame);
    return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame));
}
static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex)
{
    drmp3_uint32 iSeekPoint;
    DRMP3_ASSERT(pSeekPointIndex != NULL);
    *pSeekPointIndex = 0;
    if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) {
        return DRMP3_FALSE;
    }
    for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) {
        if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) {
            break;
        }
        *pSeekPointIndex = iSeekPoint;
    }
    return DRMP3_TRUE;
}
static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex)
{
    drmp3_seek_point seekPoint;
    drmp3_uint32 priorSeekPointIndex;
    drmp3_uint16 iMP3Frame;
    drmp3_uint64 leftoverFrames;
    DRMP3_ASSERT(pMP3 != NULL);
    DRMP3_ASSERT(pMP3->pSeekPoints != NULL);
    DRMP3_ASSERT(pMP3->seekPointCount > 0);
    if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) {
        seekPoint = pMP3->pSeekPoints[priorSeekPointIndex];
    } else {
        seekPoint.seekPosInBytes     = 0;
        seekPoint.pcmFrameIndex      = 0;
        seekPoint.mp3FramesToDiscard = 0;
        seekPoint.pcmFramesToDiscard = 0;
    }
    if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) {
        return DRMP3_FALSE;
    }
    drmp3_reset(pMP3);



( run in 1.673 second using v1.01-cache-2.11-cpan-99c4e6809bf )