App-MHFS

 view release on metacpan or  search on metacpan

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

when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at
compile time with `MA_NO_NULL`.

The returned backends are determined based on compile time settings, not the platform it's currently running on. For
example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have
PulseAudio installed.


Example 1
---------
The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is
given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends.
Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios.

```
ma_backend enabledBackends[MA_BACKEND_COUNT];
size_t enabledBackendCount;

result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount);
if (result != MA_SUCCESS) {
    // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid.
}
```


See Also
--------
ma_is_backend_enabled()
*/
MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount);

/*
Determines whether or not loopback mode is support by a backend.
*/
MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend);

#endif  /* MA_NO_DEVICE_IO */


#ifndef MA_NO_THREADING

/*
Locks a spinlock.
*/
MA_API ma_result ma_spinlock_lock(ma_spinlock* pSpinlock);

/*
Locks a spinlock, but does not yield() when looping.
*/
MA_API ma_result ma_spinlock_lock_noyield(ma_spinlock* pSpinlock);

/*
Unlocks a spinlock.
*/
MA_API ma_result ma_spinlock_unlock(ma_spinlock* pSpinlock);


/*
Creates a mutex.

A mutex must be created from a valid context. A mutex is initially unlocked.
*/
MA_API ma_result ma_mutex_init(ma_mutex* pMutex);

/*
Deletes a mutex.
*/
MA_API void ma_mutex_uninit(ma_mutex* pMutex);

/*
Locks a mutex with an infinite timeout.
*/
MA_API void ma_mutex_lock(ma_mutex* pMutex);

/*
Unlocks a mutex.
*/
MA_API void ma_mutex_unlock(ma_mutex* pMutex);


/*
Initializes an auto-reset event.
*/
MA_API ma_result ma_event_init(ma_event* pEvent);

/*
Uninitializes an auto-reset event.
*/
MA_API void ma_event_uninit(ma_event* pEvent);

/*
Waits for the specified auto-reset event to become signalled.
*/
MA_API ma_result ma_event_wait(ma_event* pEvent);

/*
Signals the specified auto-reset event.
*/
MA_API ma_result ma_event_signal(ma_event* pEvent);
#endif  /* MA_NO_THREADING */


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

Utiltities

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

/*
Adjust buffer size based on a scaling factor.

This just multiplies the base size by the scaling factor, making sure it's a size of at least 1.
*/
MA_API ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale);

/*
Calculates a buffer size in milliseconds from the specified number of frames and sample rate.
*/
MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate);

/*

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

    #if defined(__GNUC__)
        #pragma GCC diagnostic pop
    #endif
#endif
typedef int                     c89atomic_memory_order;
typedef unsigned char           c89atomic_bool;
typedef unsigned char           c89atomic_flag;
#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
#ifdef _WIN32
#ifdef _WIN64
#define C89ATOMIC_64BIT
#else
#define C89ATOMIC_32BIT
#endif
#endif
#endif
#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
#ifdef __GNUC__
#ifdef __LP64__
#define C89ATOMIC_64BIT
#else
#define C89ATOMIC_32BIT
#endif
#endif
#endif
#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT)
#include <stdint.h>
#if INTPTR_MAX == INT64_MAX
#define C89ATOMIC_64BIT
#else
#define C89ATOMIC_32BIT
#endif
#endif
#if defined(__x86_64__) || defined(_M_X64)
#define C89ATOMIC_X64
#elif defined(__i386) || defined(_M_IX86)
#define C89ATOMIC_X86
#elif defined(__arm__) || defined(_M_ARM)
#define C89ATOMIC_ARM
#endif
#ifdef _MSC_VER
    #define C89ATOMIC_INLINE __forceinline
#elif defined(__GNUC__)
    #if defined(__STRICT_ANSI__)
        #define C89ATOMIC_INLINE __inline__ __attribute__((always_inline))
    #else
        #define C89ATOMIC_INLINE inline __attribute__((always_inline))
    #endif
#else
    #define C89ATOMIC_INLINE
#endif
#if defined(_MSC_VER)
    #define c89atomic_memory_order_relaxed  0
    #define c89atomic_memory_order_consume  1
    #define c89atomic_memory_order_acquire  2
    #define c89atomic_memory_order_release  3
    #define c89atomic_memory_order_acq_rel  4
    #define c89atomic_memory_order_seq_cst  5
    #if _MSC_VER >= 1400
        #include <intrin.h>
        #define c89atomic_exchange_explicit_8( dst, src, order) (c89atomic_uint8 )_InterlockedExchange8 ((volatile char* )dst, (char )src)
        #define c89atomic_exchange_explicit_16(dst, src, order) (c89atomic_uint16)_InterlockedExchange16((volatile short*)dst, (short)src)
        #define c89atomic_exchange_explicit_32(dst, src, order) (c89atomic_uint32)_InterlockedExchange  ((volatile long* )dst, (long )src)
    #if defined(C89ATOMIC_64BIT)
        #define c89atomic_exchange_explicit_64(dst, src, order) (c89atomic_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src)
    #endif
        #define c89atomic_fetch_add_explicit_8( dst, src, order) (c89atomic_uint8 )_InterlockedExchangeAdd8 ((volatile char* )dst, (char )src)
        #define c89atomic_fetch_add_explicit_16(dst, src, order) (c89atomic_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src)
        #define c89atomic_fetch_add_explicit_32(dst, src, order) (c89atomic_uint32)_InterlockedExchangeAdd  ((volatile long* )dst, (long )src)
    #if defined(C89ATOMIC_64BIT)
        #define c89atomic_fetch_add_explicit_64(dst, src, order) (c89atomic_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src)
    #endif
        #define c89atomic_compare_and_swap_8( dst, expected, desired) (c89atomic_uint8 )_InterlockedCompareExchange8 ((volatile char*     )dst, (char     )desired, (char     )expected)
        #define c89atomic_compare_and_swap_16(dst, expected, desired) (c89atomic_uint16)_InterlockedCompareExchange16((volatile short*    )dst, (short    )desired, (short    )expected)
        #define c89atomic_compare_and_swap_32(dst, expected, desired) (c89atomic_uint32)_InterlockedCompareExchange  ((volatile long*     )dst, (long     )desired, (long     )expected)
        #define c89atomic_compare_and_swap_64(dst, expected, desired) (c89atomic_uint64)_InterlockedCompareExchange64((volatile long long*)dst, (long long)desired, (long long)expected)
    #if defined(C89ATOMIC_X64)
        #define c89atomic_thread_fence(order)   __faststorefence()
    #else
        static C89ATOMIC_INLINE void c89atomic_thread_fence(c89atomic_memory_order order)
        {
            volatile c89atomic_uint32 barrier = 0;
            (void)order;
            c89atomic_fetch_add_explicit_32(&barrier, 0, order);
        }
    #endif
    #else
        #if defined(__i386) || defined(_M_IX86)
            static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(int order)
            {
                volatile c89atomic_uint32 barrier;
                (void)order;
                __asm {
                    xchg barrier, eax
                }
            }
            static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, int order)
            {
                (void)order;
                __asm {
                    mov ecx, dst
                    mov al,  src
                    lock xchg [ecx], al
                }
            }
            static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, int order)
            {
                (void)order;
                __asm {
                    mov ecx, dst
                    mov ax,  src
                    lock xchg [ecx], ax
                }
            }
            static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, int order)
            {
                (void)order;
                __asm {
                    mov ecx, dst
                    mov eax, src
                    lock xchg [ecx], eax
                }
            }
            static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, int order)
            {
                (void)order;
                __asm {
                    mov ecx, dst
                    mov al,  src
                    lock xadd [ecx], al
                }
            }
            static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, int order)
            {
                (void)order;
                __asm {

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

        }

        resultMM = ((MA_PFN_waveOutOpen)pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC);
        if (resultMM != MMSYSERR_NOERROR) {
            errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
            goto on_error;
        }

        pDevice->playback.internalFormat             = ma_format_from_WAVEFORMATEX(&wf);
        pDevice->playback.internalChannels           = wf.nChannels;
        pDevice->playback.internalSampleRate         = wf.nSamplesPerSec;
        ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
        pDevice->playback.internalPeriods            = pConfig->periods;
        pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(periodSizeInMilliseconds, pDevice->playback.internalSampleRate);
    }

    /*
    The heap allocated data is allocated like so:
    
    [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer]
    */
    heapSize = 0;
    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
        heapSize += sizeof(WAVEHDR)*pDevice->capture.internalPeriods + (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
    }
    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
        heapSize += sizeof(WAVEHDR)*pDevice->playback.internalPeriods + (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
    }

    pDevice->winmm._pHeapData = (ma_uint8*)ma__calloc_from_callbacks(heapSize, &pContext->allocationCallbacks);
    if (pDevice->winmm._pHeapData == NULL) {
        errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY;
        goto on_error;
    }

    MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize);

    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
        ma_uint32 iPeriod;

        if (pConfig->deviceType == ma_device_type_capture) {
            pDevice->winmm.pWAVEHDRCapture            = pDevice->winmm._pHeapData;
            pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods));
        } else {
            pDevice->winmm.pWAVEHDRCapture            = pDevice->winmm._pHeapData;
            pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods + pDevice->playback.internalPeriods));
        }

        /* Prepare headers. */
        for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
            ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalFormat, pDevice->capture.internalChannels);

            ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData         = (LPSTR)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod));
            ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes;
            ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags        = 0L;
            ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops        = 0L;
            ((MA_PFN_waveInPrepareHeader)pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));

            /*
            The user data of the WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means
            it's unlocked and available for writing. A value of 1 means it's locked.
            */
            ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0;
        }
    }
    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
        ma_uint32 iPeriod;

        if (pConfig->deviceType == ma_device_type_playback) {
            pDevice->winmm.pWAVEHDRPlayback            = pDevice->winmm._pHeapData;
            pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*pDevice->playback.internalPeriods);
        } else {
            pDevice->winmm.pWAVEHDRPlayback            = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods));
            pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods + pDevice->playback.internalPeriods)) + (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeri...
        }

        /* Prepare headers. */
        for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
            ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalFormat, pDevice->playback.internalChannels);

            ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData         = (LPSTR)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod));
            ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes;
            ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags        = 0L;
            ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops        = 0L;
            ((MA_PFN_waveOutPrepareHeader)pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));

            /*
            The user data of the WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means
            it's unlocked and available for writing. A value of 1 means it's locked.
            */
            ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0;
        }
    }

    return MA_SUCCESS;

on_error:
    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
        if (pDevice->winmm.pWAVEHDRCapture != NULL) {
            ma_uint32 iPeriod;
            for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
                ((MA_PFN_waveInUnprepareHeader)pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
            }
        }

        ((MA_PFN_waveInClose)pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
    }

    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
        if (pDevice->winmm.pWAVEHDRCapture != NULL) {
            ma_uint32 iPeriod;
            for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
                ((MA_PFN_waveOutUnprepareHeader)pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
            }
        }

        ((MA_PFN_waveOutClose)pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
    }

    ma__free_from_callbacks(pDevice->winmm._pHeapData, &pContext->allocationCallbacks);
    return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, errorMsg, errorCode);
}

static ma_result ma_device_stop__winmm(ma_device* pDevice)
{
    MMRESULT resultMM;

    MA_ASSERT(pDevice != NULL);

    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
        if (pDevice->winmm.hDeviceCapture == NULL) {
            return MA_INVALID_ARGS;
        }

        resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDeviceCapture);
        if (resultMM != MMSYSERR_NOERROR) {
            ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset capture device.", ma_result_from_MMRESULT(resultMM));
        }
    }

    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
        ma_uint32 iPeriod;
        WAVEHDR* pWAVEHDR;

        if (pDevice->winmm.hDevicePlayback == NULL) {
            return MA_INVALID_ARGS;
        }

        /* We need to drain the device. To do this we just loop over each header and if it's locked just wait for the event. */
        pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
        for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) {
            if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */
                if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
                    break;  /* An error occurred so just abandon ship and stop the device without draining. */
                }

                pWAVEHDR[iPeriod].dwUser = 0;
            }
        }

        resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
        if (resultMM != MMSYSERR_NOERROR) {
            ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset playback device.", ma_result_from_MMRESULT(resultMM));
        }
    }

    return MA_SUCCESS;
}

static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
{
    ma_result result = MA_SUCCESS;
    MMRESULT resultMM;
    ma_uint32 totalFramesWritten;
    WAVEHDR* pWAVEHDR;

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

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

    pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;

    /* Keep processing as much data as possible. */
    totalFramesWritten = 0;
    while (totalFramesWritten < frameCount) {
        /* If the current header has some space available we need to write part of it. */
        if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */
            /*
            This header has room in it. We copy as much of it as we can. If we end up fully consuming the buffer we need to
            write it out and move on to the next iteration.
            */
            ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
            ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback;

            ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten));
            const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf);
            void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf);
            MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);

            pDevice->winmm.headerFramesConsumedPlayback += framesToCopy;
            totalFramesWritten += framesToCopy;

            /* If we've consumed the buffer entirely we need to write it out to the device. */
            if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) {
                pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1;            /* 1 = locked. */
                pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */

                /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
                ResetEvent((HANDLE)pDevice->winmm.hEventPlayback);

                /* The device will be started here. */
                resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(WAVEHDR));
                if (resultMM != MMSYSERR_NOERROR) {
                    result = ma_result_from_MMRESULT(resultMM);
                    ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed.", result);
                    break;
                }

                /* Make sure we move to the next header. */
                pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods;
                pDevice->winmm.headerFramesConsumedPlayback = 0;
            }

            /* If at this point we have consumed the entire input buffer we can return. */
            MA_ASSERT(totalFramesWritten <= frameCount);
            if (totalFramesWritten == frameCount) {
                break;
            }

            /* Getting here means there's more to process. */
            continue;
        }

        /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */
        if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
            result = MA_ERROR;
            break;
        }

        /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
        if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & WHDR_DONE) != 0) {
            pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0;    /* 0 = unlocked (make it available for writing). */
            pDevice->winmm.headerFramesConsumedPlayback = 0;
        }

        /* If the device has been stopped we need to break. */
        if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
            break;
        }
    }

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

    return result;
}

static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
{
    ma_result result = MA_SUCCESS;
    MMRESULT resultMM;
    ma_uint32 totalFramesRead;
    WAVEHDR* pWAVEHDR;

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

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

    pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;

    /* Keep processing as much data as possible. */
    totalFramesRead = 0;
    while (totalFramesRead < frameCount) {
        /* If the current header has some space available we need to write part of it. */
        if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */
            /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */
            ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
            ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture;

            ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead));
            const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf);
            void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf);
            MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);

            pDevice->winmm.headerFramesConsumedCapture += framesToCopy;
            totalFramesRead += framesToCopy;

            /* If we've consumed the buffer entirely we need to add it back to the device. */
            if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) {
                pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1;            /* 1 = locked. */
                pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */

                /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
                ResetEvent((HANDLE)pDevice->winmm.hEventCapture);

                /* The device will be started here. */
                resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(WAVEHDR));
                if (resultMM != MMSYSERR_NOERROR) {
                    result = ma_result_from_MMRESULT(resultMM);
                    ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed.", result);
                    break;
                }

                /* Make sure we move to the next header. */
                pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods;
                pDevice->winmm.headerFramesConsumedCapture = 0;
            }

            /* If at this point we have filled the entire input buffer we can return. */
            MA_ASSERT(totalFramesRead <= frameCount);
            if (totalFramesRead == frameCount) {
                break;
            }

            /* Getting here means there's more to process. */
            continue;
        }

        /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */
        if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
            result = MA_ERROR;
            break;
        }

        /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
        if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & WHDR_DONE) != 0) {
            pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0;    /* 0 = unlocked (make it available for reading). */
            pDevice->winmm.headerFramesConsumedCapture = 0;
        }

        /* If the device has been stopped we need to break. */
        if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
            break;
        }
    }

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

    return result;
}

static ma_result ma_device_main_loop__winmm(ma_device* pDevice)
{
    ma_result result = MA_SUCCESS;
    ma_bool32 exitLoop = MA_FALSE;
    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_ASSERT(pDevice != NULL);

    /* The capture device needs to be started immediately. */
    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
        MMRESULT resultMM;
        WAVEHDR* pWAVEHDR;
        ma_uint32 iPeriod;

        pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;

        /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
        ResetEvent((HANDLE)pDevice->winmm.hEventCapture);

        /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */
        for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
            resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
            if (resultMM != MMSYSERR_NOERROR) {
                return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture.", ma_result_from_MMRESULT(resultMM));
            }

            /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */
            pWAVEHDR[iPeriod].dwUser = 1;   /* 1 = locked. */
        }

        /* Capture devices need to be explicitly started, unlike playback devices. */
        resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDeviceCapture);
        if (resultMM != MMSYSERR_NOERROR) {
            return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device.", ma_result_from_MMRESULT(resultMM));
        }
    }


    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_uint32 capturedDeviceFramesRemaining;
                    ma_uint32 capturedDeviceFramesProcessed;
                    ma_uint32 capturedDeviceFramesToProcess;
                    ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
                    if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
                        capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
                    }

                    result = ma_device_read__winmm(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
                    if (result != MA_SUCCESS) {
                        exitLoop = MA_TRUE;
                        break;
                    }

                    capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
                    capturedDeviceFramesProcessed = 0;

                    for (;;) {
                        ma_uint8  capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
                        ma_uint8  playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
                        ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
                        ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
                        ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
                        ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
                        ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));

                        /* Convert capture data from device format to client format. */
                        result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
                        if (result != MA_SUCCESS) {
                            break;
                        }

                        /*
                        If we weren't able to generate any output frames it must mean we've exhaused all of our input. The only time this would not be the case is if capturedClientData was too small
                        which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
                        */
                        if (capturedClientFramesToProcessThisIteration == 0) {
                            break;
                        }



( run in 1.132 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )