App-MHFS

 view release on metacpan or  search on metacpan

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN


    ```c
    typedef struct
    {
        ma_async_notification_callbacks cb;
        void* pMyData;
    } my_notification;

    void my_notification_callback(ma_async_notification* pNotification)
    {
        my_notification* pMyNotification = (my_notification*)pNotification;

        // Do something in response to the sound finishing loading.
    }

    ...

    my_notification myCallback;
    myCallback.cb.onSignal = my_notification_callback;
    myCallback.pMyData     = pMyData;

    ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init();
    notifications.done.pNotification = &myCallback;

    ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, &notifications, &mySound);
    ```

In the example above we just extend the `ma_async_notification_callbacks` object and pass an
instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with
the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same
time and they should both work as expected. If using the `pNotification` system, you need to ensure
your `ma_async_notification_callbacks` object stays valid.



6.2. Resource Manager Implementation Details
--------------------------------------------
Resources are managed in two main ways:

  * By storing the entire sound inside an in-memory buffer (referred to as a data buffer)
  * By streaming audio data on the fly (referred to as a data stream)

A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or
data stream, depending on whether or not the data source was initialized with the
`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a
`ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer`
object. Both of these objects are data sources which means they can be used with any
`ma_data_source_*()` API.

Another major feature of the resource manager is the ability to asynchronously decode audio files.
This relieves the audio thread of time-consuming decoding which can negatively affect scalability
due to the audio thread needing to complete it's work extremely quickly to avoid glitching.
Asynchronous decoding is achieved through a job system. There is a central multi-producer,
multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is
posted to the queue which is then read by a job thread. The number of job threads can be
configured for improved scalability, and job threads can all run in parallel without needing to
worry about the order of execution (how this is achieved is explained below).

When a sound is being loaded asynchronously, playback can begin before the sound has been fully
decoded. This enables the application to start playback of the sound quickly, while at the same
time allowing to resource manager to keep loading in the background. Since there may be less
threads than the number of sounds being loaded at a given time, a simple scheduling system is used
to keep decoding time balanced and fair. The resource manager solves this by splitting decoding
into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a
new job will be posted to start decoding the next page. By dividing up decoding into pages, an
individual sound shouldn't ever delay every other sound from having their first page decoded. Of
course, when loading many sounds at the same time, there will always be an amount of time required
to process jobs in the queue so in heavy load situations there will still be some delay. To
determine if a data source is ready to have some frames read, use
`ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames
available starting from the current position.


6.2.1. Job Queue
----------------
The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity.
This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety.
Only a fixed number of jobs can be allocated and inserted into the queue which is done through a
lock-free data structure for allocating an index into a fixed sized array, with reference counting
for mitigation of the ABA problem. The reference count is 32-bit.

For many types of jobs it's important that they execute in a specific order. In these cases, jobs
are executed serially. For the resource manager, serial execution of jobs is only required on a
per-object basis (per data buffer or per data stream). Each of these objects stores an execution
counter. When a job is posted it is associated with an execution counter. When the job is
processed, it checks if the execution counter of the job equals the execution counter of the
owning object and if so, processes the job. If the counters are not equal, the job will be posted
back onto the job queue for later processing. When the job finishes processing the execution order
of the main object is incremented. This system means the no matter how many job threads are
executing, decoding of an individual sound will always get processed serially. The advantage to
having multiple threads comes into play when loading multiple sounds at the same time.

The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve
thread-safety for a very small section of code. This is only relevant when the resource manager
uses more than one job thread. If only using a single job thread, which is the default, the
lock should never actually wait in practice. The amount of time spent locking should be quite
short, but it's something to be aware of for those who have pedantic lock-free requirements and
need to use more than one job thread. There are plans to remove this lock in a future version.

In addition, posting a job will release a semaphore, which on Win32 is implemented with
`ReleaseSemaphore` and on POSIX platforms via a condition variable:

    ```c
    pthread_mutex_lock(&pSemaphore->lock);
    {
        pSemaphore->value += 1;
        pthread_cond_signal(&pSemaphore->cond);
    }
    pthread_mutex_unlock(&pSemaphore->lock);
    ```

Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid
this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING`
flag) and implement your own job processing routine (see the "Resource Manager" section above for
details on how to do this).



6.2.2. Data Buffers
-------------------
When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN

MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks);
MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob);
MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Returns MA_CANCELLED if the next job is a quit job. */



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

DEVICE I/O
==========

This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc.

*************************************************************************************************************************************************************
************************************************************************************************************************************************************/
#ifndef MA_NO_DEVICE_IO
/* Some backends are only supported on certain platforms. */
#if defined(MA_WIN32)
    #define MA_SUPPORT_WASAPI
    #if defined(MA_WIN32_DESKTOP)  /* DirectSound and WinMM backends are only supported on desktops. */
        #define MA_SUPPORT_DSOUND
        #define MA_SUPPORT_WINMM
        #define MA_SUPPORT_JACK    /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */
    #endif
#endif
#if defined(MA_UNIX)
    #if defined(MA_LINUX)
        #if !defined(MA_ANDROID)   /* ALSA is not supported on Android. */
            #define MA_SUPPORT_ALSA
        #endif
    #endif
    #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN)
        #define MA_SUPPORT_PULSEAUDIO
        #define MA_SUPPORT_JACK
    #endif
    #if defined(MA_ANDROID)
        #define MA_SUPPORT_AAUDIO
        #define MA_SUPPORT_OPENSL
    #endif
    #if defined(__OpenBSD__)        /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */
        #define MA_SUPPORT_SNDIO    /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */
    #endif
    #if defined(__NetBSD__) || defined(__OpenBSD__)
        #define MA_SUPPORT_AUDIO4   /* Only support audio(4) on platforms with known support. */
    #endif
    #if defined(__FreeBSD__) || defined(__DragonFly__)
        #define MA_SUPPORT_OSS      /* Only support OSS on specific platforms with known support. */
    #endif
#endif
#if defined(MA_APPLE)
    #define MA_SUPPORT_COREAUDIO
#endif
#if defined(MA_EMSCRIPTEN)
    #define MA_SUPPORT_WEBAUDIO
#endif

/* All platforms should support custom backends. */
#define MA_SUPPORT_CUSTOM

/* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */
#if !defined(MA_EMSCRIPTEN)
#define MA_SUPPORT_NULL
#endif


#if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI))
    #define MA_HAS_WASAPI
#endif
#if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND))
    #define MA_HAS_DSOUND
#endif
#if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM))
    #define MA_HAS_WINMM
#endif
#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA))
    #define MA_HAS_ALSA
#endif
#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO))
    #define MA_HAS_PULSEAUDIO
#endif
#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK))
    #define MA_HAS_JACK
#endif
#if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO))
    #define MA_HAS_COREAUDIO
#endif
#if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO))
    #define MA_HAS_SNDIO
#endif
#if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4))
    #define MA_HAS_AUDIO4
#endif
#if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS))
    #define MA_HAS_OSS
#endif
#if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO))
    #define MA_HAS_AAUDIO
#endif
#if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL))
    #define MA_HAS_OPENSL
#endif
#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO))
    #define MA_HAS_WEBAUDIO
#endif
#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM))
    #define MA_HAS_CUSTOM
#endif
#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL))
    #define MA_HAS_NULL
#endif

typedef enum
{
    ma_device_state_uninitialized = 0,
    ma_device_state_stopped       = 1,  /* The device's default state after initialization. */
    ma_device_state_started       = 2,  /* The device is started and is requesting and/or delivering audio data. */
    ma_device_state_starting      = 3,  /* Transitioning from a stopped state to started. */
    ma_device_state_stopping      = 4   /* Transitioning from a started state to stopped. */
} ma_device_state;

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN

            pDevice->null_device.operationResult = MA_SUCCESS;
            ma_event_signal(&pDevice->null_device.operationCompletionEvent);
            ma_semaphore_release(&pDevice->null_device.operationSemaphore);
            continue;
        }

        /* Suspending the device means we need to stop the timer and just continue the loop. */
        if (operation == MA_DEVICE_OP_SUSPEND__NULL) {
            /* We need to add the current run time to the prior run time, then reset the timer. */
            pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
            ma_timer_init(&pDevice->null_device.timer);

            /* We're done. */
            pDevice->null_device.operationResult = MA_SUCCESS;
            ma_event_signal(&pDevice->null_device.operationCompletionEvent);
            ma_semaphore_release(&pDevice->null_device.operationSemaphore);
            continue;
        }

        /* Killing the device means we need to get out of this loop so that this thread can terminate. */
        if (operation == MA_DEVICE_OP_KILL__NULL) {
            pDevice->null_device.operationResult = MA_SUCCESS;
            ma_event_signal(&pDevice->null_device.operationCompletionEvent);
            ma_semaphore_release(&pDevice->null_device.operationSemaphore);
            break;
        }

        /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */
        if (operation == MA_DEVICE_OP_NONE__NULL) {
            MA_ASSERT(MA_FALSE);  /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */
            pDevice->null_device.operationResult = MA_INVALID_OPERATION;
            ma_event_signal(&pDevice->null_device.operationCompletionEvent);
            ma_semaphore_release(&pDevice->null_device.operationSemaphore);
            continue;   /* Continue the loop. Don't terminate. */
        }
    }

    return (ma_thread_result)0;
}

static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation)
{
    ma_result result;

    /*
    TODO: Need to review this and consider just using mutual exclusion. I think the original motivation
    for this was to just post the event to a queue and return immediately, but that has since changed
    and now this function is synchronous. I think this can be simplified to just use a mutex.
    */

    /*
    The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later
    to support queing of operations.
    */
    result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore);
    if (result != MA_SUCCESS) {
        return result;  /* Failed to wait for the event. */
    }

    /*
    When we get here it means the background thread is not referencing the operation code and it can be changed. After changing this we need to
    signal an event to the worker thread to let it know that it can start work.
    */
    pDevice->null_device.operation = operation;

    /* Once the operation code has been set, the worker thread can start work. */
    if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) {
        return MA_ERROR;
    }

    /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */
    if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) {
        return MA_ERROR;
    }

    return pDevice->null_device.operationResult;
}

static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
{
    ma_uint32 internalSampleRate;
    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
        internalSampleRate = pDevice->capture.internalSampleRate;
    } else {
        internalSampleRate = pDevice->playback.internalSampleRate;
    }

    return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
}

static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
{
    ma_bool32 cbResult = MA_TRUE;

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

    /* Playback. */
    if (cbResult) {
        ma_device_info deviceInfo;
        MA_ZERO_OBJECT(&deviceInfo);
        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1);
        deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
        cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
    }

    /* Capture. */
    if (cbResult) {
        ma_device_info deviceInfo;
        MA_ZERO_OBJECT(&deviceInfo);
        ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1);
        deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
        cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
    }

    (void)cbResult; /* Silence a static analysis warning. */

    return MA_SUCCESS;
}

static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN


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

    /* No exclusive mode with the Core Audio backend for now. */
    if (((pConfig->deviceType == ma_device_type_capture  || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode  == ma_share_mode_exclusive) ||
        ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) {
        return MA_SHARE_MODE_NOT_SUPPORTED;
    }

    /* Capture needs to be initialized first. */
    if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
        ma_device_init_internal_data__coreaudio data;
        data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
        data.formatIn                     = pDescriptorCapture->format;
        data.channelsIn                   = pDescriptorCapture->channels;
        data.sampleRateIn                 = pDescriptorCapture->sampleRate;
        MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
        data.periodSizeInFramesIn         = pDescriptorCapture->periodSizeInFrames;
        data.periodSizeInMillisecondsIn   = pDescriptorCapture->periodSizeInMilliseconds;
        data.periodsIn                    = pDescriptorCapture->periodCount;
        data.shareMode                    = pDescriptorCapture->shareMode;
        data.performanceProfile           = pConfig->performanceProfile;
        data.registerStopEvent            = MA_TRUE;

        /* Need at least 3 periods for duplex. */
        if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) {
            data.periodsIn = 3;
        }

        result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice);
        if (result != MA_SUCCESS) {
            return result;
        }

        pDevice->coreaudio.isDefaultCaptureDevice           = (pConfig->capture.pDeviceID == NULL);
    #if defined(MA_APPLE_DESKTOP)
        pDevice->coreaudio.deviceObjectIDCapture            = (ma_uint32)data.deviceObjectID;
    #endif
        pDevice->coreaudio.audioUnitCapture                 = (ma_ptr)data.audioUnit;
        pDevice->coreaudio.pAudioBufferList                 = (ma_ptr)data.pAudioBufferList;
        pDevice->coreaudio.audioBufferCapInFrames           = data.periodSizeInFramesOut;
        pDevice->coreaudio.originalPeriodSizeInFrames       = pDescriptorCapture->periodSizeInFrames;
        pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
        pDevice->coreaudio.originalPeriods                  = pDescriptorCapture->periodCount;
        pDevice->coreaudio.originalPerformanceProfile       = pConfig->performanceProfile;

        pDescriptorCapture->format                          = data.formatOut;
        pDescriptorCapture->channels                        = data.channelsOut;
        pDescriptorCapture->sampleRate                      = data.sampleRateOut;
        MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
        pDescriptorCapture->periodSizeInFrames              = data.periodSizeInFramesOut;
        pDescriptorCapture->periodCount                     = data.periodsOut;

    #if defined(MA_APPLE_DESKTOP)
        ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio);
    
        /*
        If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
        switch the device in the background.
        */
        if (pConfig->capture.pDeviceID == NULL) {
            ma_device__track__coreaudio(pDevice);
        }
    #endif
    }

    /* Playback. */
    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
        ma_device_init_internal_data__coreaudio data;
        data.allowNominalSampleRateChange   = pConfig->coreaudio.allowNominalSampleRateChange;
        data.formatIn                       = pDescriptorPlayback->format;
        data.channelsIn                     = pDescriptorPlayback->channels;
        data.sampleRateIn                   = pDescriptorPlayback->sampleRate;
        MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
        data.shareMode                      = pDescriptorPlayback->shareMode;
        data.performanceProfile             = pConfig->performanceProfile;

        /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */
        if (pConfig->deviceType == ma_device_type_duplex) {
            data.periodSizeInFramesIn       = pDescriptorCapture->periodSizeInFrames;
            data.periodsIn                  = pDescriptorCapture->periodCount;
            data.registerStopEvent          = MA_FALSE;
        } else {
            data.periodSizeInFramesIn       = pDescriptorPlayback->periodSizeInFrames;
            data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
            data.periodsIn                  = pDescriptorPlayback->periodCount;
            data.registerStopEvent          = MA_TRUE;
        }

        result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice);
        if (result != MA_SUCCESS) {
            if (pConfig->deviceType == ma_device_type_duplex) {
                ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
                if (pDevice->coreaudio.pAudioBufferList) {
                    ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
                }
            }
            return result;
        }

        pDevice->coreaudio.isDefaultPlaybackDevice          = (pConfig->playback.pDeviceID == NULL);
    #if defined(MA_APPLE_DESKTOP)
        pDevice->coreaudio.deviceObjectIDPlayback           = (ma_uint32)data.deviceObjectID;
    #endif
        pDevice->coreaudio.audioUnitPlayback                = (ma_ptr)data.audioUnit;
        pDevice->coreaudio.originalPeriodSizeInFrames       = pDescriptorPlayback->periodSizeInFrames;
        pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
        pDevice->coreaudio.originalPeriods                  = pDescriptorPlayback->periodCount;
        pDevice->coreaudio.originalPerformanceProfile       = pConfig->performanceProfile;

        pDescriptorPlayback->format                         = data.formatOut;
        pDescriptorPlayback->channels                       = data.channelsOut;
        pDescriptorPlayback->sampleRate                     = data.sampleRateOut;
        MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
        pDescriptorPlayback->periodSizeInFrames             = data.periodSizeInFramesOut;
        pDescriptorPlayback->periodCount                    = data.periodsOut;

    #if defined(MA_APPLE_DESKTOP)
        ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);
    
        /*
        If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
        switch the device in the background.
        */
        if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) {
            ma_device__track__coreaudio(pDevice);
        }
    #endif
    }



    /*
    When stopping the device, a callback is called on another thread. We need to wait for this callback
    before returning from ma_device_stop(). This event is used for this.
    */
    ma_event_init(&pDevice->coreaudio.stopEvent);

    /*
    We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done
    differently on non-Desktop Apple platforms.
    */
#if defined(MA_APPLE_MOBILE)
    pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice];
#endif

    return MA_SUCCESS;
}


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

    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
        OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
        if (status != noErr) {
            return ma_result_from_OSStatus(status);
        }
    }

    if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
        OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
        if (status != noErr) {
            if (pDevice->type == ma_device_type_duplex) {
                ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
            }
            return ma_result_from_OSStatus(status);
        }
    }

    return MA_SUCCESS;
}

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

    /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */

    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
        OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
        if (status != noErr) {

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN

        if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) {
            return MA_INVALID_ARGS;
        }
    }

    pDevice->pContext = pContext;

    /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
    pDevice->pUserData      = pConfig->pUserData;
    pDevice->onData         = pConfig->dataCallback;
    pDevice->onNotification = pConfig->notificationCallback;
    pDevice->onStop         = pConfig->stopCallback;

    if (pConfig->playback.pDeviceID != NULL) {
        MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id));
        pDevice->playback.pID = &pDevice->playback.id;
    } else {
        pDevice->playback.pID = NULL;
    }

    if (pConfig->capture.pDeviceID != NULL) {
        MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id));
        pDevice->capture.pID = &pDevice->capture.id;
    } else {
        pDevice->capture.pID = NULL;
    }

    pDevice->noPreSilencedOutputBuffer   = pConfig->noPreSilencedOutputBuffer;
    pDevice->noClip                      = pConfig->noClip;
    pDevice->noDisableDenormals          = pConfig->noDisableDenormals;
    pDevice->noFixedSizedCallback        = pConfig->noFixedSizedCallback;
    pDevice->masterVolumeFactor          = 1;

    pDevice->type                        = pConfig->deviceType;
    pDevice->sampleRate                  = pConfig->sampleRate;
    pDevice->resampling.algorithm        = pConfig->resampling.algorithm;
    pDevice->resampling.linear.lpfOrder  = pConfig->resampling.linear.lpfOrder;
    pDevice->resampling.pBackendVTable   = pConfig->resampling.pBackendVTable;
    pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData;

    pDevice->capture.shareMode           = pConfig->capture.shareMode;
    pDevice->capture.format              = pConfig->capture.format;
    pDevice->capture.channels            = pConfig->capture.channels;
    ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);
    pDevice->capture.channelMixMode      = pConfig->capture.channelMixMode;

    pDevice->playback.shareMode          = pConfig->playback.shareMode;
    pDevice->playback.format             = pConfig->playback.format;
    pDevice->playback.channels           = pConfig->playback.channels;
    ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels);
    pDevice->playback.channelMixMode     = pConfig->playback.channelMixMode;


    result = ma_mutex_init(&pDevice->startStopLock);
    if (result != MA_SUCCESS) {
        return result;
    }

    /*
    When the device is started, the worker thread is the one that does the actual startup of the backend device. We
    use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device.

    Each of these semaphores is released internally by the worker thread when the work is completed. The start
    semaphore is also used to wake up the worker thread.
    */
    result = ma_event_init(&pDevice->wakeupEvent);
    if (result != MA_SUCCESS) {
        ma_mutex_uninit(&pDevice->startStopLock);
        return result;
    }

    result = ma_event_init(&pDevice->startEvent);
    if (result != MA_SUCCESS) {
        ma_event_uninit(&pDevice->wakeupEvent);
        ma_mutex_uninit(&pDevice->startStopLock);
        return result;
    }

    result = ma_event_init(&pDevice->stopEvent);
    if (result != MA_SUCCESS) {
        ma_event_uninit(&pDevice->startEvent);
        ma_event_uninit(&pDevice->wakeupEvent);
        ma_mutex_uninit(&pDevice->startStopLock);
        return result;
    }


    MA_ZERO_OBJECT(&descriptorPlayback);
    descriptorPlayback.pDeviceID                = pConfig->playback.pDeviceID;
    descriptorPlayback.shareMode                = pConfig->playback.shareMode;
    descriptorPlayback.format                   = pConfig->playback.format;
    descriptorPlayback.channels                 = pConfig->playback.channels;
    descriptorPlayback.sampleRate               = pConfig->sampleRate;
    ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels);
    descriptorPlayback.periodSizeInFrames       = pConfig->periodSizeInFrames;
    descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
    descriptorPlayback.periodCount              = pConfig->periods;

    if (descriptorPlayback.periodCount == 0) {
        descriptorPlayback.periodCount = MA_DEFAULT_PERIODS;
    }


    MA_ZERO_OBJECT(&descriptorCapture);
    descriptorCapture.pDeviceID                 = pConfig->capture.pDeviceID;
    descriptorCapture.shareMode                 = pConfig->capture.shareMode;
    descriptorCapture.format                    = pConfig->capture.format;
    descriptorCapture.channels                  = pConfig->capture.channels;
    descriptorCapture.sampleRate                = pConfig->sampleRate;
    ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);
    descriptorCapture.periodSizeInFrames        = pConfig->periodSizeInFrames;
    descriptorCapture.periodSizeInMilliseconds  = pConfig->periodSizeInMilliseconds;
    descriptorCapture.periodCount               = pConfig->periods;

    if (descriptorCapture.periodCount == 0) {
        descriptorCapture.periodCount = MA_DEFAULT_PERIODS;
    }


    result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture);
    if (result != MA_SUCCESS) {



( run in 0.554 second using v1.01-cache-2.11-cpan-f56aa216473 )