-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.cpp
395 lines (354 loc) · 12.8 KB
/
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
#include <windows.h>
#include <shellapi.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <iostream>
using namespace std;
BOOL per_isMuted;
BOOL reLoad=1;
float per_volume;
////////////////////////////////////////////////////////
class NotificationClient : public IMMNotificationClient
{
public:
NotificationClient() : m_cRef(1), m_pEnumerator(nullptr)
{
// 初始化目前執行緒的 COM
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
{
std::cout << "無法初始化 COM" << std::endl;
}
else
{
// 建立裝置列舉器
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&m_pEnumerator);
if (FAILED(hr))
{
std::cout << "無法建立裝置列舉器" << std::endl;
CoUninitialize();
}
else
{
// 註冊裝置變更通知
hr = m_pEnumerator->RegisterEndpointNotificationCallback(this);
if (FAILED(hr))
{
std::cout << "無法註冊裝置變更通知" << std::endl;
m_pEnumerator->Release();
m_pEnumerator = nullptr;
CoUninitialize();
}
}
}
}
~NotificationClient()
{
Close();
}
STDMETHOD_(ULONG, AddRef)()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHOD_(ULONG, Release)()
{
ULONG ulRef = InterlockedDecrement(&m_cRef);
if (ulRef == 0)
{
delete this;
}
return ulRef;
}
STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject)
{
if (riid == IID_IUnknown || riid == __uuidof(IMMNotificationClient))
{
*ppvObject = static_cast<IMMNotificationClient*>(this);
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHOD(OnDefaultDeviceChanged)(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) {// IMMNotificationClient 方法
// 預設音訊裝置已變更。
reLoad = 0;
return S_OK;
}
STDMETHOD(OnDeviceAdded)(LPCWSTR pwstrDeviceId) { return S_OK; }
STDMETHOD(OnDeviceRemoved)(LPCWSTR pwstrDeviceId) { return S_OK; }
STDMETHOD(OnDeviceStateChanged)(LPCWSTR pwstrDeviceId, DWORD dwNewState) { return S_OK; }
STDMETHOD(OnPropertyValueChanged)(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { return S_OK; }
void Close()
{
// 取消註冊裝置列舉器
if (m_pEnumerator)
{
m_pEnumerator->UnregisterEndpointNotificationCallback(this);
m_pEnumerator->Release();
m_pEnumerator = nullptr;
}
// 結束目前執行緒的 COM 函式庫
CoUninitialize();
}
private:
LONG m_cRef;
IMMDeviceEnumerator* m_pEnumerator;
};
////////////////////////////////////////////////////////
class AudioDeviceNotificationListener
{
public:
AudioDeviceNotificationListener() : bDidStart(false), pNotificationClient(nullptr), pEnumerator(nullptr), hNotificationThread(nullptr) {}
~AudioDeviceNotificationListener() { Close(); }
bool Start()
{
if (!bDidStart)
{
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr))
{
return false;
}
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), reinterpret_cast<void**>(&pEnumerator));
if (FAILED(hr))
{
CleanUp();
return false;
}
pNotificationClient = new NotificationClient();
hr = pEnumerator->RegisterEndpointNotificationCallback(pNotificationClient);
if (FAILED(hr))
{
CleanUp();
return false;
}
hNotificationThread = CreateThread(NULL, 0, NotificationThreadProc, pNotificationClient, 0, NULL);
if (hNotificationThread == NULL)
{
CleanUp();
return false;
}
bDidStart = true;
return true;
}
return false;
}
void Close()
{
if (bDidStart)
{
CleanUp();
bDidStart = false;
}
}
private:
// 靜態成員函式:處理通知執行緒
static DWORD WINAPI NotificationThreadProc(LPVOID lpParameter)
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
// 清理函式:釋放資源和取消註冊通知
void CleanUp()
{
if (hNotificationThread)
{
// 發送終止訊息至通知執行緒並等待其結束
PostThreadMessage(GetThreadId(hNotificationThread), WM_QUIT, NULL, NULL);
WaitForSingleObject(hNotificationThread, INFINITE);
CloseHandle(hNotificationThread);
hNotificationThread = nullptr;
}
if (pEnumerator)
{
// 取消註冊通知回調
pEnumerator->UnregisterEndpointNotificationCallback(pNotificationClient);
pEnumerator->Release();
pEnumerator = nullptr;
}
if (pNotificationClient)
{
// 釋放通知客戶端
pNotificationClient->Release();
pNotificationClient = nullptr;
}
// 取消 COM 初始化
CoUninitialize();
}
bool bDidStart; // 標記是否已開始監聽
NotificationClient* pNotificationClient; // 通知客戶端
IMMDeviceEnumerator* pEnumerator; // 裝置列舉器
HANDLE hNotificationThread; // 通知執行緒的處理
};
////////////////////////////////////////////////////////
class AudioEndpointVolumeCallback : public IAudioEndpointVolumeCallback
{
public:
// 增加參考計數
ULONG STDMETHODCALLTYPE AddRef() { return 1; }
// 釋放參考計數
ULONG STDMETHODCALLTYPE Release() { return 1; }
// 查詢介面
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface)
{
if (riid == IID_IUnknown || riid == __uuidof(IAudioEndpointVolumeCallback))
{
*ppvInterface = static_cast<IAudioEndpointVolumeCallback *>(this);
return S_OK;
}
else
{
*ppvInterface = NULL;
return E_NOINTERFACE;
}
}
// 音量通知回呼函式
HRESULT STDMETHODCALLTYPE OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA pNotify)
{
if (pNotify)
{
// 在此處處理音量變更通知
float volume = pNotify->fMasterVolume;
BOOL isMuted = pNotify->bMuted;
//如果音量小於1%則設為靜音
if (volume < 0.01)
{ isMuted = 1;}
//取到小數點後兩位
volume = roundf(volume * 100) / 100;
//如果靜音狀態改變
if (per_isMuted != isMuted)
{
if (isMuted == 1)
{cout << "Muted " << '\r';}
else
{cout << "UnMute " << '\r';}
}
if (per_volume != volume && volume >= 0.01) //如果音量狀態改變
{
cout << "volume:" << volume * 100 << "% " << '\r';
}
SyncNonDefaultDevicesToDefault(isMuted, volume);
}
return S_OK;
}
void SyncNonDefaultDevicesToDefault(BOOL isMuted, float volume)
{
HRESULT hr;
IMMDeviceEnumerator *deviceEnumerator = NULL;
IMMDevice *defaultDevice = NULL;
IAudioEndpointVolume *defaultEndpointVolume = NULL;
// 初始化 COM 库
hr = CoInitialize(NULL);
// 創建設備枚舉器
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
__uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
// 取得預設音訊渲染設備
hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
//取得設備的唯一識別符 (GUID)
LPWSTR defaultDeviceId = NULL;
hr = defaultDevice->GetId(&defaultDeviceId);
IMMDeviceCollection *deviceCollection = NULL;
// 枚舉音訊端點
hr = deviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &deviceCollection);
UINT deviceCount = 0;
hr = deviceCollection->GetCount(&deviceCount);
for (UINT i = 0; i < deviceCount; i++)
{
IMMDevice *currentDevice = NULL;
hr = deviceCollection->Item(i, ¤tDevice);
if (currentDevice)
{
//取得設備的唯一識別符 (GUID)
LPWSTR DeviceId = NULL;
hr = currentDevice->GetId(&DeviceId);
if(wstring(DeviceId) != wstring(defaultDeviceId)) //如果不是預設裝置
{
IAudioEndpointVolume *currentEndpointVolume = NULL;
// 啟用當前端點音量界面
hr = currentDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)¤tEndpointVolume);
// 將指定的音量值應用到非默認設備
if (currentEndpointVolume)
{
//調整靜音狀態
hr = currentEndpointVolume->SetMute(isMuted, NULL);
//調整靜音狀態,音量
hr = currentEndpointVolume->SetMasterVolumeLevelScalar(volume, NULL);
currentEndpointVolume->Release();
}
}
}
currentDevice->Release();
}
if (defaultEndpointVolume)
defaultEndpointVolume->Release();
if (defaultDevice)
defaultDevice->Release();
if (deviceCollection)
deviceCollection->Release();
if (deviceEnumerator)
deviceEnumerator->Release();
// 反初始化 COM 库
CoUninitialize();
}
};
////////////////////////////////////////////////////////
int main()
{
// Initialize COM for the main thread
HRESULT hr = CoInitialize(NULL);
// Create an instance of AudioDeviceNotificationListener
AudioDeviceNotificationListener listener;
// Start the listener
listener.Start();
cout << "Running" << endl;
// 隱藏窗口
//ShowWindow(GetConsoleWindow(), SW_HIDE);
while (true)
{
HRESULT hr;
IMMDeviceEnumerator *deviceEnumerator = NULL;
IMMDevice *defaultDevice = NULL;
IAudioEndpointVolume *defaultEndpointVolume = NULL;
AudioEndpointVolumeCallback *endpointVolumeCallback = new AudioEndpointVolumeCallback();
// 初始化 COM Library
hr = CoInitialize(NULL);
// 創建設備枚舉器
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
__uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
// 獲取默認音訊端點
hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
// 啟用默認端點音量界面
hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&defaultEndpointVolume);
// 註冊音量變更通知的回呼介面
hr = defaultEndpointVolume->RegisterControlChangeNotify(endpointVolumeCallback);
// 保持執行
while (reLoad)
{
Sleep(500); // (根據需要調整延遲時間)
}
cout <<"Default audio device changed" << '\r';
reLoad=1;
// 取消註冊預設設備的回呼介面
hr = defaultEndpointVolume->UnregisterControlChangeNotify(endpointVolumeCallback);
// 釋放資源
if (defaultEndpointVolume)
defaultEndpointVolume->Release();
if (defaultDevice)
defaultDevice->Release();
if (deviceEnumerator)
deviceEnumerator->Release();
if (endpointVolumeCallback)
delete endpointVolumeCallback;
// 反初始化 COM Library
CoUninitialize();
}
// Close the listener and clean up
listener.Close();
CoUninitialize();
return 0;
}