audio.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. /*
  2. * Audio management UI code
  3. *
  4. * Copyright 2004 Chris Morgan
  5. *
  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.
  10. *
  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.
  15. *
  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
  19. *
  20. */
  21. #define WIN32_LEAN_AND_MEAN
  22. #define NONAMELESSUNION
  23. #include "config.h"
  24. #include "wine/port.h"
  25. #include <assert.h>
  26. #include <stdlib.h>
  27. #include <stdio.h>
  28. #include <string.h>
  29. #define COBJMACROS
  30. #include <windows.h>
  31. #include <wine/debug.h>
  32. #include <shellapi.h>
  33. #include <objbase.h>
  34. #include <shlguid.h>
  35. #include <shlwapi.h>
  36. #include <shlobj.h>
  37. #include <mmsystem.h>
  38. #include <mmreg.h>
  39. #include <mmddk.h>
  40. #include "ole2.h"
  41. #include "initguid.h"
  42. #include "propkey.h"
  43. #include "devpkey.h"
  44. #include "mmdeviceapi.h"
  45. #include "audioclient.h"
  46. #include "audiopolicy.h"
  47. #include "winecfg.h"
  48. #include "resource.h"
  49. WINE_DEFAULT_DEBUG_CHANNEL(winecfg);
  50. struct DeviceInfo {
  51. WCHAR *id;
  52. PROPVARIANT name;
  53. int speaker_config;
  54. };
  55. static WCHAR g_drv_keyW[256] = {'S','o','f','t','w','a','r','e','\\',
  56. 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',0};
  57. static const WCHAR reg_out_nameW[] = {'D','e','f','a','u','l','t','O','u','t','p','u','t',0};
  58. static const WCHAR reg_in_nameW[] = {'D','e','f','a','u','l','t','I','n','p','u','t',0};
  59. 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};
  60. static const WCHAR reg_vin_nameW[] = {'D','e','f','a','u','l','t','V','o','i','c','e','I','n','p','u','t',0};
  61. static UINT num_render_devs, num_capture_devs;
  62. static struct DeviceInfo *render_devs, *capture_devs;
  63. static const struct
  64. {
  65. int text_id;
  66. DWORD speaker_mask;
  67. } speaker_configs[] =
  68. {
  69. { IDS_AUDIO_SPEAKER_5POINT1, KSAUDIO_SPEAKER_5POINT1 },
  70. { IDS_AUDIO_SPEAKER_QUAD, KSAUDIO_SPEAKER_QUAD },
  71. { IDS_AUDIO_SPEAKER_STEREO, KSAUDIO_SPEAKER_STEREO },
  72. { IDS_AUDIO_SPEAKER_MONO, KSAUDIO_SPEAKER_MONO },
  73. { 0, 0 }
  74. };
  75. static BOOL load_device(IMMDevice *dev, struct DeviceInfo *info)
  76. {
  77. IPropertyStore *ps;
  78. HRESULT hr;
  79. PROPVARIANT pv;
  80. UINT i;
  81. hr = IMMDevice_GetId(dev, &info->id);
  82. if(FAILED(hr)){
  83. info->id = NULL;
  84. return FALSE;
  85. }
  86. hr = IMMDevice_OpenPropertyStore(dev, STGM_READ, &ps);
  87. if(FAILED(hr)){
  88. CoTaskMemFree(info->id);
  89. info->id = NULL;
  90. return FALSE;
  91. }
  92. PropVariantInit(&info->name);
  93. hr = IPropertyStore_GetValue(ps,
  94. (PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &info->name);
  95. if(FAILED(hr)){
  96. CoTaskMemFree(info->id);
  97. info->id = NULL;
  98. IPropertyStore_Release(ps);
  99. return FALSE;
  100. }
  101. PropVariantInit(&pv);
  102. hr = IPropertyStore_GetValue(ps,
  103. &PKEY_AudioEndpoint_PhysicalSpeakers, &pv);
  104. info->speaker_config = -1;
  105. if(SUCCEEDED(hr) && pv.vt == VT_UI4){
  106. i = 0;
  107. while (speaker_configs[i].text_id != 0) {
  108. if ((speaker_configs[i].speaker_mask & pv.ulVal) == speaker_configs[i].speaker_mask) {
  109. info->speaker_config = i;
  110. break;
  111. }
  112. i++;
  113. }
  114. }
  115. /* fallback to stereo */
  116. if(info->speaker_config == -1)
  117. info->speaker_config = 2;
  118. IPropertyStore_Release(ps);
  119. return TRUE;
  120. }
  121. static BOOL load_devices(IMMDeviceEnumerator *devenum, EDataFlow dataflow,
  122. UINT *ndevs, struct DeviceInfo **out)
  123. {
  124. IMMDeviceCollection *coll;
  125. UINT i;
  126. HRESULT hr;
  127. hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, dataflow,
  128. DEVICE_STATE_ACTIVE, &coll);
  129. if(FAILED(hr))
  130. return FALSE;
  131. hr = IMMDeviceCollection_GetCount(coll, ndevs);
  132. if(FAILED(hr)){
  133. IMMDeviceCollection_Release(coll);
  134. return FALSE;
  135. }
  136. if(*ndevs > 0){
  137. *out = HeapAlloc(GetProcessHeap(), 0,
  138. sizeof(struct DeviceInfo) * (*ndevs));
  139. if(!*out){
  140. IMMDeviceCollection_Release(coll);
  141. return FALSE;
  142. }
  143. for(i = 0; i < *ndevs; ++i){
  144. IMMDevice *dev;
  145. hr = IMMDeviceCollection_Item(coll, i, &dev);
  146. if(FAILED(hr)){
  147. (*out)[i].id = NULL;
  148. continue;
  149. }
  150. load_device(dev, &(*out)[i]);
  151. IMMDevice_Release(dev);
  152. }
  153. }else
  154. *out = NULL;
  155. IMMDeviceCollection_Release(coll);
  156. return TRUE;
  157. }
  158. static BOOL get_driver_name(IMMDeviceEnumerator *devenum, PROPVARIANT *pv)
  159. {
  160. IMMDevice *device;
  161. IPropertyStore *ps;
  162. HRESULT hr;
  163. static const WCHAR wine_info_deviceW[] = {'W','i','n','e',' ',
  164. 'i','n','f','o',' ','d','e','v','i','c','e',0};
  165. hr = IMMDeviceEnumerator_GetDevice(devenum, wine_info_deviceW, &device);
  166. if(FAILED(hr))
  167. return FALSE;
  168. hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
  169. if(FAILED(hr)){
  170. IMMDevice_Release(device);
  171. return FALSE;
  172. }
  173. hr = IPropertyStore_GetValue(ps,
  174. (const PROPERTYKEY *)&DEVPKEY_Device_Driver, pv);
  175. IPropertyStore_Release(ps);
  176. IMMDevice_Release(device);
  177. if(FAILED(hr))
  178. return FALSE;
  179. return TRUE;
  180. }
  181. static void initAudioDlg (HWND hDlg)
  182. {
  183. WCHAR display_str[256], format_str[256], sysdefault_str[256], disabled_str[64];
  184. IMMDeviceEnumerator *devenum;
  185. BOOL have_driver = FALSE;
  186. HRESULT hr;
  187. UINT i;
  188. LVCOLUMNW lvcol;
  189. WCHAR colW[64], speaker_str[256];
  190. RECT rect;
  191. DWORD width;
  192. WINE_TRACE("\n");
  193. LoadStringW(GetModuleHandleW(NULL), IDS_AUDIO_DRIVER, format_str, ARRAY_SIZE(format_str));
  194. LoadStringW(GetModuleHandleW(NULL), IDS_AUDIO_DRIVER_NONE, disabled_str,
  195. ARRAY_SIZE(disabled_str));
  196. LoadStringW(GetModuleHandleW(NULL), IDS_AUDIO_SYSDEFAULT, sysdefault_str,
  197. ARRAY_SIZE(sysdefault_str));
  198. hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
  199. CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&devenum);
  200. if(SUCCEEDED(hr)){
  201. PROPVARIANT pv;
  202. load_devices(devenum, eRender, &num_render_devs, &render_devs);
  203. load_devices(devenum, eCapture, &num_capture_devs, &capture_devs);
  204. PropVariantInit(&pv);
  205. if(get_driver_name(devenum, &pv) && pv.pwszVal[0] != '\0'){
  206. have_driver = TRUE;
  207. wnsprintfW(display_str, ARRAY_SIZE(display_str), format_str, pv.pwszVal);
  208. lstrcatW(g_drv_keyW, pv.pwszVal);
  209. }
  210. PropVariantClear(&pv);
  211. IMMDeviceEnumerator_Release(devenum);
  212. }
  213. SendDlgItemMessageW(hDlg, IDC_AUDIOOUT_DEVICE, CB_ADDSTRING,
  214. 0, (LPARAM)sysdefault_str);
  215. SendDlgItemMessageW(hDlg, IDC_AUDIOOUT_DEVICE, CB_SETCURSEL, 0, 0);
  216. SendDlgItemMessageW(hDlg, IDC_VOICEOUT_DEVICE, CB_ADDSTRING,
  217. 0, (LPARAM)sysdefault_str);
  218. SendDlgItemMessageW(hDlg, IDC_VOICEOUT_DEVICE, CB_SETCURSEL, 0, 0);
  219. SendDlgItemMessageW(hDlg, IDC_AUDIOIN_DEVICE, CB_ADDSTRING,
  220. 0, (LPARAM)sysdefault_str);
  221. SendDlgItemMessageW(hDlg, IDC_AUDIOIN_DEVICE, CB_SETCURSEL, 0, 0);
  222. SendDlgItemMessageW(hDlg, IDC_VOICEIN_DEVICE, CB_ADDSTRING,
  223. 0, (LPARAM)sysdefault_str);
  224. SendDlgItemMessageW(hDlg, IDC_VOICEIN_DEVICE, CB_SETCURSEL, 0, 0);
  225. i = 0;
  226. while (speaker_configs[i].text_id != 0) {
  227. LoadStringW(GetModuleHandleW(NULL), speaker_configs[i].text_id, speaker_str,
  228. ARRAY_SIZE(speaker_str));
  229. SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_SPEAKERS, CB_ADDSTRING,
  230. 0, (LPARAM)speaker_str);
  231. i++;
  232. }
  233. GetClientRect(GetDlgItem(hDlg, IDC_LIST_AUDIO_DEVICES), &rect);
  234. width = (rect.right - rect.left) * 3 / 5;
  235. LoadStringW(GetModuleHandleW(NULL), IDS_AUDIO_DEVICE, colW, ARRAY_SIZE(colW));
  236. lvcol.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
  237. lvcol.pszText = colW;
  238. lvcol.cchTextMax = lstrlenW(colW);
  239. lvcol.cx = width;
  240. SendDlgItemMessageW(hDlg, IDC_LIST_AUDIO_DEVICES, LVM_INSERTCOLUMNW, 0, (LPARAM)&lvcol);
  241. LoadStringW(GetModuleHandleW(NULL), IDS_AUDIO_SPEAKER_CONFIG, colW, ARRAY_SIZE(colW));
  242. lvcol.pszText = colW;
  243. lvcol.cchTextMax = lstrlenW(colW);
  244. lvcol.cx = rect.right - rect.left - width;
  245. SendDlgItemMessageW(hDlg, IDC_LIST_AUDIO_DEVICES, LVM_INSERTCOLUMNW, 1, (LPARAM)&lvcol);
  246. EnableWindow(GetDlgItem(hDlg, IDC_SPEAKERCONFIG_SPEAKERS), 0);
  247. if(have_driver){
  248. WCHAR *reg_out_dev, *reg_vout_dev, *reg_in_dev, *reg_vin_dev;
  249. reg_out_dev = get_reg_keyW(HKEY_CURRENT_USER, g_drv_keyW, reg_out_nameW, NULL);
  250. reg_vout_dev = get_reg_keyW(HKEY_CURRENT_USER, g_drv_keyW, reg_vout_nameW, NULL);
  251. reg_in_dev = get_reg_keyW(HKEY_CURRENT_USER, g_drv_keyW, reg_in_nameW, NULL);
  252. reg_vin_dev = get_reg_keyW(HKEY_CURRENT_USER, g_drv_keyW, reg_vin_nameW, NULL);
  253. for(i = 0; i < num_render_devs; ++i){
  254. LVITEMW lvitem;
  255. if(!render_devs[i].id)
  256. continue;
  257. SendDlgItemMessageW(hDlg, IDC_AUDIOOUT_DEVICE, CB_ADDSTRING,
  258. 0, (LPARAM)render_devs[i].name.pwszVal);
  259. SendDlgItemMessageW(hDlg, IDC_AUDIOOUT_DEVICE, CB_SETITEMDATA,
  260. i + 1, (LPARAM)&render_devs[i]);
  261. if(reg_out_dev && !lstrcmpW(render_devs[i].id, reg_out_dev)){
  262. SendDlgItemMessageW(hDlg, IDC_AUDIOOUT_DEVICE, CB_SETCURSEL, i + 1, 0);
  263. SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_SPEAKERS, CB_SETCURSEL, render_devs[i].speaker_config, 0);
  264. }
  265. SendDlgItemMessageW(hDlg, IDC_VOICEOUT_DEVICE, CB_ADDSTRING,
  266. 0, (LPARAM)render_devs[i].name.pwszVal);
  267. SendDlgItemMessageW(hDlg, IDC_VOICEOUT_DEVICE, CB_SETITEMDATA,
  268. i + 1, (LPARAM)&render_devs[i]);
  269. if(reg_vout_dev && !lstrcmpW(render_devs[i].id, reg_vout_dev))
  270. SendDlgItemMessageW(hDlg, IDC_VOICEOUT_DEVICE, CB_SETCURSEL, i + 1, 0);
  271. lvitem.mask = LVIF_TEXT | LVIF_PARAM;
  272. lvitem.iItem = i;
  273. lvitem.iSubItem = 0;
  274. lvitem.pszText = render_devs[i].name.pwszVal;
  275. lvitem.cchTextMax = lstrlenW(lvitem.pszText);
  276. lvitem.lParam = (LPARAM)&render_devs[i];
  277. SendDlgItemMessageW(hDlg, IDC_LIST_AUDIO_DEVICES, LVM_INSERTITEMW, 0, (LPARAM)&lvitem);
  278. LoadStringW(GetModuleHandleW(NULL), speaker_configs[render_devs[i].speaker_config].text_id,
  279. speaker_str, ARRAY_SIZE(speaker_str));
  280. lvitem.mask = LVIF_TEXT;
  281. lvitem.iItem = i;
  282. lvitem.iSubItem = 1;
  283. lvitem.pszText = speaker_str;
  284. lvitem.cchTextMax = lstrlenW(lvitem.pszText);
  285. SendDlgItemMessageW(hDlg, IDC_LIST_AUDIO_DEVICES, LVM_SETITEMW, 0, (LPARAM)&lvitem);
  286. }
  287. for(i = 0; i < num_capture_devs; ++i){
  288. if(!capture_devs[i].id)
  289. continue;
  290. SendDlgItemMessageW(hDlg, IDC_AUDIOIN_DEVICE, CB_ADDSTRING,
  291. 0, (LPARAM)capture_devs[i].name.pwszVal);
  292. SendDlgItemMessageW(hDlg, IDC_AUDIOIN_DEVICE, CB_SETITEMDATA,
  293. i + 1, (LPARAM)&capture_devs[i]);
  294. if(reg_in_dev && !lstrcmpW(capture_devs[i].id, reg_in_dev))
  295. SendDlgItemMessageW(hDlg, IDC_AUDIOIN_DEVICE, CB_SETCURSEL, i + 1, 0);
  296. SendDlgItemMessageW(hDlg, IDC_VOICEIN_DEVICE, CB_ADDSTRING,
  297. 0, (LPARAM)capture_devs[i].name.pwszVal);
  298. SendDlgItemMessageW(hDlg, IDC_VOICEIN_DEVICE, CB_SETITEMDATA,
  299. i + 1, (LPARAM)&capture_devs[i]);
  300. if(reg_vin_dev && !lstrcmpW(capture_devs[i].id, reg_vin_dev))
  301. SendDlgItemMessageW(hDlg, IDC_VOICEIN_DEVICE, CB_SETCURSEL, i + 1, 0);
  302. }
  303. HeapFree(GetProcessHeap(), 0, reg_out_dev);
  304. HeapFree(GetProcessHeap(), 0, reg_vout_dev);
  305. HeapFree(GetProcessHeap(), 0, reg_in_dev);
  306. HeapFree(GetProcessHeap(), 0, reg_vin_dev);
  307. }else
  308. wnsprintfW(display_str, ARRAY_SIZE(display_str), format_str, disabled_str);
  309. SetDlgItemTextW(hDlg, IDC_AUDIO_DRIVER, display_str);
  310. }
  311. static void set_reg_device(HWND hDlg, int dlgitem, const WCHAR *key_name)
  312. {
  313. UINT idx;
  314. struct DeviceInfo *info;
  315. idx = SendDlgItemMessageW(hDlg, dlgitem, CB_GETCURSEL, 0, 0);
  316. info = (struct DeviceInfo *)SendDlgItemMessageW(hDlg, dlgitem,
  317. CB_GETITEMDATA, idx, 0);
  318. if(!info || info == (void*)CB_ERR)
  319. set_reg_keyW(HKEY_CURRENT_USER, g_drv_keyW, key_name, NULL);
  320. else
  321. set_reg_keyW(HKEY_CURRENT_USER, g_drv_keyW, key_name, info->id);
  322. }
  323. static void test_sound(void)
  324. {
  325. if(!PlaySoundW(MAKEINTRESOURCEW(IDW_TESTSOUND), NULL, SND_RESOURCE | SND_ASYNC)){
  326. WCHAR error_str[256], title_str[256];
  327. LoadStringW(GetModuleHandleW(NULL), IDS_AUDIO_TEST_FAILED, error_str,
  328. ARRAY_SIZE(error_str));
  329. LoadStringW(GetModuleHandleW(NULL), IDS_AUDIO_TEST_FAILED_TITLE, title_str,
  330. ARRAY_SIZE(title_str));
  331. MessageBoxW(NULL, error_str, title_str, MB_OK | MB_ICONERROR);
  332. }
  333. }
  334. static void apply_speaker_configs(void)
  335. {
  336. UINT i;
  337. IMMDeviceEnumerator *devenum;
  338. IMMDevice *dev;
  339. IPropertyStore *ps;
  340. PROPVARIANT pv;
  341. HRESULT hr;
  342. hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
  343. CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&devenum);
  344. if(FAILED(hr)){
  345. ERR("Unable to create MMDeviceEnumerator: 0x%08x\n", hr);
  346. return;
  347. }
  348. PropVariantInit(&pv);
  349. pv.vt = VT_UI4;
  350. for (i = 0; i < num_render_devs; i++) {
  351. hr = IMMDeviceEnumerator_GetDevice(devenum, render_devs[i].id, &dev);
  352. if(FAILED(hr)){
  353. WARN("Could not get MMDevice for %s: 0x%08x\n", wine_dbgstr_w(render_devs[i].id), hr);
  354. continue;
  355. }
  356. hr = IMMDevice_OpenPropertyStore(dev, STGM_WRITE, &ps);
  357. if(FAILED(hr)){
  358. WARN("Could not open property store for %s: 0x%08x\n", wine_dbgstr_w(render_devs[i].id), hr);
  359. IMMDevice_Release(dev);
  360. continue;
  361. }
  362. pv.ulVal = speaker_configs[render_devs[i].speaker_config].speaker_mask;
  363. hr = IPropertyStore_SetValue(ps, &PKEY_AudioEndpoint_PhysicalSpeakers, &pv);
  364. if (FAILED(hr))
  365. WARN("IPropertyStore_SetValue failed for %s: 0x%08x\n", wine_dbgstr_w(render_devs[i].id), hr);
  366. IPropertyStore_Release(ps);
  367. IMMDevice_Release(dev);
  368. }
  369. IMMDeviceEnumerator_Release(devenum);
  370. }
  371. static void listview_changed(HWND hDlg)
  372. {
  373. int idx;
  374. idx = SendDlgItemMessageW(hDlg, IDC_LIST_AUDIO_DEVICES, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
  375. if(idx < 0) {
  376. EnableWindow(GetDlgItem(hDlg, IDC_SPEAKERCONFIG_SPEAKERS), 0);
  377. return;
  378. }
  379. SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_SPEAKERS, CB_SETCURSEL,
  380. render_devs[idx].speaker_config, 0);
  381. EnableWindow(GetDlgItem(hDlg, IDC_SPEAKERCONFIG_SPEAKERS), 1);
  382. }
  383. INT_PTR CALLBACK
  384. AudioDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  385. {
  386. switch (uMsg) {
  387. case WM_COMMAND:
  388. switch (LOWORD(wParam)) {
  389. case IDC_AUDIO_TEST:
  390. test_sound();
  391. break;
  392. case IDC_AUDIOOUT_DEVICE:
  393. if(HIWORD(wParam) == CBN_SELCHANGE){
  394. set_reg_device(hDlg, IDC_AUDIOOUT_DEVICE, reg_out_nameW);
  395. SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0);
  396. }
  397. break;
  398. case IDC_VOICEOUT_DEVICE:
  399. if(HIWORD(wParam) == CBN_SELCHANGE){
  400. set_reg_device(hDlg, IDC_VOICEOUT_DEVICE, reg_vout_nameW);
  401. SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0);
  402. }
  403. break;
  404. case IDC_AUDIOIN_DEVICE:
  405. if(HIWORD(wParam) == CBN_SELCHANGE){
  406. set_reg_device(hDlg, IDC_AUDIOIN_DEVICE, reg_in_nameW);
  407. SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0);
  408. }
  409. break;
  410. case IDC_VOICEIN_DEVICE:
  411. if(HIWORD(wParam) == CBN_SELCHANGE){
  412. set_reg_device(hDlg, IDC_VOICEIN_DEVICE, reg_vin_nameW);
  413. SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0);
  414. }
  415. break;
  416. case IDC_SPEAKERCONFIG_SPEAKERS:
  417. if(HIWORD(wParam) == CBN_SELCHANGE){
  418. UINT dev, idx;
  419. idx = SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_SPEAKERS, CB_GETCURSEL, 0, 0);
  420. dev = SendDlgItemMessageW(hDlg, IDC_LIST_AUDIO_DEVICES, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
  421. if(dev < num_render_devs){
  422. WCHAR speaker_str[256];
  423. LVITEMW lvitem;
  424. render_devs[dev].speaker_config = idx;
  425. LoadStringW(GetModuleHandleW(NULL), speaker_configs[idx].text_id,
  426. speaker_str, ARRAY_SIZE(speaker_str));
  427. lvitem.mask = LVIF_TEXT;
  428. lvitem.iItem = dev;
  429. lvitem.iSubItem = 1;
  430. lvitem.pszText = speaker_str;
  431. lvitem.cchTextMax = lstrlenW(lvitem.pszText);
  432. SendDlgItemMessageW(hDlg, IDC_LIST_AUDIO_DEVICES, LVM_SETITEMW, 0, (LPARAM)&lvitem);
  433. SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0);
  434. }
  435. }
  436. break;
  437. }
  438. break;
  439. case WM_SHOWWINDOW:
  440. set_window_title(hDlg);
  441. break;
  442. case WM_NOTIFY:
  443. switch(((LPNMHDR)lParam)->code) {
  444. case PSN_KILLACTIVE:
  445. SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, FALSE);
  446. break;
  447. case PSN_APPLY:
  448. apply_speaker_configs();
  449. apply();
  450. SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
  451. break;
  452. case PSN_SETACTIVE:
  453. break;
  454. case LVN_ITEMCHANGED:
  455. listview_changed(hDlg);
  456. break;
  457. }
  458. break;
  459. case WM_INITDIALOG:
  460. initAudioDlg(hDlg);
  461. break;
  462. }
  463. return FALSE;
  464. }