2 * Audio management UI code
4 * Copyright 2004 Chris Morgan
6 * This library 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.
11 * This library 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 GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define WIN32_LEAN_AND_MEAN
23 #define NONAMELESSSTRUCT
24 #define NONAMELESSUNION
27 #include "wine/port.h"
36 #include <wine/debug.h>
49 #include "mmdeviceapi.h"
50 #include "audioclient.h"
51 #include "audiopolicy.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(winecfg
);
63 static WCHAR g_drv_keyW
[256] = {'S','o','f','t','w','a','r','e','\\',
64 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',0};
66 static const WCHAR reg_out_nameW
[] = {'D','e','f','a','u','l','t','O','u','t','p','u','t',0};
67 static const WCHAR reg_in_nameW
[] = {'D','e','f','a','u','l','t','I','n','p','u','t',0};
68 static const WCHAR reg_vout_nameW
[] = {'D','e','f','a','u','l','t','V','o','i','c','e','O','u','t','p','u','t',0};
69 static const WCHAR reg_vin_nameW
[] = {'D','e','f','a','u','l','t','V','o','i','c','e','I','n','p','u','t',0};
71 static UINT num_render_devs
, num_capture_devs
;
72 static struct DeviceInfo
*render_devs
, *capture_devs
;
74 static BOOL
load_device(IMMDevice
*dev
, struct DeviceInfo
*info
)
79 hr
= IMMDevice_GetId(dev
, &info
->id
);
85 hr
= IMMDevice_OpenPropertyStore(dev
, STGM_READ
, &ps
);
87 CoTaskMemFree(info
->id
);
92 PropVariantInit(&info
->name
);
94 hr
= IPropertyStore_GetValue(ps
,
95 (PROPERTYKEY
*)&DEVPKEY_Device_FriendlyName
, &info
->name
);
96 IPropertyStore_Release(ps
);
98 CoTaskMemFree(info
->id
);
106 static BOOL
load_devices(IMMDeviceEnumerator
*devenum
, EDataFlow dataflow
,
107 UINT
*ndevs
, struct DeviceInfo
**out
)
109 IMMDeviceCollection
*coll
;
113 hr
= IMMDeviceEnumerator_EnumAudioEndpoints(devenum
, dataflow
,
114 DEVICE_STATE_ACTIVE
, &coll
);
118 hr
= IMMDeviceCollection_GetCount(coll
, ndevs
);
120 IMMDeviceCollection_Release(coll
);
125 *out
= HeapAlloc(GetProcessHeap(), 0,
126 sizeof(struct DeviceInfo
) * (*ndevs
));
128 IMMDeviceCollection_Release(coll
);
132 for(i
= 0; i
< *ndevs
; ++i
){
135 hr
= IMMDeviceCollection_Item(coll
, i
, &dev
);
141 load_device(dev
, &(*out
)[i
]);
143 IMMDevice_Release(dev
);
148 IMMDeviceCollection_Release(coll
);
153 static BOOL
get_driver_name(IMMDeviceEnumerator
*devenum
, PROPVARIANT
*pv
)
159 static const WCHAR wine_info_deviceW
[] = {'W','i','n','e',' ',
160 'i','n','f','o',' ','d','e','v','i','c','e',0};
162 hr
= IMMDeviceEnumerator_GetDevice(devenum
, wine_info_deviceW
, &device
);
166 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
168 IMMDevice_Release(device
);
172 hr
= IPropertyStore_GetValue(ps
,
173 (const PROPERTYKEY
*)&DEVPKEY_Device_Driver
, pv
);
174 IPropertyStore_Release(ps
);
175 IMMDevice_Release(device
);
182 static void initAudioDlg (HWND hDlg
)
184 WCHAR display_str
[256], format_str
[256], sysdefault_str
[256], disabled_str
[64];
185 IMMDeviceEnumerator
*devenum
;
186 BOOL have_driver
= FALSE
;
191 LoadStringW(GetModuleHandle(NULL
), IDS_AUDIO_DRIVER
,
192 format_str
, sizeof(format_str
) / sizeof(*format_str
));
193 LoadStringW(GetModuleHandle(NULL
), IDS_AUDIO_DRIVER_NONE
,
194 disabled_str
, sizeof(disabled_str
) / sizeof(*disabled_str
));
195 LoadStringW(GetModuleHandle(NULL
), IDS_AUDIO_SYSDEFAULT
,
196 sysdefault_str
, sizeof(sysdefault_str
) / sizeof(*sysdefault_str
));
198 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
,
199 CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, (void**)&devenum
);
203 load_devices(devenum
, eRender
, &num_render_devs
, &render_devs
);
204 load_devices(devenum
, eCapture
, &num_capture_devs
, &capture_devs
);
206 PropVariantInit(&pv
);
207 if(get_driver_name(devenum
, &pv
) && pv
.u
.pwszVal
[0] != '\0'){
209 wnsprintfW(display_str
, sizeof(display_str
) / sizeof(*display_str
),
210 format_str
, pv
.u
.pwszVal
);
211 lstrcatW(g_drv_keyW
, pv
.u
.pwszVal
);
213 PropVariantClear(&pv
);
215 IMMDeviceEnumerator_Release(devenum
);
218 SendDlgItemMessageW(hDlg
, IDC_AUDIOOUT_DEVICE
, CB_ADDSTRING
,
219 0, (LPARAM
)sysdefault_str
);
220 SendDlgItemMessageW(hDlg
, IDC_AUDIOOUT_DEVICE
, CB_SETCURSEL
, 0, 0);
221 SendDlgItemMessageW(hDlg
, IDC_VOICEOUT_DEVICE
, CB_ADDSTRING
,
222 0, (LPARAM
)sysdefault_str
);
223 SendDlgItemMessageW(hDlg
, IDC_VOICEOUT_DEVICE
, CB_SETCURSEL
, 0, 0);
225 SendDlgItemMessageW(hDlg
, IDC_AUDIOIN_DEVICE
, CB_ADDSTRING
,
226 0, (LPARAM
)sysdefault_str
);
227 SendDlgItemMessageW(hDlg
, IDC_AUDIOIN_DEVICE
, CB_SETCURSEL
, 0, 0);
228 SendDlgItemMessageW(hDlg
, IDC_VOICEIN_DEVICE
, CB_ADDSTRING
,
229 0, (LPARAM
)sysdefault_str
);
230 SendDlgItemMessageW(hDlg
, IDC_VOICEIN_DEVICE
, CB_SETCURSEL
, 0, 0);
233 WCHAR
*reg_out_dev
, *reg_vout_dev
, *reg_in_dev
, *reg_vin_dev
;
236 reg_out_dev
= get_reg_keyW(HKEY_CURRENT_USER
, g_drv_keyW
, reg_out_nameW
, NULL
);
237 reg_vout_dev
= get_reg_keyW(HKEY_CURRENT_USER
, g_drv_keyW
, reg_vout_nameW
, NULL
);
238 reg_in_dev
= get_reg_keyW(HKEY_CURRENT_USER
, g_drv_keyW
, reg_in_nameW
, NULL
);
239 reg_vin_dev
= get_reg_keyW(HKEY_CURRENT_USER
, g_drv_keyW
, reg_vin_nameW
, NULL
);
241 for(i
= 0; i
< num_render_devs
; ++i
){
242 if(!render_devs
[i
].id
)
245 SendDlgItemMessageW(hDlg
, IDC_AUDIOOUT_DEVICE
, CB_ADDSTRING
,
246 0, (LPARAM
)render_devs
[i
].name
.u
.pwszVal
);
247 SendDlgItemMessageW(hDlg
, IDC_AUDIOOUT_DEVICE
, CB_SETITEMDATA
,
248 i
+ 1, (LPARAM
)&render_devs
[i
]);
249 if(reg_out_dev
&& !lstrcmpW(render_devs
[i
].id
, reg_out_dev
))
250 SendDlgItemMessageW(hDlg
, IDC_AUDIOOUT_DEVICE
, CB_SETCURSEL
, i
+ 1, 0);
252 SendDlgItemMessageW(hDlg
, IDC_VOICEOUT_DEVICE
, CB_ADDSTRING
,
253 0, (LPARAM
)render_devs
[i
].name
.u
.pwszVal
);
254 SendDlgItemMessageW(hDlg
, IDC_VOICEOUT_DEVICE
, CB_SETITEMDATA
,
255 i
+ 1, (LPARAM
)&render_devs
[i
]);
256 if(reg_vout_dev
&& !lstrcmpW(render_devs
[i
].id
, reg_vout_dev
))
257 SendDlgItemMessageW(hDlg
, IDC_VOICEOUT_DEVICE
, CB_SETCURSEL
, i
+ 1, 0);
260 for(i
= 0; i
< num_capture_devs
; ++i
){
261 if(!capture_devs
[i
].id
)
264 SendDlgItemMessageW(hDlg
, IDC_AUDIOIN_DEVICE
, CB_ADDSTRING
,
265 0, (LPARAM
)capture_devs
[i
].name
.u
.pwszVal
);
266 SendDlgItemMessageW(hDlg
, IDC_AUDIOIN_DEVICE
, CB_SETITEMDATA
,
267 i
+ 1, (LPARAM
)&capture_devs
[i
]);
268 if(reg_in_dev
&& !lstrcmpW(capture_devs
[i
].id
, reg_in_dev
))
269 SendDlgItemMessageW(hDlg
, IDC_AUDIOIN_DEVICE
, CB_SETCURSEL
, i
+ 1, 0);
271 SendDlgItemMessageW(hDlg
, IDC_VOICEIN_DEVICE
, CB_ADDSTRING
,
272 0, (LPARAM
)capture_devs
[i
].name
.u
.pwszVal
);
273 SendDlgItemMessageW(hDlg
, IDC_VOICEIN_DEVICE
, CB_SETITEMDATA
,
274 i
+ 1, (LPARAM
)&capture_devs
[i
]);
275 if(reg_vin_dev
&& !lstrcmpW(capture_devs
[i
].id
, reg_vin_dev
))
276 SendDlgItemMessageW(hDlg
, IDC_VOICEIN_DEVICE
, CB_SETCURSEL
, i
+ 1, 0);
279 HeapFree(GetProcessHeap(), 0, reg_out_dev
);
280 HeapFree(GetProcessHeap(), 0, reg_vout_dev
);
281 HeapFree(GetProcessHeap(), 0, reg_in_dev
);
282 HeapFree(GetProcessHeap(), 0, reg_vin_dev
);
284 wnsprintfW(display_str
, sizeof(display_str
) / sizeof(*display_str
),
285 format_str
, disabled_str
);
287 SetDlgItemTextW(hDlg
, IDC_AUDIO_DRIVER
, display_str
);
290 static void set_reg_device(HWND hDlg
, int dlgitem
, const WCHAR
*key_name
)
293 struct DeviceInfo
*info
;
295 idx
= SendDlgItemMessageW(hDlg
, dlgitem
, CB_GETCURSEL
, 0, 0);
297 info
= (struct DeviceInfo
*)SendDlgItemMessageW(hDlg
, dlgitem
,
298 CB_GETITEMDATA
, idx
, 0);
300 if(!info
|| info
== (void*)CB_ERR
)
301 set_reg_keyW(HKEY_CURRENT_USER
, g_drv_keyW
, key_name
, NULL
);
303 set_reg_keyW(HKEY_CURRENT_USER
, g_drv_keyW
, key_name
, info
->id
);
306 static void test_sound(void)
308 BOOL (WINAPI
*pPlaySoundW
)(const WCHAR
*, HMODULE
, DWORD
);
311 static const WCHAR winmmW
[] = {'w','i','n','m','m','.','d','l','l',0};
313 winmm
= LoadLibraryW(winmmW
);
315 WINE_ERR("LoadLibrary failed: %u\n", GetLastError());
319 pPlaySoundW
= (void*)GetProcAddress(winmm
, "PlaySoundW");
321 WINE_ERR("GetProcAddress failed: %u\n", GetLastError());
326 if(!pPlaySoundW(MAKEINTRESOURCEW(IDW_TESTSOUND
), NULL
, SND_RESOURCE
| SND_SYNC
)){
327 WCHAR error_str
[256], title_str
[256];
329 LoadStringW(GetModuleHandle(NULL
), IDS_AUDIO_TEST_FAILED
,
330 error_str
, sizeof(error_str
) / sizeof(*error_str
));
331 LoadStringW(GetModuleHandle(NULL
), IDS_AUDIO_TEST_FAILED_TITLE
,
332 title_str
, sizeof(title_str
) / sizeof(*title_str
));
334 MessageBoxW(NULL
, error_str
, title_str
, MB_OK
| MB_ICONERROR
);
341 AudioDlgProc (HWND hDlg
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
345 switch (LOWORD(wParam
)) {
349 case IDC_AUDIOOUT_DEVICE
:
350 if(HIWORD(wParam
) == CBN_SELCHANGE
){
351 set_reg_device(hDlg
, IDC_AUDIOOUT_DEVICE
, reg_out_nameW
);
352 SendMessageW(GetParent(hDlg
), PSM_CHANGED
, 0, 0);
355 case IDC_VOICEOUT_DEVICE
:
356 if(HIWORD(wParam
) == CBN_SELCHANGE
){
357 set_reg_device(hDlg
, IDC_VOICEOUT_DEVICE
, reg_vout_nameW
);
358 SendMessageW(GetParent(hDlg
), PSM_CHANGED
, 0, 0);
361 case IDC_AUDIOIN_DEVICE
:
362 if(HIWORD(wParam
) == CBN_SELCHANGE
){
363 set_reg_device(hDlg
, IDC_AUDIOIN_DEVICE
, reg_in_nameW
);
364 SendMessageW(GetParent(hDlg
), PSM_CHANGED
, 0, 0);
367 case IDC_VOICEIN_DEVICE
:
368 if(HIWORD(wParam
) == CBN_SELCHANGE
){
369 set_reg_device(hDlg
, IDC_VOICEIN_DEVICE
, reg_vin_nameW
);
370 SendMessageW(GetParent(hDlg
), PSM_CHANGED
, 0, 0);
377 set_window_title(hDlg
);
381 switch(((LPNMHDR
)lParam
)->code
) {
383 SetWindowLongPtr(hDlg
, DWLP_MSGRESULT
, FALSE
);
387 SetWindowLongPtr(hDlg
, DWLP_MSGRESULT
, PSNRET_NOERROR
);