mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2024-11-21 16:46:54 -08:00
winepulse: expose individual audio devices directly
This commit is contained in:
parent
463d01d3aa
commit
305af53597
@ -39,6 +39,11 @@ Wine. All those differences are also documented on the
|
||||
Included bug fixes and improvements
|
||||
===================================
|
||||
|
||||
**Bugfixes and features included in the next upcoming release [1]:**
|
||||
|
||||
* Allow selection of audio device for PulseAudio backend
|
||||
|
||||
|
||||
**Bugs fixed in Wine Staging 1.7.30 [90]:**
|
||||
|
||||
* ATL IOCS data should not be stored in GWLP_USERDATA ([Wine Bug #21767](https://bugs.winehq.org/show_bug.cgi?id=21767))
|
||||
|
1
debian/changelog
vendored
1
debian/changelog
vendored
@ -1,5 +1,6 @@
|
||||
wine-compholio (1.7.31) UNRELEASED; urgency=low
|
||||
* Added possibility to temporarily disable patches to patch system.
|
||||
* Added patch to allow selecting specific audio device for PulseAudio backend.
|
||||
* Removed patch for iphlpapi stub functions (accepted upstream).
|
||||
* Removed first part of patches for FindFirstFileExW (accepted upstream).
|
||||
-- Sebastian Lackner <sebastian@fds-team.de> Mon, 03 Nov 2014 20:10:04 +0100
|
||||
|
@ -1622,6 +1622,7 @@ winemenubuilder-Desktop_Icon_Path.ok:
|
||||
# | Included patches:
|
||||
# | * Winepulse patches extracted from https://launchpad.net/~ubuntu-
|
||||
# | wine/+archive/ubuntu/ppa/+files/wine1.7_1.7.22-0ubuntu1.debian.tar.gz. [rev 4, by Maarten Lankhorst]
|
||||
# | * Expose audio devices directly to programs. [by Mark Harmstone]
|
||||
# |
|
||||
# | This patchset fixes the following Wine bugs:
|
||||
# | * [#10495] Support for PulseAudio backend for audio
|
||||
@ -1659,8 +1660,10 @@ winepulse-PulseAudio_Support.ok:
|
||||
$(call APPLY_FILE,winepulse-PulseAudio_Support/0025-winepulse-use-a-pi-mutex-for-serialization.patch)
|
||||
$(call APPLY_FILE,winepulse-PulseAudio_Support/0026-winepulse-add-support-for-IMarshal.patch)
|
||||
$(call APPLY_FILE,winepulse-PulseAudio_Support/0027-winepulse-handle-stream-create-failing-correctly.patch)
|
||||
$(call APPLY_FILE,winepulse-PulseAudio_Support/0028-winepulse-expose-audio-devices-directly-to-programs.patch)
|
||||
@( \
|
||||
echo '+ { "winepulse-PulseAudio_Support", "Maarten Lankhorst", "Winepulse patches extracted from https://launchpad.net/~ubuntu-wine/+archive/ubuntu/ppa/+files/wine1.7_1.7.22-0ubuntu1.debian.tar.gz. [rev 4]" },'; \
|
||||
echo '+ { "winepulse-PulseAudio_Support", "Mark Harmstone", "Expose audio devices directly to programs." },'; \
|
||||
) > winepulse-PulseAudio_Support.ok
|
||||
|
||||
# Patchset winex11-CandidateWindowPos
|
||||
|
@ -0,0 +1,329 @@
|
||||
From daf353964820e0c9c3b49a5dab3eb952f27d27c3 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Harmstone <mark@harmstone.com>
|
||||
Date: Mon, 3 Nov 2014 02:06:40 +0000
|
||||
Subject: winepulse: expose audio devices directly to programs
|
||||
|
||||
At present, winepulse only exposes one input device and one output device. This
|
||||
patch adds support for individual audio devices, allowing (among other things)
|
||||
the same program to record from two devices at the same time. It also brings
|
||||
winepulse more in line with both winealsa et al. and Windows itself. The
|
||||
moveable "Pulseaudio" devices are still present, and should presumably be
|
||||
used by default.
|
||||
|
||||
Changes by Sebastian Lackner <sebastian@fds-team.de>:
|
||||
* Merge functions set_device_guid and get_device_guid as they are always used together
|
||||
* Fixed compiler warnings with -Werror
|
||||
* Some style fixes and better error handling
|
||||
---
|
||||
dlls/winepulse.drv/mmdevdrv.c | 227 ++++++++++++++++++++++++++++++++++++++++--
|
||||
1 file changed, 220 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
|
||||
index e755e8a..f6688d8 100644
|
||||
--- a/dlls/winepulse.drv/mmdevdrv.c
|
||||
+++ b/dlls/winepulse.drv/mmdevdrv.c
|
||||
@@ -85,6 +85,11 @@ const WCHAR pulse_keyW[] = {'S','o','f','t','w','a','r','e','\\',
|
||||
'W','i','n','e','\\','P','u','l','s','e',0};
|
||||
const WCHAR pulse_streamW[] = { 'S','t','r','e','a','m','V','o','l',0 };
|
||||
|
||||
+static const WCHAR drv_key_devicesW[] = {'S','o','f','t','w','a','r','e','\\',
|
||||
+ 'W','i','n','e','\\','D','r','i','v','e','r','s','\\',
|
||||
+ 'w','i','n','e','p','u','l','s','e','.','d','r','v','\\','D','e','v','i','c','e','s',0};
|
||||
+static const WCHAR guidW[] = {'g','u','i','d',0};
|
||||
+
|
||||
static GUID pulse_render_guid =
|
||||
{ 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
|
||||
static GUID pulse_capture_guid =
|
||||
@@ -173,6 +178,7 @@ struct ACImpl {
|
||||
IMMDevice *parent;
|
||||
struct list entry;
|
||||
float vol[PA_CHANNELS_MAX];
|
||||
+ char device[256];
|
||||
|
||||
LONG ref;
|
||||
EDataFlow dataflow;
|
||||
@@ -692,6 +698,9 @@ static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
|
||||
char buffer[64];
|
||||
static LONG number;
|
||||
pa_buffer_attr attr;
|
||||
+ int moving = 0;
|
||||
+ const char *dev = NULL;
|
||||
+
|
||||
if (This->stream) {
|
||||
pa_stream_disconnect(This->stream);
|
||||
while (pa_stream_get_state(This->stream) == PA_STREAM_READY)
|
||||
@@ -716,12 +725,21 @@ static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
|
||||
attr.maxlength = attr.tlength = This->bufsize_bytes;
|
||||
attr.prebuf = pa_frame_size(&This->ss);
|
||||
dump_attr(&attr);
|
||||
+
|
||||
+ /* If device name is given use exactly the specified device */
|
||||
+ if (This->device[0]){
|
||||
+ moving = PA_STREAM_DONT_MOVE;
|
||||
+ dev = This->device;
|
||||
+ }
|
||||
+
|
||||
if (This->dataflow == eRender)
|
||||
- ret = pa_stream_connect_playback(This->stream, NULL, &attr,
|
||||
- PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS, NULL, NULL);
|
||||
+ ret = pa_stream_connect_playback(This->stream, dev, &attr,
|
||||
+ PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|
|
||||
+ PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS|moving, NULL, NULL);
|
||||
else
|
||||
- ret = pa_stream_connect_record(This->stream, NULL, &attr,
|
||||
- PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS);
|
||||
+ ret = pa_stream_connect_record(This->stream, dev, &attr,
|
||||
+ PA_STREAM_START_CORKED|PA_STREAM_START_UNMUTED|PA_STREAM_AUTO_TIMING_UPDATE|
|
||||
+ PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_EARLY_REQUESTS|moving);
|
||||
if (ret < 0) {
|
||||
WARN("Returns %i\n", ret);
|
||||
return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
|
||||
@@ -740,11 +758,127 @@ static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
-HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys,
|
||||
+static BOOL get_device_guid(EDataFlow flow, const char *device, GUID *guid)
|
||||
+{
|
||||
+ HKEY key, dev_key;
|
||||
+ DWORD type, size = sizeof(*guid);
|
||||
+ WCHAR key_name[258];
|
||||
+
|
||||
+ key_name[0] = (flow == eCapture) ? '1' : '0';
|
||||
+ key_name[1] = ',';
|
||||
+ if (!MultiByteToWideChar(CP_UTF8, 0, device, -1, key_name + 2,
|
||||
+ (sizeof(key_name) / sizeof(*key_name)) - 2))
|
||||
+ return FALSE;
|
||||
+
|
||||
+ if (RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0,
|
||||
+ KEY_WRITE|KEY_READ, NULL, &key, NULL) != ERROR_SUCCESS){
|
||||
+ ERR("Failed to open registry key %s\n", debugstr_w(drv_key_devicesW));
|
||||
+ return FALSE;
|
||||
+ }
|
||||
+
|
||||
+ if (RegCreateKeyExW(key, key_name, 0, NULL, 0, KEY_WRITE|KEY_READ,
|
||||
+ NULL, &dev_key, NULL) != ERROR_SUCCESS){
|
||||
+ ERR("Failed to open registry key for device %s\n", debugstr_w(key_name));
|
||||
+ RegCloseKey(key);
|
||||
+ return FALSE;
|
||||
+ }
|
||||
+
|
||||
+ if (RegQueryValueExW(dev_key, guidW, 0, &type, (BYTE *)guid,
|
||||
+ &size) == ERROR_SUCCESS){
|
||||
+ if (type == REG_BINARY && size == sizeof(*guid)){
|
||||
+ RegCloseKey(dev_key);
|
||||
+ RegCloseKey(key);
|
||||
+ return TRUE;
|
||||
+ }
|
||||
+
|
||||
+ ERR("Invalid type for device %s GUID: %u; ignoring and overwriting\n",
|
||||
+ wine_dbgstr_w(key_name), type);
|
||||
+ }
|
||||
+
|
||||
+ /* generate new GUID for this device */
|
||||
+ CoCreateGuid(guid);
|
||||
+
|
||||
+ if (RegSetValueExW(dev_key, guidW, 0, REG_BINARY, (BYTE *)guid,
|
||||
+ sizeof(GUID)) != ERROR_SUCCESS)
|
||||
+ ERR("Failed to store device GUID for %s to registry\n", device);
|
||||
+
|
||||
+ RegCloseKey(dev_key);
|
||||
+ RegCloseKey(key);
|
||||
+ return TRUE;
|
||||
+}
|
||||
+
|
||||
+struct pulse_all_info_cb_data {
|
||||
+ EDataFlow flow;
|
||||
+ WCHAR **ids;
|
||||
+ GUID *keys;
|
||||
+ UINT num;
|
||||
+};
|
||||
+
|
||||
+static void pulse_all_sink_info_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata) {
|
||||
+ struct pulse_all_info_cb_data *st = (struct pulse_all_info_cb_data*)userdata;
|
||||
+ void *tmp;
|
||||
+ DWORD len;
|
||||
+
|
||||
+ if (!i)
|
||||
+ return;
|
||||
+
|
||||
+ tmp = HeapReAlloc(GetProcessHeap(), 0, st->ids, sizeof(WCHAR*) * (st->num + 1));
|
||||
+ if (!tmp) return;
|
||||
+ st->ids = tmp;
|
||||
+
|
||||
+ tmp = HeapReAlloc(GetProcessHeap(), 0, st->keys, sizeof(GUID) * (st->num + 1));
|
||||
+ if (!tmp) return;
|
||||
+ st->keys = tmp;
|
||||
+
|
||||
+ len = MultiByteToWideChar(CP_UTF8, 0, i->description, -1, NULL, 0);
|
||||
+ if (!len) return;
|
||||
+
|
||||
+ st->ids[st->num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
||||
+ if (!st->ids[st->num]) return;
|
||||
+
|
||||
+ MultiByteToWideChar(CP_UTF8, 0, i->description, -1, st->ids[st->num], len);
|
||||
+ if (!get_device_guid(st->flow, i->name, &(st->keys[st->num])))
|
||||
+ CoCreateGuid(&(st->keys[st->num]));
|
||||
+
|
||||
+ st->num++;
|
||||
+}
|
||||
+
|
||||
+static void pulse_all_source_info_cb(pa_context *c, const pa_source_info *i, int eol, void *userdata) {
|
||||
+ struct pulse_all_info_cb_data *st = (struct pulse_all_info_cb_data*)userdata;
|
||||
+ void *tmp;
|
||||
+ DWORD len;
|
||||
+
|
||||
+ if (!i)
|
||||
+ return;
|
||||
+
|
||||
+ tmp = HeapReAlloc(GetProcessHeap(), 0, st->ids, sizeof(WCHAR*) * (st->num + 1));
|
||||
+ if (!tmp) return;
|
||||
+ st->ids = tmp;
|
||||
+
|
||||
+ tmp = HeapReAlloc(GetProcessHeap(), 0, st->keys, sizeof(GUID) * (st->num + 1));
|
||||
+ if (!tmp) return;
|
||||
+ st->keys = tmp;
|
||||
+
|
||||
+ len = MultiByteToWideChar(CP_UTF8, 0, i->description, -1, NULL, 0);
|
||||
+ if (!len) return;
|
||||
+
|
||||
+ st->ids[st->num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
||||
+ if (!st->ids[st->num]) return;
|
||||
+
|
||||
+ MultiByteToWideChar(CP_UTF8, 0, i->description, -1, st->ids[st->num], len);
|
||||
+ if (!get_device_guid(st->flow, i->name, &(st->keys[st->num])))
|
||||
+ CoCreateGuid(&(st->keys[st->num]));
|
||||
+
|
||||
+ st->num++;
|
||||
+}
|
||||
+
|
||||
+HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, GUID **keys,
|
||||
UINT *num, UINT *def_index)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
WCHAR *id;
|
||||
+ pa_operation* o;
|
||||
+ struct pulse_all_info_cb_data st;
|
||||
|
||||
TRACE("%d %p %p %p\n", flow, ids, num, def_index);
|
||||
|
||||
@@ -778,6 +912,25 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **
|
||||
else
|
||||
(*keys)[0] = pulse_capture_guid;
|
||||
|
||||
+ st.flow = flow;
|
||||
+ st.ids = *ids;
|
||||
+ st.keys = *keys;
|
||||
+ st.num = *num;
|
||||
+
|
||||
+ if (flow == eRender)
|
||||
+ o = pa_context_get_sink_info_list(pulse_ctx, &pulse_all_sink_info_cb, &st);
|
||||
+ else
|
||||
+ o = pa_context_get_source_info_list(pulse_ctx, &pulse_all_source_info_cb, &st);
|
||||
+
|
||||
+ if (o){
|
||||
+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING){ }
|
||||
+ pa_operation_unref(o);
|
||||
+ }
|
||||
+
|
||||
+ *ids = st.ids;
|
||||
+ *keys = st.keys;
|
||||
+ *num = st.num;
|
||||
+
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -794,8 +947,66 @@ int WINAPI AUDDRV_GetPriority(void)
|
||||
return SUCCEEDED(hr) ? 3 : 0;
|
||||
}
|
||||
|
||||
+static BOOL get_pulse_name_by_guid(const GUID *guid, char *name, DWORD name_size, EDataFlow *flow)
|
||||
+{
|
||||
+ HKEY key;
|
||||
+ DWORD index = 0;
|
||||
+ WCHAR key_name[258];
|
||||
+ DWORD key_name_size;
|
||||
+
|
||||
+ if (RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ,
|
||||
+ &key) != ERROR_SUCCESS){
|
||||
+ ERR("No devices found in registry?\n");
|
||||
+ return FALSE;
|
||||
+ }
|
||||
+
|
||||
+ while(1){
|
||||
+ HKEY dev_key;
|
||||
+ DWORD size, type;
|
||||
+ GUID reg_guid;
|
||||
+
|
||||
+ key_name_size = sizeof(key_name)/sizeof(WCHAR);
|
||||
+ if(RegEnumKeyExW(key, index++, key_name, &key_name_size, NULL,
|
||||
+ NULL, NULL, NULL) != ERROR_SUCCESS)
|
||||
+ break;
|
||||
+
|
||||
+ if (RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) != ERROR_SUCCESS){
|
||||
+ ERR("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ size = sizeof(reg_guid);
|
||||
+ if (RegQueryValueExW(dev_key, guidW, 0, &type, (BYTE *)®_guid, &size) == ERROR_SUCCESS){
|
||||
+ if (type == REG_BINARY && size == sizeof(reg_guid) && IsEqualGUID(®_guid, guid)){
|
||||
+ RegCloseKey(dev_key);
|
||||
+ RegCloseKey(key);
|
||||
+
|
||||
+ TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
|
||||
+
|
||||
+ if (key_name[0] == '0')
|
||||
+ *flow = eRender;
|
||||
+ else if (key_name[0] == '1')
|
||||
+ *flow = eCapture;
|
||||
+ else{
|
||||
+ ERR("Unknown device type: %c\n", key_name[0]);
|
||||
+ return FALSE;
|
||||
+ }
|
||||
+
|
||||
+ return WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, name, name_size, NULL, NULL);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ RegCloseKey(dev_key);
|
||||
+ }
|
||||
+
|
||||
+ RegCloseKey(key);
|
||||
+ WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
|
||||
+ return FALSE;
|
||||
+}
|
||||
+
|
||||
HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
|
||||
{
|
||||
+ char pulse_name[256] = {0};
|
||||
HRESULT hr;
|
||||
ACImpl *This;
|
||||
int i;
|
||||
@@ -813,12 +1024,13 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
|
||||
}
|
||||
|
||||
TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
|
||||
+
|
||||
if (IsEqualGUID(guid, &pulse_render_guid))
|
||||
dataflow = eRender;
|
||||
else if (IsEqualGUID(guid, &pulse_capture_guid))
|
||||
dataflow = eCapture;
|
||||
- else
|
||||
- return E_UNEXPECTED;
|
||||
+ else if(!get_pulse_name_by_guid(guid, pulse_name, sizeof(pulse_name), &dataflow))
|
||||
+ return AUDCLNT_E_DEVICE_INVALIDATED;
|
||||
|
||||
*out = NULL;
|
||||
pthread_mutex_lock(&pulse_lock);
|
||||
@@ -841,6 +1053,7 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
|
||||
This->parent = dev;
|
||||
for (i = 0; i < PA_CHANNELS_MAX; ++i)
|
||||
This->vol[i] = 1.f;
|
||||
+ strcpy(This->device, pulse_name);
|
||||
|
||||
hr = CoCreateFreeThreadedMarshaler((IUnknown*)This, &This->marshal);
|
||||
if (hr) {
|
||||
--
|
||||
2.1.3
|
||||
|
@ -2,3 +2,8 @@ Author: Maarten Lankhorst
|
||||
Subject: Winepulse patches extracted from https://launchpad.net/~ubuntu-wine/+archive/ubuntu/ppa/+files/wine1.7_1.7.22-0ubuntu1.debian.tar.gz.
|
||||
Revision: 4
|
||||
Fixes: [10495] Support for PulseAudio backend for audio
|
||||
|
||||
Author: Mark Harmstone
|
||||
Subject: Expose audio devices directly to programs.
|
||||
Revision: 1
|
||||
Fixes: Allow selection of audio device for PulseAudio backend
|
||||
|
Loading…
Reference in New Issue
Block a user