From: Mark Harmstone Subject: [PATCH 3/3] winecfg: add speaker config controls to audio tab Message-Id: <54C6BF8A.2060800@burntcomma.com> Date: Mon, 26 Jan 2015 22:28:26 +0000 --- programs/winecfg/audio.c | 154 +++++++++++++++++++++++++++++++++++++++++++- programs/winecfg/winecfg.rc | 5 ++ 2 files changed, 156 insertions(+), 3 deletions(-) diff --git a/programs/winecfg/audio.c b/programs/winecfg/audio.c index 4e3158c..c260c92 100644 --- a/programs/winecfg/audio.c +++ b/programs/winecfg/audio.c @@ -45,6 +45,7 @@ #include "ole2.h" #include "initguid.h" +#include "propkey.h" #include "devpkey.h" #include "mmdeviceapi.h" #include "audioclient.h" @@ -58,6 +59,12 @@ WINE_DEFAULT_DEBUG_CHANNEL(winecfg); struct DeviceInfo { WCHAR *id; PROPVARIANT name; + int speaker_config; +}; + +struct SpeakerConfig { + int text_id; + DWORD speaker_mask; }; static WCHAR g_drv_keyW[256] = {'S','o','f','t','w','a','r','e','\\', @@ -71,10 +78,21 @@ static const WCHAR reg_vin_nameW[] = {'D','e','f','a','u','l','t','V','o','i','c static UINT num_render_devs, num_capture_devs; static struct DeviceInfo *render_devs, *capture_devs; +static const struct SpeakerConfig speaker_configs[] = +{ + { IDS_AUDIO_SPEAKER_5POINT1, KSAUDIO_SPEAKER_5POINT1 }, + { IDS_AUDIO_SPEAKER_QUAD, KSAUDIO_SPEAKER_QUAD }, + { IDS_AUDIO_SPEAKER_STEREO, KSAUDIO_SPEAKER_STEREO }, + { IDS_AUDIO_SPEAKER_MONO, KSAUDIO_SPEAKER_MONO }, + { 0, 0 } +}; + static BOOL load_device(IMMDevice *dev, struct DeviceInfo *info) { IPropertyStore *ps; HRESULT hr; + PROPVARIANT pv; + UINT i; hr = IMMDevice_GetId(dev, &info->id); if(FAILED(hr)){ @@ -93,13 +111,34 @@ static BOOL load_device(IMMDevice *dev, struct DeviceInfo *info) hr = IPropertyStore_GetValue(ps, (PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &info->name); - IPropertyStore_Release(ps); if(FAILED(hr)){ CoTaskMemFree(info->id); info->id = NULL; + IPropertyStore_Release(ps); return FALSE; } + PropVariantInit(&pv); + + hr = IPropertyStore_GetValue(ps, + &PKEY_AudioEndpoint_PhysicalSpeakers, &pv); + + info->speaker_config = 999; + if(SUCCEEDED(hr) && pv.vt == VT_UI4){ + i = 0; + while (speaker_configs[i].text_id != 0 && info->speaker_config == 999) { + if ((speaker_configs[i].speaker_mask & pv.u.ulVal) == speaker_configs[i].speaker_mask) + info->speaker_config = i; + i++; + } + } + + /* fallback to stereo */ + if(info->speaker_config == 999) + info->speaker_config = 2; + + IPropertyStore_Release(ps); + return TRUE; } @@ -185,6 +224,7 @@ static void initAudioDlg (HWND hDlg) IMMDeviceEnumerator *devenum; BOOL have_driver = FALSE; HRESULT hr; + UINT i; WINE_TRACE("\n"); @@ -229,15 +269,31 @@ static void initAudioDlg (HWND hDlg) 0, (LPARAM)sysdefault_str); SendDlgItemMessageW(hDlg, IDC_VOICEIN_DEVICE, CB_SETCURSEL, 0, 0); + i = 0; + while (speaker_configs[i].text_id != 0) { + WCHAR speaker_str[256]; + + LoadStringW(GetModuleHandleW(NULL), speaker_configs[i].text_id, + speaker_str, sizeof(speaker_str) / sizeof(*speaker_str)); + + SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_SPEAKERS, CB_ADDSTRING, + 0, (LPARAM)speaker_str); + + i++; + } + if(have_driver){ WCHAR *reg_out_dev, *reg_vout_dev, *reg_in_dev, *reg_vin_dev; - UINT i; + BOOL default_dev_found = FALSE; reg_out_dev = get_reg_keyW(HKEY_CURRENT_USER, g_drv_keyW, reg_out_nameW, NULL); reg_vout_dev = get_reg_keyW(HKEY_CURRENT_USER, g_drv_keyW, reg_vout_nameW, NULL); reg_in_dev = get_reg_keyW(HKEY_CURRENT_USER, g_drv_keyW, reg_in_nameW, NULL); reg_vin_dev = get_reg_keyW(HKEY_CURRENT_USER, g_drv_keyW, reg_vin_nameW, NULL); + SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_DEVICE, CB_SETCURSEL, i, 0); + SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_SPEAKERS, CB_SETCURSEL, render_devs[i].speaker_config, 0); + for(i = 0; i < num_render_devs; ++i){ if(!render_devs[i].id) continue; @@ -246,8 +302,16 @@ static void initAudioDlg (HWND hDlg) 0, (LPARAM)render_devs[i].name.u.pwszVal); SendDlgItemMessageW(hDlg, IDC_AUDIOOUT_DEVICE, CB_SETITEMDATA, i + 1, (LPARAM)&render_devs[i]); - if(reg_out_dev && !lstrcmpW(render_devs[i].id, reg_out_dev)) + + SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_DEVICE, CB_ADDSTRING, + 0, (LPARAM)render_devs[i].name.u.pwszVal); + + if(reg_out_dev && !lstrcmpW(render_devs[i].id, reg_out_dev)){ SendDlgItemMessageW(hDlg, IDC_AUDIOOUT_DEVICE, CB_SETCURSEL, i + 1, 0); + SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_DEVICE, CB_SETCURSEL, i, 0); + SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_SPEAKERS, CB_SETCURSEL, render_devs[i].speaker_config, 0); + default_dev_found = TRUE; + } SendDlgItemMessageW(hDlg, IDC_VOICEOUT_DEVICE, CB_ADDSTRING, 0, (LPARAM)render_devs[i].name.u.pwszVal); @@ -257,6 +321,11 @@ static void initAudioDlg (HWND hDlg) SendDlgItemMessageW(hDlg, IDC_VOICEOUT_DEVICE, CB_SETCURSEL, i + 1, 0); } + if(!default_dev_found && num_render_devs > 0){ + SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_DEVICE, CB_SETCURSEL, 0, 0); + SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_SPEAKERS, CB_SETCURSEL, render_devs[0].speaker_config, 0); + } + for(i = 0; i < num_capture_devs; ++i){ if(!capture_devs[i].id) continue; @@ -317,6 +386,60 @@ static void test_sound(void) } } +static void apply_speaker_configs(void) +{ + UINT i; + IMMDeviceEnumerator *devenum; + IMMDevice *dev; + IPropertyStore *ps; + PROPVARIANT pv; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, + CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&devenum); + + if(FAILED(hr)){ + ERR("Unable to create MMDeviceEnumerator: 0x%08x\n", hr); + return; + } + + PropVariantInit(&pv); + pv.vt = VT_UI4; + + for (i = 0; i < num_render_devs; i++) { + ERR("%s -> %u\n", wine_dbgstr_w(render_devs[i].id), speaker_configs[render_devs[i].speaker_config].speaker_mask); + hr = IMMDeviceEnumerator_GetDevice(devenum, render_devs[i].id, &dev); + + if(FAILED(hr)){ + WARN("Could not get MMDevice for %s: 0x%08x\n", wine_dbgstr_w(render_devs[i].id), hr); + continue; + } + + hr = IMMDevice_OpenPropertyStore(dev, STGM_WRITE, &ps); + + if(FAILED(hr)){ + WARN("Could not open property store for %s: 0x%08x\n", wine_dbgstr_w(render_devs[i].id), hr); + IMMDevice_Release(dev); + continue; + } + + pv.u.ulVal = speaker_configs[render_devs[i].speaker_config].speaker_mask; + WARN("speaker_config = %u\n", pv.u.ulVal); + + hr = IPropertyStore_SetValue(ps, &PKEY_AudioEndpoint_PhysicalSpeakers, &pv); + + if (FAILED(hr)) + WARN("IPropertyStore_SetValue failed for %s: 0x%08x\n", wine_dbgstr_w(render_devs[i].id), hr); + + IPropertyStore_Commit(ps); + + IPropertyStore_Release(ps); + IMMDevice_Release(dev); + } + + IMMDeviceEnumerator_Release(devenum); +} + INT_PTR CALLBACK AudioDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { @@ -350,6 +473,30 @@ AudioDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0); } break; + case IDC_SPEAKERCONFIG_DEVICE: + if(HIWORD(wParam) == CBN_SELCHANGE){ + UINT idx; + + idx = SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_DEVICE, CB_GETCURSEL, 0, 0); + + if(idx < num_render_devs){ + SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_SPEAKERS, CB_SETCURSEL, render_devs[idx].speaker_config, 0); + } + } + break; + case IDC_SPEAKERCONFIG_SPEAKERS: + if(HIWORD(wParam) == CBN_SELCHANGE){ + UINT dev, idx; + + idx = SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_SPEAKERS, CB_GETCURSEL, 0, 0); + dev = SendDlgItemMessageW(hDlg, IDC_SPEAKERCONFIG_DEVICE, CB_GETCURSEL, 0, 0); + + if(dev < num_render_devs){ + render_devs[dev].speaker_config = idx; + SendMessageW(GetParent(hDlg), PSM_CHANGED, 0, 0); + } + } + break; } break; @@ -363,6 +510,7 @@ AudioDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, FALSE); break; case PSN_APPLY: + apply_speaker_configs(); apply(); SetWindowLongPtrW(hDlg, DWLP_MSGRESULT, PSNRET_NOERROR); break; diff --git a/programs/winecfg/winecfg.rc b/programs/winecfg/winecfg.rc index fb86120..6fc1c26 100644 --- a/programs/winecfg/winecfg.rc +++ b/programs/winecfg/winecfg.rc @@ -271,6 +271,11 @@ BEGIN COMBOBOX IDC_AUDIOIN_DEVICE,110,75,135,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP COMBOBOX IDC_VOICEIN_DEVICE,110,91,135,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "&Test Sound",IDC_AUDIO_TEST,8,113,69,14 + GROUPBOX "Speaker configuration",IDC_STATIC,8,132,244,44 + LTEXT "Device:",IDC_STATIC,18,144,230,8 + COMBOBOX IDC_SPEAKERCONFIG_DEVICE,110,142,135,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Speakers:",IDC_STATIC,18,160,230,8 + COMBOBOX IDC_SPEAKERCONFIG_SPEAKERS,110,158,135,14,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP END IDD_DESKTOP_INTEGRATION DIALOG 0, 0, 260, 220