<commit_before>/*****************************************************************************
 * mmdevice.c : Windows Multimedia Device API audio output plugin for VLC
 *****************************************************************************
 * Copyright (C) 2012 <NAME>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#undef _WIN32_WINNT
#define _WIN32_WINNT 0x600 /* Windows Vista */
#define INITGUID
#define COBJMACROS
#define CONST_VTABLE

#include <stdlib.h>
#include <assert.h>
#include <audiopolicy.h>
#include <mmdeviceapi.h>

DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd,
   0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_aout.h>
#include <vlc_charset.h>
#include "mmdevice.h"

DEFINE_GUID (GUID_VLC_AUD_OUT, 0x4533f59d, 0x59ee, 0x00c6,
   0xad, 0xb2, 0xc6, 0x8b, 0x50, 0x1a, 0x66, 0x55);

static int TryEnterMTA(vlc_object_t *obj)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if (unlikely(FAILED(hr)))
    {
        msg_Err (obj, "cannot initialize COM (error 0x%lx)", hr);
        return -1;
    }
    return 0;
}
#define TryEnterMTA(o) TryEnterMTA(VLC_OBJECT(o))

static void EnterMTA(void)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if (unlikely(FAILED(hr)))
        abort();
}

static void LeaveMTA(void)
{
    CoUninitialize();
}

struct aout_sys_t
{
    audio_output_t *aout;
    aout_api_t *api; /**< Audio output back-end API */

    IMMDeviceEnumerator *it; /**< Device enumerator, NULL when exiting */
    /*TODO: IMMNotificationClient*/

    IMMDevice *dev; /**< Selected output device, NULL if none */
    IAudioSessionManager *manager; /**< Session for the output device */
    struct IAudioSessionEvents session_events;

    LONG refs;
    CRITICAL_SECTION lock; /**< Lock to protect Core Audio API state */
    HANDLE device_changed; /**< Event to reset thread */
    vlc_thread_t thread; /**< Thread for audio session control */
};

/* NOTE: The Core Audio API documentation totally fails to specify the thread
 * safety (or lack thereof) of the interfaces. This code is most pessimistic
 * and assumes that the API is not thread-safe at all.
 *
 * The audio output owner (i.e. the audio output core) is responsible for
 * serializing callbacks. This code only needs to be concerned with
 * synchronization between the set of audio output callbacks, the thread
 * and (trivially) the device and session notifications. */

static int vlc_FromHR(audio_output_t *aout, HRESULT hr)
{
    /* Restart on unplug */
    if (unlikely(hr == AUDCLNT_E_DEVICE_INVALIDATED))
        var_TriggerCallback(aout, "audio-device");
    return SUCCEEDED(hr) ? 0 : -1;
}

/*** VLC audio output callbacks ***/
static int TimeGet(audio_output_t *aout, mtime_t *restrict delay)
{
    aout_sys_t *sys = aout->sys;
    HRESULT hr;

    EnterMTA();
    EnterCriticalSection(&sys->lock);
    hr = aout_api_TimeGet(sys->api, delay);
    LeaveCriticalSection(&sys->lock);
    LeaveMTA();

    return SUCCEEDED(hr) ? 0 : -1;
}

static void Play(audio_output_t *aout, block_t *block)
{
    aout_sys_t *sys = aout->sys;
    HRESULT hr;

    EnterMTA();
    EnterCriticalSection(&sys->lock);
    hr = aout_api_Play(sys->api, block);
    LeaveCriticalSection(&sys->lock);
    LeaveMTA();

    vlc_FromHR(aout, hr);
}

static void Pause(audio_output_t *aout, bool paused, mtime_t date)
{
    aout_sys_t *sys = aout->sys;
    HRESULT hr;

    EnterMTA();
    EnterCriticalSection(&sys->lock);
    hr = aout_api_Pause(sys->api, paused);
    LeaveCriticalSection(&sys->lock);
    LeaveMTA();

    vlc_FromHR(aout, hr);
    (void) date;
}

static void Flush(audio_output_t *aout, bool wait)
{
    aout_sys_t *sys = aout->sys;

    if (wait)
        return; /* Drain not implemented */

    EnterMTA();
    EnterCriticalSection(&sys->lock);
    aout_api_Flush(sys->api);
    LeaveCriticalSection(&sys->lock);
    LeaveMTA();
}

static ISimpleAudioVolume *GetSimpleVolume(audio_output_t *aout)
{
    aout_sys_t *sys = aout->sys;
    ISimpleAudioVolume *volume;
    HRESULT hr;

    if (sys->manager == NULL)
        return NULL;

    if (TryEnterMTA(aout))
        return NULL;
    EnterCriticalSection(&sys->lock);
    hr = IAudioSessionManager_GetSimpleAudioVolume(sys->manager,
                                                   &GUID_VLC_AUD_OUT,
                                                   FALSE, &volume);
    if (FAILED(hr))
    {
        LeaveCriticalSection(&sys->lock);
        LeaveMTA();
        msg_Err(aout, "cannot get simple volume (error 0x%lx)", hr);
        return NULL;
    }
    return volume;
}

static void PutSimpleVolume(audio_output_t *aout, ISimpleAudioVolume *volume)<commit_msg>mmdevice: emulate drain<commit_after>/*****************************************************************************
 * mmdevice.c : Windows Multimedia Device API audio output plugin for VLC
 *****************************************************************************
 * Copyright (C) 2012 <NAME>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#undef _WIN32_WINNT
#define _WIN32_WINNT 0x600 /* Windows Vista */
#define INITGUID
#define COBJMACROS
#define CONST_VTABLE

#include <stdlib.h>
#include <assert.h>
#include <audiopolicy.h>
#include <mmdeviceapi.h>

DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd,
   0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_aout.h>
#include <vlc_charset.h>
#include "mmdevice.h"

DEFINE_GUID (GUID_VLC_AUD_OUT, 0x4533f59d, 0x59ee, 0x00c6,
   0xad, 0xb2, 0xc6, 0x8b, 0x50, 0x1a, 0x66, 0x55);

static int TryEnterMTA(vlc_object_t *obj)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if (unlikely(FAILED(hr)))
    {
        msg_Err (obj, "cannot initialize COM (error 0x%lx)", hr);
        return -1;
    }
    return 0;
}
#define TryEnterMTA(o) TryEnterMTA(VLC_OBJECT(o))

static void EnterMTA(void)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if (unlikely(FAILED(hr)))
        abort();
}

static void LeaveMTA(void)
{
    CoUninitialize();
}

struct aout_sys_t
{
    audio_output_t *aout;
    aout_api_t *api; /**< Audio output back-end API */

    IMMDeviceEnumerator *it; /**< Device enumerator, NULL when exiting */
    /*TODO: IMMNotificationClient*/

    IMMDevice *dev; /**< Selected output device, NULL if none */
    IAudioSessionManager *manager; /**< Session for the output device */
    struct IAudioSessionEvents session_events;

    LONG refs;
    CRITICAL_SECTION lock; /**< Lock to protect Core Audio API state */
    HANDLE device_changed; /**< Event to reset thread */
    vlc_thread_t thread; /**< Thread for audio session control */
};

/* NOTE: The Core Audio API documentation totally fails to specify the thread
 * safety (or lack thereof) of the interfaces. This code is most pessimistic
 * and assumes that the API is not thread-safe at all.
 *
 * The audio output owner (i.e. the audio output core) is responsible for
 * serializing callbacks. This code only needs to be concerned with
 * synchronization between the set of audio output callbacks, the thread
 * and (trivially) the device and session notifications. */

static int vlc_FromHR(audio_output_t *aout, HRESULT hr)
{
    /* Restart on unplug */
    if (unlikely(hr == AUDCLNT_E_DEVICE_INVALIDATED))
        var_TriggerCallback(aout, "audio-device");
    return SUCCEEDED(hr) ? 0 : -1;
}

/*** VLC audio output callbacks ***/
static int TimeGet(audio_output_t *aout, mtime_t *restrict delay)
{
    aout_sys_t *sys = aout->sys;
    HRESULT hr;

    EnterMTA();
    EnterCriticalSection(&sys->lock);
    hr = aout_api_TimeGet(sys->api, delay);
    LeaveCriticalSection(&sys->lock);
    LeaveMTA();

    return SUCCEEDED(hr) ? 0 : -1;
}

static void Play(audio_output_t *aout, block_t *block)
{
    aout_sys_t *sys = aout->sys;
    HRESULT hr;

    EnterMTA();
    EnterCriticalSection(&sys->lock);
    hr = aout_api_Play(sys->api, block);
    LeaveCriticalSection(&sys->lock);
    LeaveMTA();

    vlc_FromHR(aout, hr);
}

static void Pause(audio_output_t *aout, bool paused, mtime_t date)
{
    aout_sys_t *sys = aout->sys;
    HRESULT hr;

    EnterMTA();
    EnterCriticalSection(&sys->lock);
    hr = aout_api_Pause(sys->api, paused);
    LeaveCriticalSection(&sys->lock);
    LeaveMTA();

    vlc_FromHR(aout, hr);
    (void) date;
}

static void Flush(audio_output_t *aout, bool wait)
{
    aout_sys_t *sys = aout->sys;
    mtime_t delay = VLC_TS_INVALID;

    EnterMTA();
    EnterCriticalSection(&sys->lock);

    if (wait)
    {   /* Loosy drain emulation */
        if (FAILED(aout_api_TimeGet(sys->api, &delay)))
            delay = VLC_TS_INVALID;
    }
    else
        aout_api_Flush(sys->api);

    LeaveCriticalSection(&sys->lock);
    LeaveMTA();

    if (delay != VLC_TS_INVALID)
        Sleep((delay / (CLOCK_FREQ / 1000)) + 1);
}

static ISimpleAudioVolume *GetSimpleVolume(audio_output_t *aout)
{
    aout_sys_t *sys = aout->sys;
    ISimpleAudioVolume *volume;
    HRESULT hr;

    if (sys->manager == NULL)
        return NULL;

    if (TryEnterMTA(aout))
        return NULL;
    EnterCriticalSection(&sys->lock);
    hr = IAudioSessionManager_GetSimpleAudioVolume(sys->manager,
                                                   &GUID_VLC_AUD_OUT,
                                                   FALSE, &volume);
    if (FAILED(hr))
    {
        LeaveCriticalSection(&sys->lock);
        LeaveMTA();
        msg_Err(aout, "cannot get simple volume (error 0x%lx)", hr);
        return NULL;
    }
    return volume;
}

static void PutSimpleVolume(audio_output_t *aout, ISimpleAudioVolume *volume)
{'fraction_non_alphanumeric': 0.14348514155090558, 'fraction_numerical': 0.017671883242482856, 'mean_word_length': 3.5701084773001206, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 66, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6936543', 'n_tokens_mistral': 3893, 'n_tokens_neox': 3627, 'n_words': 1066}