1 /*
2  * This file is part of mpv.
3  *
4  * Original author: Jonathan Yong <10walls@gmail.com>
5  *
6  * mpv is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * mpv is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef MP_AO_WASAPI_H_
21 #define MP_AO_WASAPI_H_
22 
23 #include <stdlib.h>
24 #include <stdbool.h>
25 #include <windows.h>
26 #include <mmdeviceapi.h>
27 #include <audioclient.h>
28 #include <audiopolicy.h>
29 #include <endpointvolume.h>
30 
31 #include "common/msg.h"
32 #include "osdep/atomic.h"
33 #include "osdep/windows_utils.h"
34 #include "internal.h"
35 #include "ao.h"
36 
37 typedef struct change_notify {
38     IMMNotificationClient client; // this must be first in the structure!
39     IMMDeviceEnumerator *pEnumerator; // object where client is registered
40     LPWSTR monitored; // Monitored device
41     bool is_hotplug;
42     struct ao *ao;
43 } change_notify;
44 
45 HRESULT wasapi_change_init(struct ao* ao, bool is_hotplug);
46 void wasapi_change_uninit(struct ao* ao);
47 
48 enum wasapi_thread_state {
49     WASAPI_THREAD_FEED = 0,
50     WASAPI_THREAD_RESUME,
51     WASAPI_THREAD_RESET,
52     WASAPI_THREAD_SHUTDOWN
53 };
54 
55 typedef struct wasapi_state {
56     struct mp_log *log;
57 
58     bool init_ok;            // status of init phase
59     // Thread handles
60     HANDLE hInitDone;        // set when init is complete in audio thread
61     HANDLE hAudioThread;     // the audio thread itself
62     HANDLE hWake;            // thread wakeup event
63     atomic_int thread_state; // enum wasapi_thread_state (what to do on wakeup)
64     struct mp_dispatch_queue *dispatch; // for volume/mute/session display
65 
66     // for setting the audio thread priority
67     HANDLE hTask;
68 
69     // ID of the device to use
70     LPWSTR deviceID;
71     // WASAPI object handles owned and used by audio thread
72     IMMDevice *pDevice;
73     IAudioClient *pAudioClient;
74     IAudioRenderClient *pRenderClient;
75 
76     // WASAPI internal clock information, for estimating delay
77     IAudioClock *pAudioClock;
78     atomic_ullong sample_count;  // samples per channel written by GetBuffer
79     UINT64 clock_frequency;      // scale for position returned by GetPosition
80     LARGE_INTEGER qpc_frequency; // frequency of Windows' high resolution timer
81 
82     // WASAPI control
83     IAudioSessionControl *pSessionControl; // setting the stream title
84     IAudioEndpointVolume *pEndpointVolume; // exclusive mode volume/mute
85     ISimpleAudioVolume *pAudioVolume;      // shared mode volume/mute
86     DWORD vol_hw_support; // is hardware volume supported for exclusive-mode?
87 
88     // ao options
89     int opt_exclusive;
90 
91     // format info
92     WAVEFORMATEXTENSIBLE format;
93     AUDCLNT_SHAREMODE share_mode; // AUDCLNT_SHAREMODE_EXCLUSIVE / SHARED
94     UINT32 bufferFrameCount;      // number of frames in buffer
95     struct ao_convert_fmt convert_format;
96 
97     change_notify change;
98 } wasapi_state;
99 
100 char *mp_PKEY_to_str_buf(char *buf, size_t buf_size, const PROPERTYKEY *pkey);
101 #define mp_PKEY_to_str(pkey) mp_PKEY_to_str_buf((char[42]){0}, 42, (pkey))
102 
103 void wasapi_list_devs(struct ao *ao, struct ao_device_list *list);
104 bstr wasapi_get_specified_device_string(struct ao *ao);
105 LPWSTR wasapi_find_deviceID(struct ao *ao);
106 
107 bool wasapi_thread_init(struct ao *ao);
108 void wasapi_thread_uninit(struct ao *ao);
109 
110 #define EXIT_ON_ERROR(hres)  \
111               do { if (FAILED(hres)) { goto exit_label; } } while(0)
112 #define SAFE_DESTROY(unk, release) \
113               do { if ((unk) != NULL) { release; (unk) = NULL; } } while(0)
114 
115 #endif
116