Skip to content

Instantly share code, notes, and snippets.

@sarnex
Created June 11, 2015 00:49
Show Gist options
  • Save sarnex/648328e6157e7dcef903 to your computer and use it in GitHub Desktop.
Save sarnex/648328e6157e7dcef903 to your computer and use it in GitHub Desktop.
From 5f022bf5b42a6a38819859ff902ffd2c45f75e2e Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:28 +0100
Subject: winemenubuilder: silence an err
---
programs/winemenubuilder/winemenubuilder.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c
index bc1540b..4bb5d09 100644
--- a/programs/winemenubuilder/winemenubuilder.c
+++ b/programs/winemenubuilder/winemenubuilder.c
@@ -2549,7 +2549,7 @@ static BOOL write_freedesktop_association_entry(const char *desktopPath, const c
fclose(desktop);
}
else
- WINE_ERR("error writing association file %s\n", wine_dbgstr_a(desktopPath));
+ WINE_WARN("error writing association file %s\n", wine_dbgstr_a(desktopPath));
return ret;
}
--
2.3.5
From 07360edd466f5ef93c1775a63c6e9cc721d73bb9 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:28 +0100
Subject: valgrind prevent crash hack
---
libs/wine/loader.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/libs/wine/loader.c b/libs/wine/loader.c
index 3591ede..fcb58cf 100644
--- a/libs/wine/loader.c
+++ b/libs/wine/loader.c
@@ -63,6 +63,10 @@ extern char **environ;
#include "winbase.h"
#include "wine/library.h"
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
/* argc/argv for the Windows application */
int __wine_main_argc = 0;
char **__wine_main_argv = NULL;
@@ -650,7 +654,6 @@ int wine_dll_get_owner( const char *name, char *buffer, int size, int *exists )
return ret;
}
-
/***********************************************************************
* set_max_limit
*
@@ -661,6 +664,11 @@ static void set_max_limit( int limit )
#ifdef HAVE_SETRLIMIT
struct rlimit rlimit;
+#if defined(RLIMIT_NOFILE) && defined(RUNNING_ON_VALGRIND)
+ if (limit == RLIMIT_NOFILE && RUNNING_ON_VALGRIND)
+ return;
+#endif
+
if (!getrlimit( limit, &rlimit ))
{
rlimit.rlim_cur = rlimit.rlim_max;
--
2.3.5
From 2edde6c6d72ed2a98a9b13f631ba5aff2e329c68 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:28 +0100
Subject: server: Use rtkit to set realtime priority, try 4
---
libs/wine/loader.c | 27 ++++++++
server/Makefile.in | 2 +
server/rtkit.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++++++
server/thread.c | 16 +++++
4 files changed, 225 insertions(+)
create mode 100644 server/rtkit.c
diff --git a/libs/wine/loader.c b/libs/wine/loader.c
index fcb58cf..600d99b 100644
--- a/libs/wine/loader.c
+++ b/libs/wine/loader.c
@@ -921,6 +921,32 @@ jint JNI_OnLoad( JavaVM *vm, void *reserved )
#endif /* __ANDROID__ */
/***********************************************************************
+ * set_rttime_limit
+ *
+ * set a limit on the cpu time used
+ */
+static void set_rttime_limit(void)
+{
+#if defined(HAVE_SETRLIMIT) && defined(__linux__)
+#ifndef RLIMIT_RTTIME
+#define RLIMIT_RTTIME 15
+#endif
+ struct rlimit rlimit;
+
+ if (!getrlimit( RLIMIT_RTTIME, &rlimit ))
+ {
+ /* 50 ms realtime, then 10 ms to undo realtime */
+ if (rlimit.rlim_max > 150000000ULL)
+ rlimit.rlim_max = 150000000ULL;
+ rlimit.rlim_cur = rlimit.rlim_max - 10000000ULL;
+
+ setrlimit( RLIMIT_RTTIME, &rlimit );
+ }
+#endif
+}
+
+
+/***********************************************************************
* wine_init
*
* Main Wine initialisation.
@@ -939,6 +965,7 @@ void wine_init( int argc, char *argv[], char *error, int error_size )
#ifdef RLIMIT_AS
set_max_limit( RLIMIT_AS );
#endif
+ set_rttime_limit();
wine_init_argv0_path( argv[0] );
build_dll_path();
diff --git a/server/Makefile.in b/server/Makefile.in
index 19a4fac..76bdb96 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -1,4 +1,5 @@
EXTRALIBS = $(POLL_LIBS) $(RT_LIBS)
+EXTRAINCL = $(DBUS_CFLAGS)
C_SRCS = \
async.c \
@@ -30,6 +31,7 @@ C_SRCS = \
region.c \
registry.c \
request.c \
+ rtkit.c \
semaphore.c \
serial.c \
signal.c \
diff --git a/server/rtkit.c b/server/rtkit.c
new file mode 100644
index 0000000..a212106
--- /dev/null
+++ b/server/rtkit.c
@@ -0,0 +1,180 @@
+/*
+ * Rtkit dbus calls
+ * Copyright 2010 Maarten Lankhorst for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include "wine/port.h"
+#include "wine/library.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_SCHED_H
+#include <sys/sched.h>
+#endif
+#include <sys/resource.h>
+
+#if defined(HAVE_SETRLIMIT) && defined(__linux__) && defined(SONAME_LIBDBUS_1) && defined(HAVE_SCHED_H)
+
+#include <sched.h>
+#include <string.h>
+#include <unistd.h>
+#include <dbus/dbus.h>
+#include <stdio.h>
+#include "object.h"
+
+#ifndef RLIMIT_RTTIME
+#define RLIMIT_RTTIME 15
+#endif
+
+#define FUNCPTR(fn) static typeof(fn) *p ##fn
+
+FUNCPTR(dbus_error_init);
+FUNCPTR(dbus_error_free);
+FUNCPTR(dbus_bus_get);
+FUNCPTR(dbus_message_new_method_call);
+FUNCPTR(dbus_message_append_args);
+FUNCPTR(dbus_connection_send_with_reply_and_block);
+FUNCPTR(dbus_message_unref);
+FUNCPTR(dbus_set_error_from_message);
+#undef FUNCPTR
+
+static int translate_error( unsigned tid, const char *name )
+{
+ if (!strcmp( name, DBUS_ERROR_NO_MEMORY ))
+ return -ENOMEM;
+ if (!strcmp( name, DBUS_ERROR_SERVICE_UNKNOWN ) ||
+ !strcmp( name, DBUS_ERROR_NAME_HAS_NO_OWNER ))
+ return -ENOENT;
+ if (!strcmp( name, DBUS_ERROR_ACCESS_DENIED ) ||
+ !strcmp( name, DBUS_ERROR_AUTH_FAILED ))
+ return -EACCES;
+
+ if (debug_level)
+ fprintf( stderr, "%04x: Could not map error \"%s\"\n", tid, name );
+ return -EIO;
+}
+
+static void init_dbus(void)
+{
+#define FUNCPTR(fn) p ##fn = wine_dlsym( libdbus, #fn, NULL, 0 );
+ char error[512];
+ void *libdbus = wine_dlopen( SONAME_LIBDBUS_1, RTLD_NOW, error, sizeof( error ) );
+ FUNCPTR(dbus_error_init);
+ FUNCPTR(dbus_error_free);
+ FUNCPTR(dbus_bus_get);
+ FUNCPTR(dbus_message_new_method_call);
+ FUNCPTR(dbus_message_append_args);
+ FUNCPTR(dbus_connection_send_with_reply_and_block);
+ FUNCPTR(dbus_message_unref);
+ FUNCPTR(dbus_set_error_from_message);
+#undef FUNCPTR
+}
+
+static DBusConnection *get_dbus(void)
+{
+ static DBusConnection *bus;
+ DBusError error;
+
+ if (bus)
+ return bus;
+ init_dbus();
+ pdbus_error_init( &error );
+
+ bus = pdbus_bus_get( DBUS_BUS_SYSTEM, &error );
+ return bus;
+}
+
+int rtkit_make_realtime( pid_t process, pid_t thread, int priority )
+{
+ DBusConnection *bus;
+ DBusMessage *m = NULL, *r = NULL;
+ dbus_uint64_t pid = process;
+ dbus_uint64_t tid = thread;
+ dbus_uint32_t rtprio = priority;
+ DBusError error;
+ int ret;
+
+ bus = get_dbus();
+ if (!bus)
+ return -ENOTSUP;
+
+ pdbus_error_init( &error );
+ m = pdbus_message_new_method_call( "org.freedesktop.RealtimeKit1",
+ "/org/freedesktop/RealtimeKit1",
+ "org.freedesktop.RealtimeKit1",
+ "MakeThreadRealtimeWithPID" );
+ if (!m)
+ {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = pdbus_message_append_args( m, DBUS_TYPE_UINT64, &pid,
+ DBUS_TYPE_UINT64, &tid,
+ DBUS_TYPE_UINT32, &rtprio,
+ DBUS_TYPE_INVALID );
+ if (!ret)
+ {
+ ret = -ENOMEM;
+ goto out;
+ }
+ r = pdbus_connection_send_with_reply_and_block( bus, m, -1, &error );
+ if (!r)
+ {
+ ret = translate_error( tid, error.name );
+ goto out;
+ }
+ if (pdbus_set_error_from_message( &error, r ))
+ ret = translate_error( tid, error.name );
+ else
+ ret = 0;
+out:
+ if (m)
+ pdbus_message_unref( m );
+ if (r)
+ pdbus_message_unref( r );
+ pdbus_error_free( &error );
+ if (debug_level)
+ fprintf( stderr, "%04x: Setting realtime priority of %u returns %i\n", (int)tid, rtprio, ret );
+ return ret;
+}
+
+int rtkit_undo_realtime( pid_t thread )
+{
+ struct sched_param parm;
+ int ret;
+ memset( &parm, 0, sizeof( parm ) );
+ ret = sched_setscheduler( thread, SCHED_OTHER, &parm );
+ if (ret < 0)
+ return -errno;
+ return ret;
+}
+
+#else
+
+int rtkit_make_realtime( pid_t process, pid_t thread, int priority )
+{
+ return -ENOTSUP;
+}
+
+int rtkit_undo_realtime( pid_t thread )
+{
+ return -ENOTSUP;
+}
+
+#endif
diff --git a/server/thread.c b/server/thread.c
index 906b79d..892c51a 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -52,6 +52,8 @@
#include "user.h"
#include "security.h"
+extern int rtkit_make_realtime(pid_t process, pid_t thread, int priority);
+extern int rtkit_undo_realtime(pid_t thread);
#ifdef __i386__
static const unsigned int supported_cpus = CPU_FLAG(CPU_x86);
@@ -482,7 +484,17 @@ static void set_thread_info( struct thread *thread,
if ((req->priority >= min && req->priority <= max) ||
req->priority == THREAD_PRIORITY_IDLE ||
req->priority == THREAD_PRIORITY_TIME_CRITICAL)
+ {
+ if (thread->unix_tid == -1)
+ {}
+ else if (thread->priority == THREAD_PRIORITY_TIME_CRITICAL &&
+ req->priority != THREAD_PRIORITY_TIME_CRITICAL)
+ rtkit_undo_realtime(thread->unix_tid);
+ else if (thread->priority != THREAD_PRIORITY_TIME_CRITICAL &&
+ req->priority == THREAD_PRIORITY_TIME_CRITICAL)
+ rtkit_make_realtime(thread->unix_pid, thread->unix_tid, 1);
thread->priority = req->priority;
+ }
else
set_error( STATUS_INVALID_PARAMETER );
}
@@ -1309,6 +1321,10 @@ DECL_HANDLER(init_thread)
}
debug_level = max( debug_level, req->debug_level );
+ /* Raced with SetThreadPriority */
+ if (current->priority == THREAD_PRIORITY_TIME_CRITICAL)
+ rtkit_make_realtime(current->unix_pid, current->unix_tid, 1);
+
reply->pid = get_process_id( process );
reply->tid = get_thread_id( current );
reply->version = SERVER_PROTOCOL_VERSION;
--
2.3.5
From ac23f694dc12355fcea974224f6ac18b6b3d2cbd Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:29 +0100
Subject: avrt: Add realtime to stub
---
dlls/avrt/main.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/dlls/avrt/main.c b/dlls/avrt/main.c
index 8a11025..aa6b95d 100644
--- a/dlls/avrt/main.c
+++ b/dlls/avrt/main.c
@@ -80,6 +80,7 @@ HANDLE WINAPI AvSetMmThreadCharacteristicsW(LPCWSTR TaskName, LPDWORD TaskIndex)
SetLastError(ERROR_INVALID_HANDLE);
return NULL;
}
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
return (HANDLE)0x12345678;
}
--
2.3.5
From 7047fea9ba6ebf3c5533ebc3ba57115ea93b353f Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:29 +0100
Subject: server: Bump priority on server to process messages faster
---
server/main.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/server/main.c b/server/main.c
index 7aed338..6c257b4 100644
--- a/server/main.c
+++ b/server/main.c
@@ -28,6 +28,7 @@
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
+#include <sys/syscall.h>
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
@@ -38,6 +39,8 @@
#include "request.h"
#include "wine/library.h"
+extern int rtkit_make_realtime(pid_t process, pid_t thread, int priority);
+
/* command-line options */
int debug_level = 0;
int foreground = 0;
@@ -145,6 +148,7 @@ int main( int argc, char *argv[] )
init_signals();
init_directories();
init_registry();
+ rtkit_make_realtime(getpid(), syscall( SYS_gettid ), 2);
main_loop();
return 0;
}
--
2.3.5
From 86dff38ff2ae49313c61fdc51bf7eb77da1c8bef Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:29 +0100
Subject: rtkit: add SIGXCPU handling to wineserver
This is dependent on getting the correct server_pid in the previous commit.
Processes now forward SIGXCPU to wineserver, who will attempt to downgrade
all threads that were set to realtime priority by avrt first, and if another
SIGXCPU is received or none were found it will downgrade all realtime threads.
Special-thanks-to: tizbac
---
dlls/avrt/main.c | 2 +-
dlls/ntdll/server.c | 94 +++++++++++++++++++++++++++++++++-
libs/wine/loader.c | 10 ++--
server/main.c | 4 +-
server/rtkit.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++------
server/thread.c | 40 +++++++++++----
server/thread.h | 2 +
7 files changed, 261 insertions(+), 35 deletions(-)
diff --git a/dlls/avrt/main.c b/dlls/avrt/main.c
index aa6b95d..4e29abf 100644
--- a/dlls/avrt/main.c
+++ b/dlls/avrt/main.c
@@ -80,7 +80,7 @@ HANDLE WINAPI AvSetMmThreadCharacteristicsW(LPCWSTR TaskName, LPDWORD TaskIndex)
SetLastError(ERROR_INVALID_HANDLE);
return NULL;
}
- SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL-1);
return (HANDLE)0x12345678;
}
diff --git a/dlls/ntdll/server.c b/dlls/ntdll/server.c
index 69d01be..07beefa 100644
--- a/dlls/ntdll/server.c
+++ b/dlls/ntdll/server.c
@@ -81,6 +81,9 @@
#include "ntdll_misc.h"
WINE_DEFAULT_DEBUG_CHANNEL(server);
+WINE_DECLARE_DEBUG_CHANNEL(winediag);
+WINE_DECLARE_DEBUG_CHANNEL(tid);
+WINE_DECLARE_DEBUG_CHANNEL(timestamp);
/* Some versions of glibc don't define this */
#ifndef SCM_RIGHTS
@@ -1330,6 +1333,88 @@ static int get_unix_tid(void)
}
+#ifdef SIGXCPU
+static int convert_tidtostr_r(char *s, unsigned hex)
+{
+ int i;
+ char *start = s;
+
+ for (i = 0; i < 8; ++i) {
+ unsigned c = hex >> (28 - 4 * i);
+
+ if (c || i >= 4)
+ { /* last 4 digits always printed */
+ c &= 0xf;
+
+ if (c < 10)
+ *s++ = '0' + c;
+ else
+ *s++ = 'a' + c - 10;
+ }
+ }
+
+ *s++ = ':';
+ return s - start;
+}
+
+static int convert_stamptostr_r(char *s, unsigned stamp)
+{
+ unsigned high = stamp / 1000, low = stamp % 1000, i;
+ char *start = s;
+
+ for (i = 1000000; i; i /= 10) {
+ if (high >= i)
+ *s++ = '0' + (high / i) % 10;
+ else if (i <= 100)
+ *s++ = ' ';
+ }
+
+ *s++ = '.';
+
+ for (i = 100; i; i /= 10)
+ *s++ = '0' + (low / i) % 10;
+
+ *s++ = ':';
+
+ return s - start;
+}
+
+
+static const char throttle_str[] =
+"fixme:winediag:sigxcpu_handler realtime priority was throttled due to program exceeding time limit\n";
+
+static void sigxcpu_handler( int sig )
+{
+ char temp[16];
+ int old_errno = errno, ret;
+
+ if (server_pid > 0)
+ kill(server_pid, SIGXCPU);
+ else {
+ /* uh oh, somehow init failed to get server_pid */
+ struct sched_param parm;
+ memset(&parm, 0, sizeof(parm));
+ sched_setscheduler(0, SCHED_OTHER | SCHED_RESET_ON_FORK, &parm);
+ }
+
+ if (FIXME_ON(winediag)) {
+ if (TRACE_ON(timestamp)) {
+ ret = convert_stamptostr_r(temp, NtGetTickCount());
+ write(2, temp, ret);
+ }
+
+ if (TRACE_ON(tid)) {
+ ret = convert_tidtostr_r(temp, GetCurrentThreadId());
+ write(2, temp, ret);
+ }
+
+ write(2, throttle_str, sizeof(throttle_str)-1);
+ }
+
+ errno = old_errno;
+}
+#endif
+
/***********************************************************************
* server_init_process
*
@@ -1339,6 +1424,14 @@ void server_init_process(void)
{
obj_handle_t version;
const char *env_socket = getenv( "WINESERVERSOCKET" );
+#ifdef SIGXCPU
+ struct sigaction sa;
+
+ sa.sa_handler = sigxcpu_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction( SIGXCPU, &sa, NULL );
+#endif
server_pid = -1;
if (env_socket)
@@ -1422,7 +1515,6 @@ NTSTATUS server_init_process_done(void)
return status;
}
-
/***********************************************************************
* server_init_thread
*
diff --git a/libs/wine/loader.c b/libs/wine/loader.c
index 600d99b..7261522 100644
--- a/libs/wine/loader.c
+++ b/libs/wine/loader.c
@@ -935,10 +935,12 @@ static void set_rttime_limit(void)
if (!getrlimit( RLIMIT_RTTIME, &rlimit ))
{
- /* 50 ms realtime, then 10 ms to undo realtime */
- if (rlimit.rlim_max > 150000000ULL)
- rlimit.rlim_max = 150000000ULL;
- rlimit.rlim_cur = rlimit.rlim_max - 10000000ULL;
+ /* 1000 ms maximum realtime before the first SIGXCPU, this will drop
+ * all realtime threads to normal priority.
+ */
+ if (rlimit.rlim_max > 5000000)
+ rlimit.rlim_max = 5000000;
+ rlimit.rlim_cur = 1000000;
setrlimit( RLIMIT_RTTIME, &rlimit );
}
diff --git a/server/main.c b/server/main.c
index 6c257b4..f78c0fb 100644
--- a/server/main.c
+++ b/server/main.c
@@ -39,7 +39,7 @@
#include "request.h"
#include "wine/library.h"
-extern int rtkit_make_realtime(pid_t process, pid_t thread, int priority);
+extern int rtkit_make_realtime(struct thread *thread, pid_t tid, int priority);
/* command-line options */
int debug_level = 0;
@@ -148,7 +148,7 @@ int main( int argc, char *argv[] )
init_signals();
init_directories();
init_registry();
- rtkit_make_realtime(getpid(), syscall( SYS_gettid ), 2);
+ rtkit_make_realtime(NULL, syscall( SYS_gettid ), 3);
main_loop();
return 0;
}
diff --git a/server/rtkit.c b/server/rtkit.c
index a212106..53e5ce4 100644
--- a/server/rtkit.c
+++ b/server/rtkit.c
@@ -35,7 +35,11 @@
#include <unistd.h>
#include <dbus/dbus.h>
#include <stdio.h>
+#include <signal.h>
+#include <limits.h>
+#include <syscall.h>
#include "object.h"
+#include "thread.h"
#ifndef RLIMIT_RTTIME
#define RLIMIT_RTTIME 15
@@ -53,6 +57,8 @@ FUNCPTR(dbus_message_unref);
FUNCPTR(dbus_set_error_from_message);
#undef FUNCPTR
+static struct list rt_thread_list = LIST_INIT(rt_thread_list);
+
static int translate_error( unsigned tid, const char *name )
{
if (!strcmp( name, DBUS_ERROR_NO_MEMORY ))
@@ -85,6 +91,88 @@ static void init_dbus(void)
#undef FUNCPTR
}
+#define MSG_SIGXCPU "wineserver: SIGXCPU called on wineserver from kernel, realtime priority removed!\n"
+
+static int sched_normal(struct thread *cur)
+{
+ int ret = 0;
+
+ if (cur->unix_tid != -1) {
+ struct sched_param parm;
+ memset( &parm, 0, sizeof( parm ) );
+ ret = sched_setscheduler(cur->unix_tid, SCHED_OTHER | SCHED_RESET_ON_FORK, &parm);
+ if (ret < 0)
+ ret = -errno;
+ }
+
+ list_remove(&cur->rt_entry);
+ list_init(&cur->rt_entry);
+ cur->rt_prio = 0;
+ cur->priority = 0;
+ return ret;
+}
+
+static void sigxcpu_handler(int sig, siginfo_t *si, void *ucontext)
+{
+ struct thread *cur, *tmp;
+ int found = 0;
+ int old_errno = errno;
+
+ if (si->si_code & SI_KERNEL) {
+ struct sched_param parm;
+ memset( &parm, 0, sizeof( parm ) );
+
+ sched_setscheduler(syscall( SYS_gettid ), SCHED_OTHER | SCHED_RESET_ON_FORK, &parm);
+
+ write(2, MSG_SIGXCPU, sizeof(MSG_SIGXCPU)-1);
+ goto restore_errno;
+ }
+
+ LIST_FOR_EACH_ENTRY_SAFE(cur, tmp, &rt_thread_list, struct thread, rt_entry)
+ {
+ if (si->si_pid == cur->unix_pid && cur->rt_prio == 1) {
+ found = 1;
+ sched_normal(cur);
+ }
+ }
+
+ if (!found) {
+ LIST_FOR_EACH_ENTRY_SAFE(cur, tmp, &rt_thread_list, struct thread, rt_entry)
+ {
+ if (si->si_pid == cur->unix_pid)
+ sched_normal(cur);
+ }
+ }
+
+restore_errno:
+ errno = old_errno;
+}
+
+static void setup_rt(void)
+{
+ struct sigaction sa;
+ struct rlimit rlimit;
+
+ sa.sa_sigaction = sigxcpu_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_SIGINFO;
+ sigaction(SIGXCPU, &sa, NULL);
+
+ if (!getrlimit( RLIMIT_RTTIME, &rlimit ))
+ {
+ /* wineserver can run for 1.5 seconds continuously at realtime before
+ * it gets throttled down. At this point we probably hit a bug
+ * somewhere.
+ */
+ if (rlimit.rlim_max > 2000000)
+ rlimit.rlim_max = 2000000;
+ if (rlimit.rlim_cur > 1500000)
+ rlimit.rlim_cur = 1500000;
+
+ setrlimit( RLIMIT_RTTIME, &rlimit );
+ }
+}
+
static DBusConnection *get_dbus(void)
{
static DBusConnection *bus;
@@ -96,16 +184,18 @@ static DBusConnection *get_dbus(void)
pdbus_error_init( &error );
bus = pdbus_bus_get( DBUS_BUS_SYSTEM, &error );
+ setup_rt();
return bus;
}
-int rtkit_make_realtime( pid_t process, pid_t thread, int priority )
+int rtkit_make_realtime( struct thread *thread, pid_t unix_tid, int priority )
{
DBusConnection *bus;
DBusMessage *m = NULL, *r = NULL;
- dbus_uint64_t pid = process;
- dbus_uint64_t tid = thread;
+ dbus_uint64_t pid = thread ? thread->unix_pid : getpid();
+ dbus_uint64_t tid = unix_tid;
dbus_uint32_t rtprio = priority;
+ sigset_t sigset;
DBusError error;
int ret;
@@ -133,16 +223,29 @@ int rtkit_make_realtime( pid_t process, pid_t thread, int priority )
ret = -ENOMEM;
goto out;
}
+
+ sigemptyset( &sigset );
+ sigaddset( &sigset, SIGXCPU );
+ sigprocmask( SIG_BLOCK, &sigset, NULL );
+
r = pdbus_connection_send_with_reply_and_block( bus, m, -1, &error );
if (!r)
{
ret = translate_error( tid, error.name );
- goto out;
+ goto out_unblock;
}
if (pdbus_set_error_from_message( &error, r ))
ret = translate_error( tid, error.name );
- else
+ else {
ret = 0;
+ if (thread) {
+ if (list_empty(&thread->rt_entry))
+ list_add_tail( &rt_thread_list, &thread->rt_entry );
+ thread->rt_prio = rtprio;
+ }
+ }
+out_unblock:
+ sigprocmask( SIG_UNBLOCK, &sigset, NULL );
out:
if (m)
pdbus_message_unref( m );
@@ -150,29 +253,38 @@ out:
pdbus_message_unref( r );
pdbus_error_free( &error );
if (debug_level)
- fprintf( stderr, "%04x: Setting realtime priority of %u returns %i\n", (int)tid, rtprio, ret );
+ fprintf( stderr, "%04x: Setting realtime priority of %u returns %i %m\n", (int)tid, rtprio, ret );
return ret;
}
-int rtkit_undo_realtime( pid_t thread )
+int rtkit_undo_realtime( struct thread *thread )
{
- struct sched_param parm;
- int ret;
- memset( &parm, 0, sizeof( parm ) );
- ret = sched_setscheduler( thread, SCHED_OTHER, &parm );
- if (ret < 0)
- return -errno;
- return ret;
+ sigset_t sigset;
+ int ret = 0;
+
+ sigemptyset( &sigset );
+ sigaddset( &sigset, SIGXCPU );
+ sigprocmask( SIG_BLOCK, &sigset, NULL );
+
+ if (!list_empty(&thread->rt_entry))
+ ret = sched_normal(thread);
+
+ if (debug_level)
+ fprintf( stderr, "%04x: Removing realtime priority of %u returns %i %m\n",
+ (int)thread->unix_tid, thread->rt_prio, ret );
+
+ sigprocmask( SIG_UNBLOCK, &sigset, NULL );
+ return ret < 0 ? -errno : 0;
}
#else
-int rtkit_make_realtime( pid_t process, pid_t thread, int priority )
+int rtkit_make_realtime( struct thread *thread, pid_t unix_tid, int priority )
{
return -ENOTSUP;
}
-int rtkit_undo_realtime( pid_t thread )
+int rtkit_undo_realtime( struct thread *thread )
{
return -ENOTSUP;
}
diff --git a/server/thread.c b/server/thread.c
index 892c51a..9f18f15 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -52,8 +52,8 @@
#include "user.h"
#include "security.h"
-extern int rtkit_make_realtime(pid_t process, pid_t thread, int priority);
-extern int rtkit_undo_realtime(pid_t thread);
+extern int rtkit_make_realtime(struct thread *thread, pid_t tid, int priority);
+extern int rtkit_undo_realtime(struct thread *thread);
#ifdef __i386__
static const unsigned int supported_cpus = CPU_FLAG(CPU_x86);
@@ -203,6 +203,8 @@ static inline void init_thread_structure( struct thread *thread )
list_init( &thread->mutex_list );
list_init( &thread->system_apc );
list_init( &thread->user_apc );
+ list_init( &thread->rt_entry );
+ thread->rt_prio = 0;
for (i = 0; i < MAX_INFLIGHT_FDS; i++)
thread->inflight[i].server = thread->inflight[i].client = -1;
@@ -277,6 +279,9 @@ static void cleanup_thread( struct thread *thread )
{
int i;
+ thread->unix_tid = -1;
+ if (!list_empty(&thread->rt_entry))
+ rtkit_undo_realtime(thread);
clear_apc_queue( &thread->system_apc );
clear_apc_queue( &thread->user_apc );
free( thread->req_data );
@@ -468,6 +473,15 @@ affinity_t get_thread_affinity( struct thread *thread )
#define THREAD_PRIORITY_REALTIME_HIGHEST 6
#define THREAD_PRIORITY_REALTIME_LOWEST -7
+static int rtprio(int ntprio)
+{
+ if (ntprio == THREAD_PRIORITY_TIME_CRITICAL - 1)
+ return 1;
+ else if (ntprio == THREAD_PRIORITY_TIME_CRITICAL)
+ return 2;
+ return 0;
+}
+
/* set all information about a thread */
static void set_thread_info( struct thread *thread,
const struct set_thread_info_request *req )
@@ -483,17 +497,21 @@ static void set_thread_info( struct thread *thread,
}
if ((req->priority >= min && req->priority <= max) ||
req->priority == THREAD_PRIORITY_IDLE ||
+ req->priority == THREAD_PRIORITY_TIME_CRITICAL - 1 ||
req->priority == THREAD_PRIORITY_TIME_CRITICAL)
{
+ int newprio = rtprio(req->priority);
if (thread->unix_tid == -1)
- {}
- else if (thread->priority == THREAD_PRIORITY_TIME_CRITICAL &&
- req->priority != THREAD_PRIORITY_TIME_CRITICAL)
- rtkit_undo_realtime(thread->unix_tid);
- else if (thread->priority != THREAD_PRIORITY_TIME_CRITICAL &&
- req->priority == THREAD_PRIORITY_TIME_CRITICAL)
- rtkit_make_realtime(thread->unix_pid, thread->unix_tid, 1);
- thread->priority = req->priority;
+ thread->rt_prio = newprio;
+ else if (thread->priority == THREAD_PRIORITY_TIME_CRITICAL && !newprio)
+ rtkit_undo_realtime(thread);
+ else if (thread->rt_prio != newprio)
+ rtkit_make_realtime(thread, thread->unix_tid, newprio);
+
+ if (newprio)
+ thread->priority = THREAD_PRIORITY_TIME_CRITICAL;
+ else
+ thread->priority = req->priority;
}
else
set_error( STATUS_INVALID_PARAMETER );
@@ -1323,7 +1341,7 @@ DECL_HANDLER(init_thread)
/* Raced with SetThreadPriority */
if (current->priority == THREAD_PRIORITY_TIME_CRITICAL)
- rtkit_make_realtime(current->unix_pid, current->unix_tid, 1);
+ rtkit_make_realtime(current, current->unix_tid, current->rt_prio);
reply->pid = get_process_id( process );
reply->tid = get_thread_id( current );
diff --git a/server/thread.h b/server/thread.h
index 996d95b..36f09ce 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -87,6 +87,8 @@ struct thread
timeout_t creation_time; /* Thread creation time */
timeout_t exit_time; /* Thread exit time */
struct token *token; /* security token associated with this thread */
+ struct list rt_entry; /* entry for member in linked realtime list */
+ int rt_prio; /* current realtime thread priority */
};
struct thread_snapshot
--
2.3.5
From ffc3fe6117be529dcea6ecb97f252b0fbfe7f963 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:29 +0100
Subject: mmdevapi: be stricter about tests
Dont care about broken drivers..
---
dlls/mmdevapi/tests/capture.c | 8 ++---
dlls/mmdevapi/tests/render.c | 77 +++++++++++++++++++------------------------
2 files changed, 37 insertions(+), 48 deletions(-)
diff --git a/dlls/mmdevapi/tests/capture.c b/dlls/mmdevapi/tests/capture.c
index 702843c..8f0d626 100644
--- a/dlls/mmdevapi/tests/capture.c
+++ b/dlls/mmdevapi/tests/capture.c
@@ -241,7 +241,7 @@ static void test_capture(IAudioClient *ac, HANDLE handle, WAVEFORMATEX *wfx)
ok(hr == S_OK, "Valid IAudioCaptureClient_GetBuffer returns %08x\n", hr);
ok(frames2 == frames, "GetBuffer after ReleaseBuffer(0) %u/%u\n", frames2, frames);
ok(pos2 == pos, "Position after ReleaseBuffer(0) %u/%u\n", (UINT)pos2, (UINT)pos);
- todo_wine ok(qpc2 == qpc, "HPC after ReleaseBuffer(0) %u vs. %u\n", (UINT)qpc2, (UINT)qpc);
+ ok(qpc2 == qpc, "HPC after ReleaseBuffer(0) %u vs. %u\n", (UINT)qpc2, (UINT)qpc);
}
/* trace after the GCP test because log output to MS-DOS console disturbs timing */
@@ -304,13 +304,13 @@ static void test_capture(IAudioClient *ac, HANDLE handle, WAVEFORMATEX *wfx)
if(hr == S_OK){
/* The discontinuity is reported here, but is this an old or new packet? */
- todo_wine ok(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY, "expect DISCONTINUITY %x\n", flags);
+ ok(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY, "expect DISCONTINUITY %x\n", flags);
ok(pad == next, "GCP %u vs. BufferSize %u\n", (UINT32)pad, next);
/* Native's position is one period further than what we read.
* Perhaps that's precisely the meaning of DATA_DISCONTINUITY:
* signal when the position jump left a gap. */
- todo_wine ok(pos == sum + frames, "Position %u gap %d\n",
+ ok(pos == sum + frames, "Position %u gap %d\n",
(UINT)pos, (UINT)pos - sum);
if(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY)
sum = pos;
@@ -388,7 +388,7 @@ static void test_capture(IAudioClient *ac, HANDLE handle, WAVEFORMATEX *wfx)
/* Only PulseAudio goes here; despite snd_pcm_drop it manages
* to fill GetBufferSize with a single snd_pcm_read */
trace("Test marked todo: only PulseAudio gets here\n");
- todo_wine ok(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY, "expect DISCONTINUITY %x\n", flags);
+ ok(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY, "expect DISCONTINUITY %x\n", flags);
/* Reset zeroes padding, not the position */
ok(pos >= sum, "Position %u last %u\n", (UINT)pos, sum);
/*sum = pos; check after next GetBuffer */
diff --git a/dlls/mmdevapi/tests/render.c b/dlls/mmdevapi/tests/render.c
index 3a48c8d..a6f0c09 100644
--- a/dlls/mmdevapi/tests/render.c
+++ b/dlls/mmdevapi/tests/render.c
@@ -983,7 +983,7 @@ static void test_clock(int share)
ok(hr == S_OK, "GetBuffer failed: %08x\n", hr);
trace("data at %p\n", data);
- hr = IAudioRenderClient_ReleaseBuffer(arc, avail, winetest_debug>2 ?
+ hr = IAudioRenderClient_ReleaseBuffer(arc, avail, winetest_interactive ?
wave_generate_tone(pwfx, data, avail) : AUDCLNT_BUFFERFLAGS_SILENT);
ok(hr == S_OK, "ReleaseBuffer failed: %08x\n", hr);
if(hr == S_OK) sum += avail;
@@ -1020,8 +1020,7 @@ static void test_clock(int share)
ok(hr == S_OK, "GetPosition failed: %08x\n", hr);
ok(pos >= last, "Position %u vs. last %u\n", (UINT)pos,(UINT)last);
last = pos;
- if(/*share &&*/ winetest_debug>1)
- ok(pos*1000/freq <= slept*1.1, "Position %u too far after stop %ums\n", (UINT)pos, slept);
+ ok(pos*1000/freq <= slept*1.1, "Position %u too far after stop %ums\n", (UINT)pos, slept);
hr = IAudioClient_Start(ac); /* #2 */
ok(hr == S_OK, "Start failed: %08x\n", hr);
@@ -1054,12 +1053,11 @@ static void test_clock(int share)
ok(pos * pwfx->nSamplesPerSec <= sum * freq, "Position %u > written %u\n", (UINT)pos, sum);
/* Prove that Stop must not drop frames (in shared mode). */
ok(pad ? pos > last : pos >= last, "Position %u vs. last %u\n", (UINT)pos,(UINT)last);
- if (share && pad > 0 && winetest_debug>1)
+ if (share && pad > 0)
ok(pos*1000/freq <= slept*1.1, "Position %u too far after playing %ums\n", (UINT)pos, slept);
/* in exclusive mode, testbot's w7 machines yield pos > sum-pad */
- if(/*share &&*/ winetest_debug>1)
- ok(pos * pwfx->nSamplesPerSec == (sum-pad) * freq,
- "Position %u after stop vs. %u padding\n", (UINT)pos, pad);
+ ok(pos * pwfx->nSamplesPerSec == (sum-pad) * freq,
+ "Position %u after stop vs. %u padding\n", (UINT)pos, pad);
last = pos;
Sleep(100);
@@ -1086,7 +1084,7 @@ static void test_clock(int share)
ok(hr == S_OK, "GetBuffer failed: %08x\n", hr);
trace("data at %p\n", data);
- hr = IAudioRenderClient_ReleaseBuffer(arc, avail, winetest_debug>2 ?
+ hr = IAudioRenderClient_ReleaseBuffer(arc, avail, winetest_interactive ?
wave_generate_tone(pwfx, data, avail) : AUDCLNT_BUFFERFLAGS_SILENT);
ok(hr == S_OK, "ReleaseBuffer failed: %08x\n", hr);
if(hr == S_OK) sum += avail;
@@ -1111,10 +1109,7 @@ static void test_clock(int share)
trace("position %u past %ums sleep #3\n", (UINT)pos, slept);
ok(pos > last, "Position %u vs. last %u\n", (UINT)pos,(UINT)last);
ok(pos * pwfx->nSamplesPerSec <= sum * freq, "Position %u > written %u\n", (UINT)pos, sum);
- if (winetest_debug>1)
- ok(pos*1000/freq <= slept*1.1, "Position %u too far after playing %ums\n", (UINT)pos, slept);
- else
- skip("Rerun with WINETEST_DEBUG=2 for GetPosition tests.\n");
+ ok(pos*1000/freq <= slept*1.1, "Position %u too far after playing %ums\n", (UINT)pos, slept);
last = pos;
hr = IAudioClient_Reset(ac);
@@ -1132,11 +1127,10 @@ static void test_clock(int share)
ok(pos >= last, "Position %u vs. last %u\n", (UINT)pos,(UINT)last);
ok(pcpos > pcpos0, "pcpos should increase\n");
ok(pos * pwfx->nSamplesPerSec <= sum * freq, "Position %u > written %u\n", (UINT)pos, sum);
- if (pad > 0 && winetest_debug>1)
+ if (pad > 0)
ok(pos*1000/freq <= slept*1.1, "Position %u too far after stop %ums\n", (UINT)pos, slept);
- if(winetest_debug>1)
- ok(pos * pwfx->nSamplesPerSec == (sum-pad) * freq,
- "Position %u after stop vs. %u padding\n", (UINT)pos, pad);
+ ok(pos * pwfx->nSamplesPerSec == (sum-pad) * freq,
+ "Position %u after stop vs. %u padding\n", (UINT)pos, pad);
last = pos;
/* Begin the big loop */
@@ -1159,19 +1153,17 @@ static void test_clock(int share)
ok(hr == S_OK, "GetBuffer failed: %08x\n", hr);
trace("data at %p for prefill %u\n", data, avail);
- if (winetest_debug>2) {
- hr = IAudioClient_Stop(ac);
- ok(hr == S_OK, "Stop failed: %08x\n", hr);
+ hr = IAudioClient_Stop(ac);
+ ok(hr == S_OK, "Stop failed: %08x\n", hr);
- Sleep(20);
- slept += 20;
+ Sleep(20);
+ slept += 20;
- hr = IAudioClient_Reset(ac);
- ok(hr == AUDCLNT_E_BUFFER_OPERATION_PENDING, "Reset failed: %08x\n", hr);
+ hr = IAudioClient_Reset(ac);
+ ok(hr == AUDCLNT_E_BUFFER_OPERATION_PENDING, "Reset failed: %08x\n", hr);
- hr = IAudioClient_Start(ac);
- ok(hr == S_OK, "Start failed: %08x\n", hr);
- }
+ hr = IAudioClient_Start(ac);
+ ok(hr == S_OK, "Start failed: %08x\n", hr);
/* Despite passed time, data must still point to valid memory... */
hr = IAudioRenderClient_ReleaseBuffer(arc, avail,
@@ -1216,14 +1208,13 @@ static void test_clock(int share)
trace("padding %u position %u/%u slept %ums iteration %d\n", pad, (UINT)pos, sum-pad, slept, i);
ok(pad ? pos > last : pos >= last, "No position increase at iteration %d\n", i);
ok(pos * pwfx->nSamplesPerSec <= sum * freq, "Position %u > written %u\n", (UINT)pos, sum);
- if (winetest_debug>1) {
- /* Padding does not lag behind by much */
- ok(pos * pwfx->nSamplesPerSec <= (sum-pad+fragment) * freq, "Position %u > written %u\n", (UINT)pos, sum);
- ok(pos*1000/freq <= slept*1.1, "Position %u too far after %ums\n", (UINT)pos, slept);
- if (pad) /* not in case of underrun */
- ok((pos-last)*1000/freq >= 90 && 110 >= (pos-last)*1000/freq,
- "Position delta %ld not regular: %ld ms\n", (long)(pos-last), (long)((pos-last)*1000/freq));
- }
+
+ /* Padding does not lag behind by much */
+ ok(pos * pwfx->nSamplesPerSec <= (sum-pad+fragment) * freq, "Position %u > written %u\n", (UINT)pos, sum);
+ ok(pos*1000/freq <= slept*1.1, "Position %u too far after %ums\n", (UINT)pos, slept);
+ if (pad) /* not in case of underrun */
+ ok((pos-last)*1000/freq >= 90 && 110 >= (pos-last)*1000/freq,
+ "Position delta %ld not regular\n", (long)(pos-last));
last = pos;
hr = IAudioClient_GetStreamLatency(ac, &t1);
@@ -2088,7 +2079,7 @@ static void test_worst_case(void)
hr = IAudioClock_GetFrequency(acl, &freq);
ok(hr == S_OK, "GetFrequency failed: %08x\n", hr);
- for(j = 0; j <= (winetest_interactive ? 9 : 2); j++){
+ for(j = 0; j < 10; j++){
sum = 0;
trace("Should play %ums continuous tone with fragment size %u.\n",
(ULONG)(defp/100), fragment);
@@ -2097,15 +2088,13 @@ static void test_worst_case(void)
ok(hr == S_OK, "GetPosition failed: %08x\n", hr);
/* XAudio2 prefills one period, play without it */
- if(winetest_debug>2){
- hr = IAudioRenderClient_GetBuffer(arc, fragment, &data);
- ok(hr == S_OK, "GetBuffer failed: %08x\n", hr);
-
- hr = IAudioRenderClient_ReleaseBuffer(arc, fragment, AUDCLNT_BUFFERFLAGS_SILENT);
- ok(hr == S_OK, "ReleaseBuffer failed: %08x\n", hr);
- if(hr == S_OK)
- sum += fragment;
- }
+ hr = IAudioRenderClient_GetBuffer(arc, fragment, &data);
+ ok(hr == S_OK, "GetBuffer failed: %08x\n", hr);
+
+ hr = IAudioRenderClient_ReleaseBuffer(arc, fragment, AUDCLNT_BUFFERFLAGS_SILENT);
+ ok(hr == S_OK, "ReleaseBuffer failed: %08x\n", hr);
+ if(hr == S_OK)
+ sum += fragment;
hr = IAudioClient_Start(ac);
ok(hr == S_OK, "Start failed: %08x\n", hr);
--
2.3.5
From ee13bf705d736f6e8b6bd2c16c9bf883b5e40ac9 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:29 +0100
Subject: winepulse: Add initial stub for pulseaudio support
---
Just the basic of initialization and pulseaudio mainloop support is
added here.
---
configure | 98 +++++++++++-
configure.ac | 31 +++-
dlls/mmdevapi/main.c | 2 +-
dlls/winepulse.drv/Makefile.in | 7 +
dlls/winepulse.drv/mmdevdrv.c | 290 ++++++++++++++++++++++++++++++++++
dlls/winepulse.drv/winepulse.drv.spec | 5 +
6 files changed, 428 insertions(+), 5 deletions(-)
create mode 100644 dlls/winepulse.drv/Makefile.in
create mode 100644 dlls/winepulse.drv/mmdevdrv.c
create mode 100644 dlls/winepulse.drv/winepulse.drv.spec
diff --git a/configure b/configure
index 5f5e9d5..37ae481 100755
--- a/configure
+++ b/configure
@@ -653,6 +653,8 @@ OSS4_CFLAGS
ALSA_LIBS
GSTREAMER_LIBS
GSTREAMER_CFLAGS
+PULSEINCL
+PULSELIBS
GETTEXTPO_LIBS
Z_LIBS
FREETYPE_LIBS
@@ -827,6 +829,7 @@ with_oss
with_pcap
with_png
with_pthread
+with_pulse
with_sane
with_tiff
with_v4l
@@ -1296,6 +1299,7 @@ enable_winemapi
enable_winemp3_acm
enable_wineoss_drv
enable_wineps_drv
+enable_winepulse_drv
enable_wineqtdecoder
enable_winex11_drv
enable_wing32
@@ -2139,6 +2143,7 @@ Optional Packages:
--without-pcap do not use the Packet Capture library
--without-png do not use PNG
--without-pthread do not use the pthread library
+ --without-pulse do not use PulseAudio sound support
--without-sane do not use SANE (scanner support)
--without-tiff do not use TIFF
--without-v4l do not use v4l1 (v4l support)
@@ -3386,6 +3391,12 @@ if test "${with_pthread+set}" = set; then :
fi
+# Check whether --with-pulse was given.
+if test "${with_pulse+set}" = set; then :
+ withval=$with_pulse;
+fi
+
+
# Check whether --with-sane was given.
if test "${with_sane+set}" = set; then :
withval=$with_sane;
@@ -12372,6 +12383,87 @@ esac
fi
fi
+PULSELIBS=""
+
+PULSEINCL=""
+
+if test "x$with_pulse" != "xno";
+then
+ ac_save_CPPFLAGS="$CPPFLAGS"
+ if test "$PKG_CONFIG" != "false";
+ then
+ ac_pulse_libs="`$PKG_CONFIG --libs libpulse 2>/dev/null`"
+ ac_pulse_cflags="`$PKG_CONFIG --cflags-only-I libpulse 2>/dev/null`"
+
+ CPPFLAGS="$CPPFLAGS $ac_pulse_cflags"
+ for ac_header in pulse/pulseaudio.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "pulse/pulseaudio.h" "ac_cv_header_pulse_pulseaudio_h" "$ac_includes_default"
+if test "x$ac_cv_header_pulse_pulseaudio_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_PULSE_PULSEAUDIO_H 1
+_ACEOF
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pa_stream_is_corked in -lpulse" >&5
+$as_echo_n "checking for pa_stream_is_corked in -lpulse... " >&6; }
+if ${ac_cv_lib_pulse_pa_stream_is_corked+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpulse $ac_pulse_libs $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pa_stream_is_corked ();
+int
+main ()
+{
+return pa_stream_is_corked ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pulse_pa_stream_is_corked=yes
+else
+ ac_cv_lib_pulse_pa_stream_is_corked=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pulse_pa_stream_is_corked" >&5
+$as_echo "$ac_cv_lib_pulse_pa_stream_is_corked" >&6; }
+if test "x$ac_cv_lib_pulse_pa_stream_is_corked" = xyes; then :
+
+$as_echo "#define HAVE_PULSEAUDIO 1" >>confdefs.h
+
+ PULSELIBS="$ac_pulse_libs"
+ PULSEINCL="$ac_pulse_cflags"
+fi
+
+
+fi
+
+done
+
+ fi
+ CPPFLAGS="$ac_save_CPPFLAGS"
+fi
+if test "$ac_cv_lib_pulse_pa_stream_is_corked" != "yes"; then :
+ case "x$with_pulse" in
+ x) as_fn_append wine_warnings "|libpulse ${notice_platform}development files not found or too old, Pulse won't be supported." ;;
+ xno) ;;
+ *) as_fn_error $? "libpulse ${notice_platform}development files not found or too old, Pulse won't be supported.
+This is an error since --with-pulse was requested." "$LINENO" 5 ;;
+esac
+fi
+
if test "x$with_gstreamer" != "xno"
then
if ${GSTREAMER_CFLAGS:+false} :; then :
@@ -13683,12 +13775,13 @@ fi
test -n "$ALSA_LIBS" || enable_winealsa_drv=${enable_winealsa_drv:-no}
test -n "$COREAUDIO_LIBS" || enable_winecoreaudio_drv=${enable_winecoreaudio_drv:-no}
+test -n "$PULSELIBS" || enable_winepulse_drv=${enable_winepulse_drv:-no}
test "x$ac_cv_member_oss_sysinfo_numaudioengines" = xyes || enable_wineoss_drv=${enable_wineoss_drv:-no}
test "$ac_cv_header_linux_joystick_h" = "yes" -o "$ac_cv_header_IOKit_hid_IOHIDLib_h" = "yes" || enable_winejoystick_drv=${enable_winejoystick_drv:-no}
-if test "x$ALSA_LIBS$COREAUDIO_LIBS" = "x" -a \
+if test "x$ALSA_LIBS$COREAUDIO_LIBS$PULSELIBS" = "x" -a \
"x$ac_cv_member_oss_sysinfo_numaudioengines" != xyes -a \
- "x$with_alsa$with_coreaudio$with_oss" != xnonono
+ "x$with_alsa$with_coreaudio$with_oss$with_pulse" != xnononono
then
as_fn_append wine_warnings "|No sound system was found. Windows applications will be silent."
fi
@@ -17637,6 +17730,7 @@ wine_fn_config_dll winemp3.acm enable_winemp3_acm
wine_fn_config_dll wineoss.drv enable_wineoss_drv
wine_fn_config_dll wineps.drv enable_wineps_drv clean,po
wine_fn_config_dll wineps16.drv16 enable_win16
+wine_fn_config_dll winepulse.drv enable_winepulse_drv
wine_fn_config_dll wineqtdecoder enable_wineqtdecoder
wine_fn_config_dll winex11.drv enable_winex11_drv
wine_fn_config_dll wing.dll16 enable_win16
diff --git a/configure.ac b/configure.ac
index d23227a..76aed78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -72,6 +72,7 @@ AC_ARG_WITH(pcap, AS_HELP_STRING([--without-pcap],[do not use the Packet Ca
AC_ARG_WITH(png, AS_HELP_STRING([--without-png],[do not use PNG]))
AC_ARG_WITH(pthread, AS_HELP_STRING([--without-pthread],[do not use the pthread library]),
[if test "x$withval" = "xno"; then ac_cv_header_pthread_h=no; fi])
+AC_ARG_WITH(pulse, AC_HELP_STRING([--without-pulse],[do not use PulseAudio sound support]))
AC_ARG_WITH(sane, AS_HELP_STRING([--without-sane],[do not use SANE (scanner support)]))
AC_ARG_WITH(tiff, AS_HELP_STRING([--without-tiff],[do not use TIFF]))
AC_ARG_WITH(v4l, AS_HELP_STRING([--without-v4l],[do not use v4l1 (v4l support)]))
@@ -1548,6 +1549,30 @@ then
[GetText ${notice_platform}development files not found (or too old), po files can't be rebuilt.])
fi
+dnl **** Check for PulseAudio ****
+AC_SUBST(PULSELIBS,"")
+AC_SUBST(PULSEINCL,"")
+if test "x$with_pulse" != "xno";
+then
+ ac_save_CPPFLAGS="$CPPFLAGS"
+ if test "$PKG_CONFIG" != "false";
+ then
+ ac_pulse_libs="`$PKG_CONFIG --libs libpulse 2>/dev/null`"
+ ac_pulse_cflags="`$PKG_CONFIG --cflags-only-I libpulse 2>/dev/null`"
+
+ CPPFLAGS="$CPPFLAGS $ac_pulse_cflags"
+ AC_CHECK_HEADERS(pulse/pulseaudio.h,
+ [AC_CHECK_LIB(pulse, pa_stream_is_corked,
+ [AC_DEFINE(HAVE_PULSEAUDIO, 1, [Define if you have pulseaudio])
+ PULSELIBS="$ac_pulse_libs"
+ PULSEINCL="$ac_pulse_cflags"],,$ac_pulse_libs)
+ ])
+ fi
+ CPPFLAGS="$ac_save_CPPFLAGS"
+fi
+WINE_WARNING_WITH(pulse, [test "$ac_cv_lib_pulse_pa_stream_is_corked" != "yes"],
+ [libpulse ${notice_platform}development files not found or too old, Pulse won't be supported.])
+
dnl **** Check for gstreamer ****
if test "x$with_gstreamer" != "xno"
then
@@ -1766,13 +1791,14 @@ fi
dnl **** Disable unsupported winmm drivers ****
test -n "$ALSA_LIBS" || enable_winealsa_drv=${enable_winealsa_drv:-no}
test -n "$COREAUDIO_LIBS" || enable_winecoreaudio_drv=${enable_winecoreaudio_drv:-no}
+test -n "$PULSELIBS" || enable_winepulse_drv=${enable_winepulse_drv:-no}
test "x$ac_cv_member_oss_sysinfo_numaudioengines" = xyes || enable_wineoss_drv=${enable_wineoss_drv:-no}
test "$ac_cv_header_linux_joystick_h" = "yes" -o "$ac_cv_header_IOKit_hid_IOHIDLib_h" = "yes" || enable_winejoystick_drv=${enable_winejoystick_drv:-no}
dnl **** Check for any sound system ****
-if test "x$ALSA_LIBS$COREAUDIO_LIBS" = "x" -a \
+if test "x$ALSA_LIBS$COREAUDIO_LIBS$PULSELIBS" = "x" -a \
"x$ac_cv_member_oss_sysinfo_numaudioengines" != xyes -a \
- "x$with_alsa$with_coreaudio$with_oss" != xnonono
+ "x$with_alsa$with_coreaudio$with_oss$with_pulse" != xnononono
then
WINE_WARNING([No sound system was found. Windows applications will be silent.])
fi
@@ -3339,6 +3365,7 @@ WINE_CONFIG_DLL(winemp3.acm)
WINE_CONFIG_DLL(wineoss.drv)
WINE_CONFIG_DLL(wineps.drv,,[clean,po])
WINE_CONFIG_DLL(wineps16.drv16,enable_win16)
+WINE_CONFIG_DLL(winepulse.drv)
WINE_CONFIG_DLL(wineqtdecoder)
WINE_CONFIG_DLL(winex11.drv)
WINE_CONFIG_DLL(wing.dll16,enable_win16)
diff --git a/dlls/mmdevapi/main.c b/dlls/mmdevapi/main.c
index 52cf6f1..aa4baa5 100644
--- a/dlls/mmdevapi/main.c
+++ b/dlls/mmdevapi/main.c
@@ -113,7 +113,7 @@ static BOOL init_driver(void)
{
static const WCHAR drv_value[] = {'A','u','d','i','o',0};
- static WCHAR default_list[] = {'a','l','s','a',',','o','s','s',',',
+ static WCHAR default_list[] = {'p','u','l','s','e',',','a','l','s','a',',','o','s','s',',',
'c','o','r','e','a','u','d','i','o',0};
DriverFuncs driver;
diff --git a/dlls/winepulse.drv/Makefile.in b/dlls/winepulse.drv/Makefile.in
new file mode 100644
index 0000000..158bbc0
--- /dev/null
+++ b/dlls/winepulse.drv/Makefile.in
@@ -0,0 +1,7 @@
+MODULE = winepulse.drv
+IMPORTS = dxguid uuid winmm user32 advapi32 ole32
+EXTRALIBS = @PULSELIBS@ $(PTHREAD_LIBS)
+EXTRAINCL = @PULSEINCL@
+
+C_SRCS = \
+ mmdevdrv.c
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
new file mode 100644
index 0000000..d187bdc
--- /dev/null
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2011-2012 Maarten Lankhorst
+ * Copyright 2010-2011 Maarten Lankhorst for CodeWeavers
+ * Copyright 2011 Andrew Eikum for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Pulseaudio driver support.. hell froze over
+ */
+
+#define NONAMELESSUNION
+#define COBJMACROS
+#include "config.h"
+#include <poll.h>
+#include <pthread.h>
+
+#include <stdarg.h>
+#include <unistd.h>
+#include <math.h>
+#include <stdio.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "wine/list.h"
+
+#include "ole2.h"
+#include "dshow.h"
+#include "dsound.h"
+#include "propsys.h"
+
+#include "initguid.h"
+#include "ks.h"
+#include "ksmedia.h"
+#include "mmdeviceapi.h"
+#include "audioclient.h"
+#include "endpointvolume.h"
+#include "audiopolicy.h"
+
+#include "wine/list.h"
+
+#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
+
+WINE_DEFAULT_DEBUG_CHANNEL(pulse);
+
+static const REFERENCE_TIME MinimumPeriod = 30000;
+static const REFERENCE_TIME DefaultPeriod = 100000;
+
+static pa_context *pulse_ctx;
+static pa_mainloop *pulse_ml;
+
+static HANDLE pulse_thread;
+static pthread_mutex_t pulse_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER;
+
+static DWORD pulse_stream_volume;
+
+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 };
+
+BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
+{
+ if (reason == DLL_PROCESS_ATTACH) {
+ HKEY key;
+ if (RegOpenKeyW(HKEY_CURRENT_USER, pulse_keyW, &key) == ERROR_SUCCESS) {
+ DWORD size = sizeof(pulse_stream_volume);
+ RegQueryValueExW(key, pulse_streamW, 0, NULL,
+ (BYTE*)&pulse_stream_volume, &size);
+ RegCloseKey(key);
+ }
+ DisableThreadLibraryCalls(dll);
+ } else if (reason == DLL_PROCESS_DETACH) {
+ if (pulse_ctx) {
+ pa_context_disconnect(pulse_ctx);
+ pa_context_unref(pulse_ctx);
+ }
+ if (pulse_ml)
+ pa_mainloop_quit(pulse_ml, 0);
+ CloseHandle(pulse_thread);
+ }
+ return TRUE;
+}
+
+
+static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0};
+
+/* Following pulseaudio design here, mainloop has the lock taken whenever
+ * it is handling something for pulse, and the lock is required whenever
+ * doing any pa_* call that can affect the state in any way
+ *
+ * pa_cond_wait is used when waiting on results, because the mainloop needs
+ * the same lock taken to affect the state
+ *
+ * This is basically the same as the pa_threaded_mainloop implementation,
+ * but that cannot be used because it uses pthread_create directly
+ *
+ * pa_threaded_mainloop_(un)lock -> pthread_mutex_(un)lock
+ * pa_threaded_mainloop_signal -> pthread_cond_signal
+ * pa_threaded_mainloop_wait -> pthread_cond_wait
+ */
+
+static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *userdata) {
+ int r;
+ pthread_mutex_unlock(&pulse_lock);
+ r = poll(ufds, nfds, timeout);
+ pthread_mutex_lock(&pulse_lock);
+ return r;
+}
+
+static DWORD CALLBACK pulse_mainloop_thread(void *tmp) {
+ int ret;
+ pulse_ml = pa_mainloop_new();
+ pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL);
+ pthread_mutex_lock(&pulse_lock);
+ pthread_cond_signal(&pulse_cond);
+ pa_mainloop_run(pulse_ml, &ret);
+ pthread_mutex_unlock(&pulse_lock);
+ pa_mainloop_free(pulse_ml);
+ CloseHandle(pulse_thread);
+ return ret;
+}
+
+static void pulse_contextcallback(pa_context *c, void *userdata);
+
+static HRESULT pulse_connect(void)
+{
+ int len;
+ WCHAR path[PATH_MAX], *name;
+ char *str;
+
+ if (!pulse_thread)
+ {
+ if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, NULL, 0, NULL)))
+ {
+ ERR("Failed to create mainloop thread.");
+ return E_FAIL;
+ }
+ SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL);
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ }
+
+ if (pulse_ctx && PA_CONTEXT_IS_GOOD(pa_context_get_state(pulse_ctx)))
+ return S_OK;
+ if (pulse_ctx)
+ pa_context_unref(pulse_ctx);
+
+ GetModuleFileNameW(NULL, path, sizeof(path)/sizeof(*path));
+ name = strrchrW(path, '\\');
+ if (!name)
+ name = path;
+ else
+ name++;
+ len = WideCharToMultiByte(CP_UNIXCP, 0, name, -1, NULL, 0, NULL, NULL);
+ str = pa_xmalloc(len);
+ WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
+ TRACE("Name: %s\n", str);
+ pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), str);
+ pa_xfree(str);
+ if (!pulse_ctx) {
+ ERR("Failed to create context\n");
+ return E_FAIL;
+ }
+
+ pa_context_set_state_callback(pulse_ctx, pulse_contextcallback, NULL);
+
+ TRACE("libpulse protocol version: %u. API Version %u\n", pa_context_get_protocol_version(pulse_ctx), PA_API_VERSION);
+ if (pa_context_connect(pulse_ctx, NULL, 0, NULL) < 0)
+ goto fail;
+
+ /* Wait for connection */
+ while (pthread_cond_wait(&pulse_cond, &pulse_lock)) {
+ pa_context_state_t state = pa_context_get_state(pulse_ctx);
+
+ if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
+ goto fail;
+
+ if (state == PA_CONTEXT_READY)
+ break;
+ }
+
+ TRACE("Connected to server %s with protocol version: %i.\n",
+ pa_context_get_server(pulse_ctx),
+ pa_context_get_server_protocol_version(pulse_ctx));
+ return S_OK;
+
+fail:
+ pa_context_unref(pulse_ctx);
+ pulse_ctx = NULL;
+ return E_FAIL;
+}
+
+static void pulse_contextcallback(pa_context *c, void *userdata) {
+ switch (pa_context_get_state(c)) {
+ default:
+ FIXME("Unhandled state: %i\n", pa_context_get_state(c));
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_UNCONNECTED:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ case PA_CONTEXT_TERMINATED:
+ TRACE("State change to %i\n", pa_context_get_state(c));
+ return;
+
+ case PA_CONTEXT_READY:
+ TRACE("Ready\n");
+ break;
+
+ case PA_CONTEXT_FAILED:
+ ERR("Context failed: %s\n", pa_strerror(pa_context_errno(c)));
+ break;
+ }
+ pthread_cond_signal(&pulse_cond);
+}
+
+HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys,
+ UINT *num, UINT *def_index)
+{
+ HRESULT hr = S_OK;
+ TRACE("%d %p %p %p\n", flow, ids, num, def_index);
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_connect();
+ pthread_mutex_unlock(&pulse_lock);
+ if (FAILED(hr))
+ return hr;
+ *num = 1;
+ *def_index = 0;
+
+ *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
+ if (!*ids)
+ return E_OUTOFMEMORY;
+
+ (*ids)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
+ if (!(*ids)[0]) {
+ HeapFree(GetProcessHeap(), 0, *ids);
+ return E_OUTOFMEMORY;
+ }
+
+ lstrcpyW((*ids)[0], defaultW);
+
+ *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(void *));
+ (*keys)[0] = NULL;
+
+ return S_OK;
+}
+
+int WINAPI AUDDRV_GetPriority(void)
+{
+ HRESULT hr;
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_connect();
+ pthread_mutex_unlock(&pulse_lock);
+ return SUCCEEDED(hr) ? 3 : 0;
+}
+
+HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev,
+ EDataFlow dataflow, IAudioClient **out)
+{
+ TRACE("%p %p %d %p\n", key, dev, dataflow, out);
+ if (dataflow != eRender && dataflow != eCapture)
+ return E_UNEXPECTED;
+
+ *out = NULL;
+ return E_NOTIMPL;
+}
+
+HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
+ IAudioSessionManager2 **out)
+{
+ *out = NULL;
+ return E_NOTIMPL;
+}
diff --git a/dlls/winepulse.drv/winepulse.drv.spec b/dlls/winepulse.drv/winepulse.drv.spec
new file mode 100644
index 0000000..a089166
--- /dev/null
+++ b/dlls/winepulse.drv/winepulse.drv.spec
@@ -0,0 +1,5 @@
+# MMDevAPI driver functions
+@ stdcall -private GetPriority() AUDDRV_GetPriority
+@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs
+@ stdcall -private GetAudioEndpoint(ptr ptr long ptr) AUDDRV_GetAudioEndpoint
+@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager
--
2.3.5
From 6b4d5551b1c7d747d01f4ed9300eaf985c8c218a Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:29 +0100
Subject: winepulse: Add format and period probing
---
dlls/winepulse.drv/mmdevdrv.c | 128 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 128 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index d187bdc..40db26d 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -70,6 +70,10 @@ static HANDLE pulse_thread;
static pthread_mutex_t pulse_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER;
+/* Mixer format + period times */
+static WAVEFORMATEXTENSIBLE pulse_fmt[2];
+static REFERENCE_TIME pulse_min_period[2], pulse_def_period[2];
+
static DWORD pulse_stream_volume;
const WCHAR pulse_keyW[] = {'S','o','f','t','w','a','r','e','\\',
@@ -139,6 +143,121 @@ static DWORD CALLBACK pulse_mainloop_thread(void *tmp) {
}
static void pulse_contextcallback(pa_context *c, void *userdata);
+static void pulse_stream_state(pa_stream *s, void *user);
+
+static const enum pa_channel_position pulse_pos_from_wfx[] = {
+ PA_CHANNEL_POSITION_FRONT_LEFT,
+ PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_FRONT_CENTER,
+ PA_CHANNEL_POSITION_LFE,
+ PA_CHANNEL_POSITION_REAR_LEFT,
+ PA_CHANNEL_POSITION_REAR_RIGHT,
+ PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,
+ PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,
+ PA_CHANNEL_POSITION_REAR_CENTER,
+ PA_CHANNEL_POSITION_SIDE_LEFT,
+ PA_CHANNEL_POSITION_SIDE_RIGHT,
+ PA_CHANNEL_POSITION_TOP_CENTER,
+ PA_CHANNEL_POSITION_TOP_FRONT_LEFT,
+ PA_CHANNEL_POSITION_TOP_FRONT_CENTER,
+ PA_CHANNEL_POSITION_TOP_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_TOP_REAR_LEFT,
+ PA_CHANNEL_POSITION_TOP_REAR_CENTER,
+ PA_CHANNEL_POSITION_TOP_REAR_RIGHT
+};
+
+static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
+ WAVEFORMATEX *wfx = &fmt->Format;
+ pa_stream *stream;
+ pa_channel_map map;
+ pa_sample_spec ss;
+ pa_buffer_attr attr;
+ int ret, i;
+ unsigned int length = 0;
+
+ pa_channel_map_init_auto(&map, 2, PA_CHANNEL_MAP_ALSA);
+ ss.rate = 48000;
+ ss.format = PA_SAMPLE_FLOAT32LE;
+ ss.channels = map.channels;
+
+ attr.maxlength = -1;
+ attr.tlength = -1;
+ attr.minreq = attr.fragsize = pa_frame_size(&ss);
+ attr.prebuf = 0;
+
+ stream = pa_stream_new(pulse_ctx, "format test stream", &ss, &map);
+ if (stream)
+ pa_stream_set_state_callback(stream, pulse_stream_state, NULL);
+ if (!stream)
+ ret = -1;
+ else if (render)
+ ret = pa_stream_connect_playback(stream, NULL, &attr,
+ PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS, NULL, NULL);
+ else
+ ret = pa_stream_connect_record(stream, NULL, &attr, PA_STREAM_START_CORKED|PA_STREAM_FIX_RATE|PA_STREAM_FIX_CHANNELS|PA_STREAM_EARLY_REQUESTS);
+ if (ret >= 0) {
+ while (pa_stream_get_state(stream) == PA_STREAM_CREATING)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ if (pa_stream_get_state(stream) == PA_STREAM_READY) {
+ ss = *pa_stream_get_sample_spec(stream);
+ map = *pa_stream_get_channel_map(stream);
+ if (render)
+ length = pa_stream_get_buffer_attr(stream)->minreq;
+ else
+ length = pa_stream_get_buffer_attr(stream)->fragsize;
+ pa_stream_disconnect(stream);
+ while (pa_stream_get_state(stream) == PA_STREAM_READY)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ }
+ }
+ if (stream)
+ pa_stream_unref(stream);
+ if (length)
+ pulse_def_period[!render] = pulse_min_period[!render] = pa_bytes_to_usec(10 * length, &ss);
+ else
+ pulse_min_period[!render] = MinimumPeriod;
+ if (pulse_def_period[!render] <= DefaultPeriod)
+ pulse_def_period[!render] = DefaultPeriod;
+
+ wfx->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wfx->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
+ wfx->nChannels = ss.channels;
+ wfx->wBitsPerSample = 8 * pa_sample_size_of_format(ss.format);
+ wfx->nSamplesPerSec = ss.rate;
+ wfx->nBlockAlign = pa_frame_size(&ss);
+ wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
+ if (ss.format != PA_SAMPLE_S24_32LE)
+ fmt->Samples.wValidBitsPerSample = wfx->wBitsPerSample;
+ else
+ fmt->Samples.wValidBitsPerSample = 24;
+ if (ss.format == PA_SAMPLE_FLOAT32LE)
+ fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ else
+ fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+
+ fmt->dwChannelMask = 0;
+ for (i = 0; i < map.channels; ++i)
+ switch (map.map[i]) {
+ default: FIXME("Unhandled channel %s\n", pa_channel_position_to_string(map.map[i])); break;
+ case PA_CHANNEL_POSITION_FRONT_LEFT: fmt->dwChannelMask |= SPEAKER_FRONT_LEFT; break;
+ case PA_CHANNEL_POSITION_MONO:
+ case PA_CHANNEL_POSITION_FRONT_CENTER: fmt->dwChannelMask |= SPEAKER_FRONT_CENTER; break;
+ case PA_CHANNEL_POSITION_FRONT_RIGHT: fmt->dwChannelMask |= SPEAKER_FRONT_RIGHT; break;
+ case PA_CHANNEL_POSITION_REAR_LEFT: fmt->dwChannelMask |= SPEAKER_BACK_LEFT; break;
+ case PA_CHANNEL_POSITION_REAR_CENTER: fmt->dwChannelMask |= SPEAKER_BACK_CENTER; break;
+ case PA_CHANNEL_POSITION_REAR_RIGHT: fmt->dwChannelMask |= SPEAKER_BACK_RIGHT; break;
+ case PA_CHANNEL_POSITION_LFE: fmt->dwChannelMask |= SPEAKER_LOW_FREQUENCY; break;
+ case PA_CHANNEL_POSITION_SIDE_LEFT: fmt->dwChannelMask |= SPEAKER_SIDE_LEFT; break;
+ case PA_CHANNEL_POSITION_SIDE_RIGHT: fmt->dwChannelMask |= SPEAKER_SIDE_RIGHT; break;
+ case PA_CHANNEL_POSITION_TOP_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_CENTER; break;
+ case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_LEFT; break;
+ case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_CENTER; break;
+ case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_RIGHT; break;
+ case PA_CHANNEL_POSITION_TOP_REAR_LEFT: fmt->dwChannelMask |= SPEAKER_TOP_BACK_LEFT; break;
+ case PA_CHANNEL_POSITION_TOP_REAR_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_BACK_CENTER; break;
+ case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: fmt->dwChannelMask |= SPEAKER_TOP_BACK_RIGHT; break;
+ }
+}
static HRESULT pulse_connect(void)
{
@@ -199,6 +318,8 @@ static HRESULT pulse_connect(void)
TRACE("Connected to server %s with protocol version: %i.\n",
pa_context_get_server(pulse_ctx),
pa_context_get_server_protocol_version(pulse_ctx));
+ pulse_probe_settings(1, &pulse_fmt[0]);
+ pulse_probe_settings(0, &pulse_fmt[1]);
return S_OK;
fail:
@@ -230,6 +351,13 @@ static void pulse_contextcallback(pa_context *c, void *userdata) {
pthread_cond_signal(&pulse_cond);
}
+static void pulse_stream_state(pa_stream *s, void *user)
+{
+ pa_stream_state_t state = pa_stream_get_state(s);
+ TRACE("Stream state changed to %i\n", state);
+ pthread_cond_signal(&pulse_cond);
+}
+
HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys,
UINT *num, UINT *def_index)
{
--
2.3.5
From cb7a7a7c14f2c13cec38b30a7f3323b008ef298b Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:29 +0100
Subject: winepulse: Add audioclient
---
Without AudioRenderClient and AudioCaptureClient it won't work,
but it's easier to review
---
dlls/winepulse.drv/mmdevdrv.c | 1041 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 1040 insertions(+), 1 deletion(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 40db26d..37d85ff 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -103,9 +103,55 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
return TRUE;
}
+typedef struct ACImpl ACImpl;
+
+typedef struct _ACPacket {
+ struct list entry;
+ UINT64 qpcpos;
+ BYTE *data;
+ UINT32 discont;
+} ACPacket;
+
+struct ACImpl {
+ IAudioClient IAudioClient_iface;
+ IAudioRenderClient IAudioRenderClient_iface;
+ IAudioCaptureClient IAudioCaptureClient_iface;
+ IAudioClock IAudioClock_iface;
+ IAudioClock2 IAudioClock2_iface;
+ IAudioStreamVolume IAudioStreamVolume_iface;
+ IMMDevice *parent;
+ struct list entry;
+ float vol[PA_CHANNELS_MAX];
+
+ LONG ref;
+ EDataFlow dataflow;
+ DWORD flags;
+ AUDCLNT_SHAREMODE share;
+ HANDLE event;
+
+ UINT32 bufsize_frames, bufsize_bytes, locked, capture_period, pad, started, peek_ofs;
+ void *locked_ptr, *tmp_buffer;
+
+ pa_stream *stream;
+ pa_sample_spec ss;
+ pa_channel_map map;
+
+ INT64 clock_lastpos, clock_written;
+ pa_usec_t clock_pulse;
+
+ struct list packet_free_head;
+ struct list packet_filled_head;
+};
static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0};
+static const IAudioClientVtbl AudioClient_Vtbl;
+
+static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
+{
+ return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
+}
+
/* Following pulseaudio design here, mainloop has the lock taken whenever
* it is handling something for pulse, and the lock is required whenever
* doing any pa_* call that can affect the state in any way
@@ -351,6 +397,192 @@ static void pulse_contextcallback(pa_context *c, void *userdata) {
pthread_cond_signal(&pulse_cond);
}
+static HRESULT pulse_stream_valid(ACImpl *This) {
+ if (!This->stream)
+ return AUDCLNT_E_NOT_INITIALIZED;
+ if (!This->stream || pa_stream_get_state(This->stream) != PA_STREAM_READY)
+ return AUDCLNT_E_DEVICE_INVALIDATED;
+ return S_OK;
+}
+
+static void dump_attr(const pa_buffer_attr *attr) {
+ TRACE("maxlength: %u\n", attr->maxlength);
+ TRACE("minreq: %u\n", attr->minreq);
+ TRACE("fragsize: %u\n", attr->fragsize);
+ TRACE("tlength: %u\n", attr->tlength);
+ TRACE("prebuf: %u\n", attr->prebuf);
+}
+
+static void pulse_op_cb(pa_stream *s, int success, void *user) {
+ TRACE("Success: %i\n", success);
+ *(int*)user = success;
+ pthread_cond_signal(&pulse_cond);
+}
+
+static void pulse_attr_update(pa_stream *s, void *user) {
+ const pa_buffer_attr *attr = pa_stream_get_buffer_attr(s);
+ TRACE("New attributes or device moved:\n");
+ dump_attr(attr);
+}
+
+static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata)
+{
+ ACImpl *This = userdata;
+ pa_usec_t time;
+ UINT32 oldpad = This->pad;
+
+ if (bytes < This->bufsize_bytes)
+ This->pad = This->bufsize_bytes - bytes;
+ else
+ This->pad = 0;
+
+ assert(oldpad >= This->pad);
+
+ if (0 && This->pad && pa_stream_get_time(This->stream, &time) >= 0)
+ This->clock_pulse = time;
+ else
+ This->clock_pulse = PA_USEC_INVALID;
+
+ This->clock_written += oldpad - This->pad;
+ TRACE("New pad: %u (-%u)\n", This->pad / pa_frame_size(&This->ss), (oldpad - This->pad) / pa_frame_size(&This->ss));
+
+ if (This->event)
+ SetEvent(This->event);
+}
+
+static void pulse_underflow_callback(pa_stream *s, void *userdata)
+{
+ ACImpl *This = userdata;
+ This->clock_pulse = PA_USEC_INVALID;
+ WARN("Underflow\n");
+}
+
+/* Latency is periodically updated even when nothing is played,
+ * because of PA_STREAM_AUTO_TIMING_UPDATE so use it as timer
+ *
+ * Perfect for passing all tests :)
+ */
+static void pulse_latency_callback(pa_stream *s, void *userdata)
+{
+ ACImpl *This = userdata;
+ if (!This->pad && This->event)
+ SetEvent(This->event);
+}
+
+static void pulse_started_callback(pa_stream *s, void *userdata)
+{
+ ACImpl *This = userdata;
+ pa_usec_t time;
+
+ TRACE("(Re)started playing\n");
+ assert(This->clock_pulse == PA_USEC_INVALID);
+ if (0 && pa_stream_get_time(This->stream, &time) >= 0)
+ This->clock_pulse = time;
+ if (This->event)
+ SetEvent(This->event);
+}
+
+static void pulse_rd_loop(ACImpl *This, size_t bytes)
+{
+ while (bytes >= This->capture_period) {
+ ACPacket *p, *next;
+ LARGE_INTEGER stamp, freq;
+ BYTE *dst, *src;
+ UINT32 src_len, copy, rem = This->capture_period;
+ if (!(p = (ACPacket*)list_head(&This->packet_free_head))) {
+ p = (ACPacket*)list_head(&This->packet_filled_head);
+ if (!p->discont) {
+ next = (ACPacket*)p->entry.next;
+ next->discont = 1;
+ } else
+ p = (ACPacket*)list_tail(&This->packet_filled_head);
+ assert(This->pad == This->bufsize_bytes);
+ } else {
+ assert(This->pad < This->bufsize_bytes);
+ This->pad += This->capture_period;
+ assert(This->pad <= This->bufsize_bytes);
+ }
+ QueryPerformanceCounter(&stamp);
+ QueryPerformanceFrequency(&freq);
+ p->qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
+ p->discont = 0;
+ list_remove(&p->entry);
+ list_add_tail(&This->packet_filled_head, &p->entry);
+
+ dst = p->data;
+ while (rem) {
+ pa_stream_peek(This->stream, (const void**)&src, &src_len);
+ assert(src_len && src_len <= bytes);
+ assert(This->peek_ofs < src_len);
+ src += This->peek_ofs;
+ src_len -= This->peek_ofs;
+
+ copy = rem;
+ if (copy > src_len)
+ copy = src_len;
+ memcpy(dst, src, rem);
+ src += copy;
+ src_len -= copy;
+ dst += copy;
+ rem -= copy;
+
+ if (!src_len) {
+ This->peek_ofs = 0;
+ pa_stream_drop(This->stream);
+ } else
+ This->peek_ofs += copy;
+ }
+ bytes -= This->capture_period;
+ }
+}
+
+static void pulse_rd_drop(ACImpl *This, size_t bytes)
+{
+ while (bytes >= This->capture_period) {
+ UINT32 src_len, copy, rem = This->capture_period;
+ while (rem) {
+ const void *src;
+ pa_stream_peek(This->stream, &src, &src_len);
+ assert(src_len && src_len <= bytes);
+ assert(This->peek_ofs < src_len);
+ src_len -= This->peek_ofs;
+
+ copy = rem;
+ if (copy > src_len)
+ copy = src_len;
+
+ src_len -= copy;
+ rem -= copy;
+
+ if (!src_len) {
+ This->peek_ofs = 0;
+ pa_stream_drop(This->stream);
+ } else
+ This->peek_ofs += copy;
+ bytes -= copy;
+ }
+ }
+}
+
+static void pulse_rd_callback(pa_stream *s, size_t bytes, void *userdata)
+{
+ ACImpl *This = userdata;
+
+ TRACE("Readable total: %u, fragsize: %u\n", bytes, pa_stream_get_buffer_attr(s)->fragsize);
+ assert(bytes >= This->peek_ofs);
+ bytes -= This->peek_ofs;
+ if (bytes < This->capture_period)
+ return;
+
+ if (This->started)
+ pulse_rd_loop(This, bytes);
+ else
+ pulse_rd_drop(This, bytes);
+
+ if (This->event)
+ SetEvent(This->event);
+}
+
static void pulse_stream_state(pa_stream *s, void *user)
{
pa_stream_state_t state = pa_stream_get_state(s);
@@ -358,6 +590,53 @@ static void pulse_stream_state(pa_stream *s, void *user)
pthread_cond_signal(&pulse_cond);
}
+static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
+ int ret;
+ char buffer[64];
+ static LONG number;
+ pa_buffer_attr attr;
+ if (This->stream) {
+ pa_stream_disconnect(This->stream);
+ while (pa_stream_get_state(This->stream) == PA_STREAM_READY)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ pa_stream_unref(This->stream);
+ }
+ ret = InterlockedIncrement(&number);
+ sprintf(buffer, "audio stream #%i", ret);
+ This->stream = pa_stream_new(pulse_ctx, buffer, &This->ss, &This->map);
+ pa_stream_set_state_callback(This->stream, pulse_stream_state, This);
+ pa_stream_set_buffer_attr_callback(This->stream, pulse_attr_update, This);
+ pa_stream_set_moved_callback(This->stream, pulse_attr_update, This);
+
+ /* Pulseaudio will fill in correct values */
+ attr.minreq = attr.fragsize = period_bytes;
+ attr.maxlength = attr.tlength = This->bufsize_bytes;
+ attr.prebuf = pa_frame_size(&This->ss);
+ dump_attr(&attr);
+ 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);
+ 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);
+ if (ret < 0) {
+ WARN("Returns %i\n", ret);
+ return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
+ }
+ while (pa_stream_get_state(This->stream) == PA_STREAM_CREATING)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ if (pa_stream_get_state(This->stream) != PA_STREAM_READY)
+ return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
+
+ if (This->dataflow == eRender) {
+ pa_stream_set_write_callback(This->stream, pulse_wr_callback, This);
+ pa_stream_set_underflow_callback(This->stream, pulse_underflow_callback, This);
+ pa_stream_set_started_callback(This->stream, pulse_started_callback, This);
+ } else
+ pa_stream_set_read_callback(This->stream, pulse_rd_callback, This);
+ return S_OK;
+}
+
HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys,
UINT *num, UINT *def_index)
{
@@ -402,14 +681,774 @@ int WINAPI AUDDRV_GetPriority(void)
HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev,
EDataFlow dataflow, IAudioClient **out)
{
+ HRESULT hr;
+ ACImpl *This;
+ int i;
+
TRACE("%p %p %d %p\n", key, dev, dataflow, out);
if (dataflow != eRender && dataflow != eCapture)
return E_UNEXPECTED;
*out = NULL;
- return E_NOTIMPL;
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_connect();
+ pthread_mutex_unlock(&pulse_lock);
+ if (FAILED(hr))
+ return hr;
+
+ This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
+ if (!This)
+ return E_OUTOFMEMORY;
+
+ This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
+ This->dataflow = dataflow;
+ This->parent = dev;
+ This->clock_pulse = PA_USEC_INVALID;
+ for (i = 0; i < PA_CHANNELS_MAX; ++i)
+ This->vol[i] = 1.f;
+ IMMDevice_AddRef(This->parent);
+
+ *out = &This->IAudioClient_iface;
+ IAudioClient_AddRef(&This->IAudioClient_iface);
+
+ return S_OK;
+}
+
+static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
+ REFIID riid, void **ppv)
+{
+ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
+
+ if (!ppv)
+ return E_POINTER;
+ *ppv = NULL;
+ if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
+ *ppv = iface;
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+ WARN("Unknown interface %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI AudioClient_AddRef(IAudioClient *iface)
+{
+ ACImpl *This = impl_from_IAudioClient(iface);
+ ULONG ref;
+ ref = InterlockedIncrement(&This->ref);
+ TRACE("(%p) Refcount now %u\n", This, ref);
+ return ref;
+}
+
+static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
+{
+ ACImpl *This = impl_from_IAudioClient(iface);
+ ULONG ref;
+ ref = InterlockedDecrement(&This->ref);
+ TRACE("(%p) Refcount now %u\n", This, ref);
+ if (!ref) {
+ if (This->stream) {
+ pthread_mutex_lock(&pulse_lock);
+ if (PA_STREAM_IS_GOOD(pa_stream_get_state(This->stream))) {
+ pa_stream_disconnect(This->stream);
+ while (PA_STREAM_IS_GOOD(pa_stream_get_state(This->stream)))
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ }
+ pa_stream_unref(This->stream);
+ This->stream = NULL;
+ list_remove(&This->entry);
+ pthread_mutex_unlock(&pulse_lock);
+ }
+ IMMDevice_Release(This->parent);
+ HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
+ HeapFree(GetProcessHeap(), 0, This);
+ }
+ return ref;
+}
+
+static void dump_fmt(const WAVEFORMATEX *fmt)
+{
+ TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
+ switch(fmt->wFormatTag) {
+ case WAVE_FORMAT_PCM:
+ TRACE("WAVE_FORMAT_PCM");
+ break;
+ case WAVE_FORMAT_IEEE_FLOAT:
+ TRACE("WAVE_FORMAT_IEEE_FLOAT");
+ break;
+ case WAVE_FORMAT_EXTENSIBLE:
+ TRACE("WAVE_FORMAT_EXTENSIBLE");
+ break;
+ default:
+ TRACE("Unknown");
+ break;
+ }
+ TRACE(")\n");
+
+ TRACE("nChannels: %u\n", fmt->nChannels);
+ TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
+ TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
+ TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
+ TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
+ TRACE("cbSize: %u\n", fmt->cbSize);
+
+ if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
+ WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
+ TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
+ TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
+ TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
+ }
+}
+
+static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
+{
+ WAVEFORMATEX *ret;
+ size_t size;
+
+ if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
+ size = sizeof(WAVEFORMATEXTENSIBLE);
+ else
+ size = sizeof(WAVEFORMATEX);
+
+ ret = CoTaskMemAlloc(size);
+ if (!ret)
+ return NULL;
+
+ memcpy(ret, fmt, size);
+
+ ret->cbSize = size - sizeof(WAVEFORMATEX);
+
+ return ret;
+}
+
+static DWORD get_channel_mask(unsigned int channels)
+{
+ switch(channels) {
+ case 0:
+ return 0;
+ case 1:
+ return SPEAKER_FRONT_CENTER;
+ case 2:
+ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
+ case 3:
+ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
+ SPEAKER_LOW_FREQUENCY;
+ case 4:
+ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
+ SPEAKER_BACK_RIGHT;
+ case 5:
+ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
+ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY;
+ case 6:
+ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
+ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER;
+ case 7:
+ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
+ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER |
+ SPEAKER_BACK_CENTER;
+ case 8:
+ return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
+ SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER |
+ SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
+ }
+ FIXME("Unknown speaker configuration: %u\n", channels);
+ return 0;
+}
+
+static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
+{
+ pa_channel_map_init(&This->map);
+ This->ss.rate = fmt->nSamplesPerSec;
+ This->ss.format = PA_SAMPLE_INVALID;
+ switch(fmt->wFormatTag) {
+ case WAVE_FORMAT_IEEE_FLOAT:
+ if (!fmt->nChannels || fmt->nChannels > 2 || fmt->wBitsPerSample != 32)
+ break;
+ This->ss.format = PA_SAMPLE_FLOAT32LE;
+ pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
+ break;
+ case WAVE_FORMAT_PCM:
+ if (!fmt->nChannels || fmt->nChannels > 2)
+ break;
+ if (fmt->wBitsPerSample == 8)
+ This->ss.format = PA_SAMPLE_U8;
+ else if (fmt->wBitsPerSample == 16)
+ This->ss.format = PA_SAMPLE_S16LE;
+ pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
+ break;
+ case WAVE_FORMAT_EXTENSIBLE: {
+ WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)fmt;
+ DWORD mask = wfe->dwChannelMask;
+ DWORD i = 0, j;
+ if (fmt->cbSize != (sizeof(*wfe) - sizeof(*fmt)) && fmt->cbSize != sizeof(*wfe))
+ break;
+ if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) &&
+ (!wfe->Samples.wValidBitsPerSample || wfe->Samples.wValidBitsPerSample == 32) &&
+ fmt->wBitsPerSample == 32)
+ This->ss.format = PA_SAMPLE_FLOAT32LE;
+ else if (IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
+ DWORD valid = wfe->Samples.wValidBitsPerSample;
+ if (!valid)
+ valid = fmt->wBitsPerSample;
+ if (!valid || valid > fmt->wBitsPerSample)
+ break;
+ switch (fmt->wBitsPerSample) {
+ case 8:
+ if (valid == 8)
+ This->ss.format = PA_SAMPLE_U8;
+ break;
+ case 16:
+ if (valid == 16)
+ This->ss.format = PA_SAMPLE_S16LE;
+ break;
+ case 24:
+ if (valid == 24)
+ This->ss.format = PA_SAMPLE_S24LE;
+ break;
+ case 32:
+ if (valid == 24)
+ This->ss.format = PA_SAMPLE_S24_32LE;
+ else if (valid == 32)
+ This->ss.format = PA_SAMPLE_S32LE;
+ default:
+ break;
+ }
+ }
+ This->map.channels = fmt->nChannels;
+ if (!mask)
+ mask = get_channel_mask(fmt->nChannels);
+ for (j = 0; j < sizeof(pulse_pos_from_wfx)/sizeof(*pulse_pos_from_wfx) && i < fmt->nChannels; ++j) {
+ if (mask & (1 << j))
+ This->map.map[i++] = pulse_pos_from_wfx[j];
+ }
+
+ /* Special case for mono since pulse appears to map it differently */
+ if (mask == SPEAKER_FRONT_CENTER)
+ This->map.map[0] = PA_CHANNEL_POSITION_MONO;
+
+ if ((mask & SPEAKER_ALL) && i < fmt->nChannels) {
+ This->map.map[i++] = PA_CHANNEL_POSITION_MONO;
+ FIXME("Is the 'all' channel mapped correctly?\n");
+ }
+
+ if (i < fmt->nChannels || (mask & SPEAKER_RESERVED)) {
+ This->map.channels = 0;
+ ERR("Invalid channel mask: %i/%i and %x\n", i, fmt->nChannels, mask);
+ break;
+ }
+ break;
+ }
+ default: FIXME("Unhandled tag %x\n", fmt->wFormatTag);
+ }
+ This->ss.channels = This->map.channels;
+ if (!pa_channel_map_valid(&This->map) || This->ss.format == PA_SAMPLE_INVALID) {
+ ERR("Invalid format! Channel spec valid: %i, format: %i\n", pa_channel_map_valid(&This->map), This->ss.format);
+ dump_fmt(fmt);
+ return AUDCLNT_E_UNSUPPORTED_FORMAT;
+ }
+ return S_OK;
}
+static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
+ AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
+ REFERENCE_TIME period, const WAVEFORMATEX *fmt,
+ const GUID *sessionguid)
+{
+ ACImpl *This = impl_from_IAudioClient(iface);
+ HRESULT hr = S_OK;
+ UINT period_bytes;
+
+ TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
+ wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
+
+ if (!fmt)
+ return E_POINTER;
+
+ if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
+ return AUDCLNT_E_NOT_INITIALIZED;
+ if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
+ return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
+
+ if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
+ AUDCLNT_STREAMFLAGS_LOOPBACK |
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
+ AUDCLNT_STREAMFLAGS_NOPERSIST |
+ AUDCLNT_STREAMFLAGS_RATEADJUST |
+ AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
+ AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
+ AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED)) {
+ TRACE("Unknown flags: %08x\n", flags);
+ return E_INVALIDARG;
+ }
+
+ pthread_mutex_lock(&pulse_lock);
+ if (This->stream) {
+ pthread_mutex_unlock(&pulse_lock);
+ return AUDCLNT_E_ALREADY_INITIALIZED;
+ }
+
+ hr = pulse_spec_from_waveformat(This, fmt);
+ if (FAILED(hr))
+ goto exit;
+
+ if (mode == AUDCLNT_SHAREMODE_SHARED) {
+ period = pulse_def_period[This->dataflow == eCapture];
+ if (duration < 2 * period)
+ duration = 2 * period;
+ }
+ period_bytes = pa_frame_size(&This->ss) * MulDiv(period, This->ss.rate, 10000000);
+
+ if (duration < 20000000)
+ This->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec);
+ else
+ This->bufsize_frames = 2 * fmt->nSamplesPerSec;
+ This->bufsize_bytes = This->bufsize_frames * pa_frame_size(&This->ss);
+
+ This->share = mode;
+ This->flags = flags;
+ hr = pulse_stream_connect(This, period_bytes);
+ if (SUCCEEDED(hr)) {
+ UINT32 unalign;
+ const pa_buffer_attr *attr = pa_stream_get_buffer_attr(This->stream);
+ /* Update frames according to new size */
+ dump_attr(attr);
+ if (This->dataflow == eRender)
+ This->bufsize_bytes = attr->tlength;
+ else {
+ This->capture_period = period_bytes = attr->fragsize;
+ if ((unalign = This->bufsize_bytes % period_bytes))
+ This->bufsize_bytes += period_bytes - unalign;
+ }
+ This->bufsize_frames = This->bufsize_bytes / pa_frame_size(&This->ss);
+ }
+ if (SUCCEEDED(hr)) {
+ UINT32 i, capture_packets = This->capture_period ? This->bufsize_bytes / This->capture_period : 0;
+ This->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, This->bufsize_bytes + capture_packets * sizeof(ACPacket));
+ if (!This->tmp_buffer)
+ hr = E_OUTOFMEMORY;
+ else {
+ ACPacket *cur_packet = (ACPacket*)((char*)This->tmp_buffer + This->bufsize_bytes);
+ BYTE *data = This->tmp_buffer;
+ memset(This->tmp_buffer, This->ss.format == PA_SAMPLE_U8 ? 0x80 : 0, This->bufsize_bytes);
+ list_init(&This->packet_free_head);
+ list_init(&This->packet_filled_head);
+ for (i = 0; i < capture_packets; ++i, ++cur_packet) {
+ list_add_tail(&This->packet_free_head, &cur_packet->entry);
+ cur_packet->data = data;
+ data += This->capture_period;
+ }
+ assert(!This->capture_period || This->bufsize_bytes == This->capture_period * capture_packets);
+ assert(!capture_packets || data - This->bufsize_bytes == This->tmp_buffer);
+ }
+ }
+
+exit:
+ if (FAILED(hr)) {
+ HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
+ This->tmp_buffer = NULL;
+ if (This->stream) {
+ pa_stream_disconnect(This->stream);
+ pa_stream_unref(This->stream);
+ This->stream = NULL;
+ }
+ }
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+}
+
+static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient *iface,
+ UINT32 *out)
+{
+ ACImpl *This = impl_from_IAudioClient(iface);
+ HRESULT hr;
+
+ TRACE("(%p)->(%p)\n", This, out);
+
+ if (!out)
+ return E_POINTER;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (SUCCEEDED(hr))
+ *out = This->bufsize_frames;
+ pthread_mutex_unlock(&pulse_lock);
+
+ return hr;
+}
+
+static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient *iface,
+ REFERENCE_TIME *latency)
+{
+ ACImpl *This = impl_from_IAudioClient(iface);
+ const pa_buffer_attr *attr;
+ REFERENCE_TIME lat;
+ HRESULT hr;
+
+ TRACE("(%p)->(%p)\n", This, latency);
+
+ if (!latency)
+ return E_POINTER;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr)) {
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+ }
+ attr = pa_stream_get_buffer_attr(This->stream);
+ if (This->dataflow == eRender)
+ lat = attr->minreq / pa_frame_size(&This->ss);
+ else
+ lat = attr->fragsize / pa_frame_size(&This->ss);
+ *latency = 10000000;
+ *latency *= lat;
+ *latency /= This->ss.rate;
+ pthread_mutex_unlock(&pulse_lock);
+ TRACE("Latency: %u ms\n", (DWORD)(*latency / 10000));
+ return S_OK;
+}
+
+static void ACImpl_GetRenderPad(ACImpl *This, UINT32 *out)
+{
+ *out = This->pad / pa_frame_size(&This->ss);
+}
+
+static void ACImpl_GetCapturePad(ACImpl *This, UINT32 *out)
+{
+ ACPacket *packet = This->locked_ptr;
+ if (!packet && !list_empty(&This->packet_filled_head)) {
+ packet = (ACPacket*)list_head(&This->packet_filled_head);
+ This->locked_ptr = packet;
+ list_remove(&packet->entry);
+ }
+ if (out)
+ *out = This->pad / pa_frame_size(&This->ss);
+}
+
+static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
+ UINT32 *out)
+{
+ ACImpl *This = impl_from_IAudioClient(iface);
+ HRESULT hr;
+
+ TRACE("(%p)->(%p)\n", This, out);
+
+ if (!out)
+ return E_POINTER;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr)) {
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+ }
+
+ if (This->dataflow == eRender)
+ ACImpl_GetRenderPad(This, out);
+ else
+ ACImpl_GetCapturePad(This, out);
+ pthread_mutex_unlock(&pulse_lock);
+
+ TRACE("%p Pad: %u ms (%u)\n", This, MulDiv(*out, 1000, This->ss.rate), *out);
+ return S_OK;
+}
+
+static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
+ AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
+ WAVEFORMATEX **out)
+{
+ ACImpl *This = impl_from_IAudioClient(iface);
+ HRESULT hr = S_OK;
+ WAVEFORMATEX *closest = NULL;
+
+ TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
+
+ if (!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
+ return E_POINTER;
+
+ if (out)
+ *out = NULL;
+ if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
+ return E_INVALIDARG;
+ if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
+ return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
+ if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
+ fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
+ return E_INVALIDARG;
+
+ dump_fmt(fmt);
+
+ closest = clone_format(fmt);
+ if (!closest)
+ hr = E_OUTOFMEMORY;
+
+ if (hr == S_OK || !out) {
+ CoTaskMemFree(closest);
+ if (out)
+ *out = NULL;
+ } else if (closest) {
+ closest->nBlockAlign =
+ closest->nChannels * closest->wBitsPerSample / 8;
+ closest->nAvgBytesPerSec =
+ closest->nBlockAlign * closest->nSamplesPerSec;
+ *out = closest;
+ }
+
+ TRACE("returning: %08x %p\n", hr, out ? *out : NULL);
+ return hr;
+}
+
+static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
+ WAVEFORMATEX **pwfx)
+{
+ ACImpl *This = impl_from_IAudioClient(iface);
+ WAVEFORMATEXTENSIBLE *fmt = &pulse_fmt[This->dataflow == eCapture];
+
+ TRACE("(%p)->(%p)\n", This, pwfx);
+
+ if (!pwfx)
+ return E_POINTER;
+
+ *pwfx = clone_format(&fmt->Format);
+ if (!*pwfx)
+ return E_OUTOFMEMORY;
+ dump_fmt(*pwfx);
+ return S_OK;
+}
+
+static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient *iface,
+ REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
+{
+ ACImpl *This = impl_from_IAudioClient(iface);
+
+ TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
+
+ if (!defperiod && !minperiod)
+ return E_POINTER;
+
+ if (defperiod)
+ *defperiod = pulse_def_period[This->dataflow == eCapture];
+ if (minperiod)
+ *minperiod = pulse_min_period[This->dataflow == eCapture];
+
+ return S_OK;
+}
+
+static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
+{
+ ACImpl *This = impl_from_IAudioClient(iface);
+ HRESULT hr = S_OK;
+ int success;
+ pa_operation *o;
+
+ TRACE("(%p)\n", This);
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr)) {
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+ }
+
+ if ((This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !This->event) {
+ pthread_mutex_unlock(&pulse_lock);
+ return AUDCLNT_E_EVENTHANDLE_NOT_SET;
+ }
+
+ if (This->started) {
+ pthread_mutex_unlock(&pulse_lock);
+ return AUDCLNT_E_NOT_STOPPED;
+ }
+ This->clock_pulse = PA_USEC_INVALID;
+
+ if (pa_stream_is_corked(This->stream)) {
+ o = pa_stream_cork(This->stream, 0, pulse_op_cb, &success);
+ if (o) {
+ while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ pa_operation_unref(o);
+ } else
+ success = 0;
+ if (!success)
+ hr = E_FAIL;
+ }
+ if (SUCCEEDED(hr)) {
+ This->started = TRUE;
+ if (This->dataflow == eRender && This->event)
+ pa_stream_set_latency_update_callback(This->stream, pulse_latency_callback, This);
+ }
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+}
+
+static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
+{
+ ACImpl *This = impl_from_IAudioClient(iface);
+ HRESULT hr = S_OK;
+ pa_operation *o;
+ int success;
+
+ TRACE("(%p)\n", This);
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr)) {
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+ }
+
+ if (!This->started) {
+ pthread_mutex_unlock(&pulse_lock);
+ return S_FALSE;
+ }
+
+ if (This->dataflow == eRender) {
+ o = pa_stream_cork(This->stream, 1, pulse_op_cb, &success);
+ if (o) {
+ while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ pa_operation_unref(o);
+ } else
+ success = 0;
+ if (!success)
+ hr = E_FAIL;
+ }
+ if (SUCCEEDED(hr)) {
+ This->started = FALSE;
+ This->clock_pulse = PA_USEC_INVALID;
+ }
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+}
+
+static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
+{
+ ACImpl *This = impl_from_IAudioClient(iface);
+ HRESULT hr = S_OK;
+
+ TRACE("(%p)\n", This);
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr)) {
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+ }
+
+ if (This->started) {
+ pthread_mutex_unlock(&pulse_lock);
+ return AUDCLNT_E_NOT_STOPPED;
+ }
+
+ if (This->locked) {
+ pthread_mutex_unlock(&pulse_lock);
+ return AUDCLNT_E_BUFFER_OPERATION_PENDING;
+ }
+
+ if (This->dataflow == eRender) {
+ /* If there is still data in the render buffer it needs to be removed from the server */
+ int success = 0;
+ if (This->pad) {
+ pa_operation *o = pa_stream_flush(This->stream, pulse_op_cb, &success);
+ if (o) {
+ while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ pa_operation_unref(o);
+ }
+ }
+ if (success || !This->pad)
+ This->clock_lastpos = This->clock_written = This->pad = 0;
+ } else {
+ ACPacket *p;
+ This->clock_written += This->pad;
+ This->pad = 0;
+
+ if ((p = This->locked_ptr)) {
+ This->locked_ptr = NULL;
+ list_add_tail(&This->packet_free_head, &p->entry);
+ }
+ list_move_tail(&This->packet_free_head, &This->packet_filled_head);
+ }
+ pthread_mutex_unlock(&pulse_lock);
+
+ return hr;
+}
+
+static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient *iface,
+ HANDLE event)
+{
+ ACImpl *This = impl_from_IAudioClient(iface);
+ HRESULT hr;
+
+ TRACE("(%p)->(%p)\n", This, event);
+
+ if (!event)
+ return E_INVALIDARG;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr)) {
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+ }
+
+ if (!(This->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK))
+ hr = AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED;
+ else if (This->event)
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
+ else
+ This->event = event;
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+}
+
+static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
+ void **ppv)
+{
+ ACImpl *This = impl_from_IAudioClient(iface);
+ HRESULT hr;
+
+ TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
+
+ if (!ppv)
+ return E_POINTER;
+ *ppv = NULL;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ pthread_mutex_unlock(&pulse_lock);
+ if (FAILED(hr))
+ return hr;
+
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+
+ FIXME("stub %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static const IAudioClientVtbl AudioClient_Vtbl =
+{
+ AudioClient_QueryInterface,
+ AudioClient_AddRef,
+ AudioClient_Release,
+ AudioClient_Initialize,
+ AudioClient_GetBufferSize,
+ AudioClient_GetStreamLatency,
+ AudioClient_GetCurrentPadding,
+ AudioClient_IsFormatSupported,
+ AudioClient_GetMixFormat,
+ AudioClient_GetDevicePeriod,
+ AudioClient_Start,
+ AudioClient_Stop,
+ AudioClient_Reset,
+ AudioClient_SetEventHandle,
+ AudioClient_GetService
+};
+
HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
IAudioSessionManager2 **out)
{
--
2.3.5
From a416ac84c76eabafa00e967af185af1f26e1cb14 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:29 +0100
Subject: winepulse: Add IAudioRenderClient and IAudioCaptureClient
---
dlls/winepulse.drv/mmdevdrv.c | 301 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 301 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 37d85ff..01cfd25 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -146,12 +146,27 @@ struct ACImpl {
static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0};
static const IAudioClientVtbl AudioClient_Vtbl;
+static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
+static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
+static const IAudioClockVtbl AudioClock_Vtbl;
+static const IAudioClock2Vtbl AudioClock2_Vtbl;
+static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
{
return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
}
+static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
+{
+ return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
+}
+
+static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
+{
+ return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
+}
+
/* Following pulseaudio design here, mainloop has the lock taken whenever
* it is handling something for pulse, and the lock is required whenever
* doing any pa_* call that can affect the state in any way
@@ -701,6 +716,11 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev,
return E_OUTOFMEMORY;
This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
+ This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
+ This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
+ This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
+ This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
+ This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
This->dataflow = dataflow;
This->parent = dev;
This->clock_pulse = PA_USEC_INVALID;
@@ -1421,6 +1441,16 @@ static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
if (FAILED(hr))
return hr;
+ if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
+ if (This->dataflow != eRender)
+ return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
+ *ppv = &This->IAudioRenderClient_iface;
+ } else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) {
+ if (This->dataflow != eCapture)
+ return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
+ *ppv = &This->IAudioCaptureClient_iface;
+ }
+
if (*ppv) {
IUnknown_AddRef((IUnknown*)*ppv);
return S_OK;
@@ -1449,6 +1479,277 @@ static const IAudioClientVtbl AudioClient_Vtbl =
AudioClient_GetService
};
+static HRESULT WINAPI AudioRenderClient_QueryInterface(
+ IAudioRenderClient *iface, REFIID riid, void **ppv)
+{
+ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
+
+ if (!ppv)
+ return E_POINTER;
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown) ||
+ IsEqualIID(riid, &IID_IAudioRenderClient))
+ *ppv = iface;
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+
+ WARN("Unknown interface %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
+{
+ ACImpl *This = impl_from_IAudioRenderClient(iface);
+ return AudioClient_AddRef(&This->IAudioClient_iface);
+}
+
+static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
+{
+ ACImpl *This = impl_from_IAudioRenderClient(iface);
+ return AudioClient_Release(&This->IAudioClient_iface);
+}
+
+static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
+ UINT32 frames, BYTE **data)
+{
+ ACImpl *This = impl_from_IAudioRenderClient(iface);
+ UINT32 avail, pad, req, bytes = frames * pa_frame_size(&This->ss);
+ HRESULT hr = S_OK;
+ int ret = -1;
+
+ TRACE("(%p)->(%u, %p)\n", This, frames, data);
+
+ if (!data)
+ return E_POINTER;
+ *data = NULL;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr) || This->locked) {
+ pthread_mutex_unlock(&pulse_lock);
+ return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
+ }
+ if (!frames) {
+ pthread_mutex_unlock(&pulse_lock);
+ return S_OK;
+ }
+
+ ACImpl_GetRenderPad(This, &pad);
+ avail = This->bufsize_frames - pad;
+ if (avail < frames || bytes > This->bufsize_bytes) {
+ pthread_mutex_unlock(&pulse_lock);
+ WARN("Wanted to write %u, but only %u available\n", frames, avail);
+ return AUDCLNT_E_BUFFER_TOO_LARGE;
+ }
+
+ This->locked = frames;
+ req = bytes;
+ ret = pa_stream_begin_write(This->stream, &This->locked_ptr, &req);
+ if (ret < 0 || req < bytes) {
+ FIXME("%p Not using pulse locked data: %i %u/%u %u/%u\n", This, ret, req/pa_frame_size(&This->ss), frames, pad, This->bufsize_frames);
+ if (ret >= 0)
+ pa_stream_cancel_write(This->stream);
+ *data = This->tmp_buffer;
+ This->locked_ptr = NULL;
+ } else
+ *data = This->locked_ptr;
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+}
+
+static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
+ IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
+{
+ ACImpl *This = impl_from_IAudioRenderClient(iface);
+ UINT32 written_bytes = written_frames * pa_frame_size(&This->ss);
+
+ TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
+
+ pthread_mutex_lock(&pulse_lock);
+ if (!This->locked || !written_frames) {
+ if (This->locked_ptr)
+ pa_stream_cancel_write(This->stream);
+ This->locked = 0;
+ This->locked_ptr = NULL;
+ pthread_mutex_unlock(&pulse_lock);
+ return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
+ }
+
+ if (This->locked < written_frames) {
+ pthread_mutex_unlock(&pulse_lock);
+ return AUDCLNT_E_INVALID_SIZE;
+ }
+
+ if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
+ if (This->ss.format == PA_SAMPLE_U8)
+ memset(This->tmp_buffer, 128, written_bytes);
+ else
+ memset(This->tmp_buffer, 0, written_bytes);
+ }
+
+ This->locked = 0;
+ if (This->locked_ptr)
+ pa_stream_write(This->stream, This->locked_ptr, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
+ else
+ pa_stream_write(This->stream, This->tmp_buffer, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
+ This->pad += written_bytes;
+ This->locked_ptr = NULL;
+ TRACE("Released %u, pad %u\n", written_frames, This->pad / pa_frame_size(&This->ss));
+ assert(This->pad <= This->bufsize_bytes);
+ pthread_mutex_unlock(&pulse_lock);
+ return S_OK;
+}
+
+static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
+ AudioRenderClient_QueryInterface,
+ AudioRenderClient_AddRef,
+ AudioRenderClient_Release,
+ AudioRenderClient_GetBuffer,
+ AudioRenderClient_ReleaseBuffer
+};
+
+static HRESULT WINAPI AudioCaptureClient_QueryInterface(
+ IAudioCaptureClient *iface, REFIID riid, void **ppv)
+{
+ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
+
+ if (!ppv)
+ return E_POINTER;
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown) ||
+ IsEqualIID(riid, &IID_IAudioCaptureClient))
+ *ppv = iface;
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+
+ WARN("Unknown interface %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
+{
+ ACImpl *This = impl_from_IAudioCaptureClient(iface);
+ return IAudioClient_AddRef(&This->IAudioClient_iface);
+}
+
+static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
+{
+ ACImpl *This = impl_from_IAudioCaptureClient(iface);
+ return IAudioClient_Release(&This->IAudioClient_iface);
+}
+
+static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
+ BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
+ UINT64 *qpcpos)
+{
+ ACImpl *This = impl_from_IAudioCaptureClient(iface);
+ HRESULT hr;
+ ACPacket *packet;
+
+ TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
+ devpos, qpcpos);
+
+ if (!data || !frames || !flags)
+ return E_POINTER;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr) || This->locked) {
+ pthread_mutex_unlock(&pulse_lock);
+ return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
+ }
+
+ ACImpl_GetCapturePad(This, NULL);
+ if ((packet = This->locked_ptr)) {
+ *frames = This->capture_period / pa_frame_size(&This->ss);
+ *flags = 0;
+ if (packet->discont)
+ *flags |= AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY;
+ if (devpos) {
+ if (packet->discont)
+ *devpos = (This->clock_written + This->capture_period) / pa_frame_size(&This->ss);
+ else
+ *devpos = This->clock_written / pa_frame_size(&This->ss);
+ }
+ if (qpcpos)
+ *qpcpos = packet->qpcpos;
+ *data = packet->data;
+ }
+ else
+ *frames = 0;
+ This->locked = *frames;
+ pthread_mutex_unlock(&pulse_lock);
+ return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
+}
+
+static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
+ IAudioCaptureClient *iface, UINT32 done)
+{
+ ACImpl *This = impl_from_IAudioCaptureClient(iface);
+
+ TRACE("(%p)->(%u)\n", This, done);
+
+ pthread_mutex_lock(&pulse_lock);
+ if (!This->locked && done) {
+ pthread_mutex_unlock(&pulse_lock);
+ return AUDCLNT_E_OUT_OF_ORDER;
+ }
+ if (done && This->locked != done) {
+ pthread_mutex_unlock(&pulse_lock);
+ return AUDCLNT_E_INVALID_SIZE;
+ }
+ if (done) {
+ ACPacket *packet = This->locked_ptr;
+ This->locked_ptr = NULL;
+ This->pad -= This->capture_period;
+ if (packet->discont)
+ This->clock_written += 2 * This->capture_period;
+ else
+ This->clock_written += This->capture_period;
+ list_add_tail(&This->packet_free_head, &packet->entry);
+ }
+ This->locked = 0;
+ pthread_mutex_unlock(&pulse_lock);
+ return S_OK;
+}
+
+static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
+ IAudioCaptureClient *iface, UINT32 *frames)
+{
+ ACImpl *This = impl_from_IAudioCaptureClient(iface);
+ ACPacket *p;
+
+ TRACE("(%p)->(%p)\n", This, frames);
+ if (!frames)
+ return E_POINTER;
+
+ pthread_mutex_lock(&pulse_lock);
+ ACImpl_GetCapturePad(This, NULL);
+ p = This->locked_ptr;
+ if (p)
+ *frames = This->capture_period / pa_frame_size(&This->ss);
+ else
+ *frames = 0;
+ pthread_mutex_unlock(&pulse_lock);
+ return S_OK;
+}
+
+static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
+{
+ AudioCaptureClient_QueryInterface,
+ AudioCaptureClient_AddRef,
+ AudioCaptureClient_Release,
+ AudioCaptureClient_GetBuffer,
+ AudioCaptureClient_ReleaseBuffer,
+ AudioCaptureClient_GetNextPacketSize
+};
+
HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
IAudioSessionManager2 **out)
{
--
2.3.5
From f1caa03ef9d8df55d85385fd3e886d4dd97431fe Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:29 +0100
Subject: winepulse: Add IAudioClock and IAudioClock2
---
dlls/winepulse.drv/mmdevdrv.c | 172 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 172 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 01cfd25..3ed2288 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -167,6 +167,16 @@ static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
}
+static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
+{
+ return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
+}
+
+static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
+{
+ return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
+}
+
/* Following pulseaudio design here, mainloop has the lock taken whenever
* it is handling something for pulse, and the lock is required whenever
* doing any pa_* call that can affect the state in any way
@@ -1449,6 +1459,8 @@ static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
if (This->dataflow != eCapture)
return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
*ppv = &This->IAudioCaptureClient_iface;
+ } else if (IsEqualIID(riid, &IID_IAudioClock)) {
+ *ppv = &This->IAudioClock_iface;
}
if (*ppv) {
@@ -1750,6 +1762,166 @@ static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
AudioCaptureClient_GetNextPacketSize
};
+static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
+ REFIID riid, void **ppv)
+{
+ ACImpl *This = impl_from_IAudioClock(iface);
+
+ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
+
+ if (!ppv)
+ return E_POINTER;
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
+ *ppv = iface;
+ else if (IsEqualIID(riid, &IID_IAudioClock2))
+ *ppv = &This->IAudioClock2_iface;
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+
+ WARN("Unknown interface %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
+{
+ ACImpl *This = impl_from_IAudioClock(iface);
+ return IAudioClient_AddRef(&This->IAudioClient_iface);
+}
+
+static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
+{
+ ACImpl *This = impl_from_IAudioClock(iface);
+ return IAudioClient_Release(&This->IAudioClient_iface);
+}
+
+static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
+{
+ ACImpl *This = impl_from_IAudioClock(iface);
+ HRESULT hr;
+
+ TRACE("(%p)->(%p)\n", This, freq);
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (SUCCEEDED(hr))
+ *freq = This->ss.rate * pa_frame_size(&This->ss);
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+}
+
+static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
+ UINT64 *qpctime)
+{
+ ACImpl *This = impl_from_IAudioClock(iface);
+ pa_usec_t time;
+ HRESULT hr;
+
+ TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
+
+ if (!pos)
+ return E_POINTER;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr)) {
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+ }
+
+ *pos = This->clock_written;
+ if (This->clock_pulse != PA_USEC_INVALID && pa_stream_get_time(This->stream, &time) >= 0) {
+ UINT32 delta = pa_usec_to_bytes(time - This->clock_pulse, &This->ss);
+ if (delta < This->pad)
+ *pos += delta;
+ else
+ *pos += This->pad;
+ }
+
+ /* Make time never go backwards */
+ if (*pos < This->clock_lastpos)
+ *pos = This->clock_lastpos;
+ else
+ This->clock_lastpos = *pos;
+ pthread_mutex_unlock(&pulse_lock);
+
+ TRACE("%p Position: %u\n", This, (unsigned)*pos);
+
+ if (qpctime) {
+ LARGE_INTEGER stamp, freq;
+ QueryPerformanceCounter(&stamp);
+ QueryPerformanceFrequency(&freq);
+ *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
+ }
+
+ return S_OK;
+}
+
+static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
+ DWORD *chars)
+{
+ ACImpl *This = impl_from_IAudioClock(iface);
+
+ TRACE("(%p)->(%p)\n", This, chars);
+
+ if (!chars)
+ return E_POINTER;
+
+ *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
+
+ return S_OK;
+}
+
+static const IAudioClockVtbl AudioClock_Vtbl =
+{
+ AudioClock_QueryInterface,
+ AudioClock_AddRef,
+ AudioClock_Release,
+ AudioClock_GetFrequency,
+ AudioClock_GetPosition,
+ AudioClock_GetCharacteristics
+};
+
+static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
+ REFIID riid, void **ppv)
+{
+ ACImpl *This = impl_from_IAudioClock2(iface);
+ return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
+}
+
+static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
+{
+ ACImpl *This = impl_from_IAudioClock2(iface);
+ return IAudioClient_AddRef(&This->IAudioClient_iface);
+}
+
+static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
+{
+ ACImpl *This = impl_from_IAudioClock2(iface);
+ return IAudioClient_Release(&This->IAudioClient_iface);
+}
+
+static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
+ UINT64 *pos, UINT64 *qpctime)
+{
+ ACImpl *This = impl_from_IAudioClock2(iface);
+ HRESULT hr = AudioClock_GetPosition(&This->IAudioClock_iface, pos, qpctime);
+ if (SUCCEEDED(hr))
+ *pos /= pa_frame_size(&This->ss);
+ return hr;
+}
+
+static const IAudioClock2Vtbl AudioClock2_Vtbl =
+{
+ AudioClock2_QueryInterface,
+ AudioClock2_AddRef,
+ AudioClock2_Release,
+ AudioClock2_GetDevicePosition
+};
+
HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
IAudioSessionManager2 **out)
{
--
2.3.5
From f8c5f205d72a877fdcacc38eb48060ebcfe7f922 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:29 +0100
Subject: winepulse: Add audiostreamvolume
---
Pulse allows streams to set volume, but for various reasons it's
better off being disabled by default.
It can be enabled with HKCU\Software\Wine\Pulse\StreamVol=0x1
---
dlls/winepulse.drv/mmdevdrv.c | 236 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 236 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 3ed2288..b7414c2 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -177,6 +177,11 @@ static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
}
+static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
+{
+ return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
+}
+
/* Following pulseaudio design here, mainloop has the lock taken whenever
* it is handling something for pulse, and the lock is required whenever
* doing any pa_* call that can affect the state in any way
@@ -444,6 +449,12 @@ static void pulse_op_cb(pa_stream *s, int success, void *user) {
pthread_cond_signal(&pulse_cond);
}
+static void pulse_ctx_op_cb(pa_context *c, int success, void *user) {
+ TRACE("Success: %i\n", success);
+ *(int*)user = success;
+ pthread_cond_signal(&pulse_cond);
+}
+
static void pulse_attr_update(pa_stream *s, void *user) {
const pa_buffer_attr *attr = pa_stream_get_buffer_attr(s);
TRACE("New attributes or device moved:\n");
@@ -1461,6 +1472,8 @@ static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
*ppv = &This->IAudioCaptureClient_iface;
} else if (IsEqualIID(riid, &IID_IAudioClock)) {
*ppv = &This->IAudioClock_iface;
+ } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) {
+ *ppv = &This->IAudioStreamVolume_iface;
}
if (*ppv) {
@@ -1922,6 +1935,229 @@ static const IAudioClock2Vtbl AudioClock2_Vtbl =
AudioClock2_GetDevicePosition
};
+static HRESULT WINAPI AudioStreamVolume_QueryInterface(
+ IAudioStreamVolume *iface, REFIID riid, void **ppv)
+{
+ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
+
+ if (!ppv)
+ return E_POINTER;
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown) ||
+ IsEqualIID(riid, &IID_IAudioStreamVolume))
+ *ppv = iface;
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+
+ WARN("Unknown interface %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
+{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+ return IAudioClient_AddRef(&This->IAudioClient_iface);
+}
+
+static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
+{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+ return IAudioClient_Release(&This->IAudioClient_iface);
+}
+
+static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
+ IAudioStreamVolume *iface, UINT32 *out)
+{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+
+ TRACE("(%p)->(%p)\n", This, out);
+
+ if (!out)
+ return E_POINTER;
+
+ *out = This->ss.channels;
+
+ return S_OK;
+}
+
+struct pulse_info_cb_data {
+ UINT32 n;
+ float *levels;
+};
+
+static void pulse_sink_input_info_cb(pa_context *c, const pa_sink_input_info *info, int eol, void *data)
+{
+ struct pulse_info_cb_data *d = data;
+ int i;
+ if (eol)
+ return;
+ for (i = 0; i < d->n; ++i)
+ d->levels[i] = (float)info->volume.values[i] / (float)PA_VOLUME_NORM;
+ pthread_cond_signal(&pulse_cond);
+}
+
+static void pulse_source_info_cb(pa_context *c, const pa_source_info *info, int eol, void *data)
+{
+ struct pulse_info_cb_data *d = data;
+ int i;
+ if (eol)
+ return;
+ for (i = 0; i < d->n; ++i)
+ d->levels[i] = (float)info->volume.values[i] / (float)PA_VOLUME_NORM;
+ pthread_cond_signal(&pulse_cond);
+}
+
+static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
+ IAudioStreamVolume *iface, UINT32 count, const float *levels)
+{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+ pa_operation *o;
+ HRESULT hr;
+ int success = 0, i;
+ pa_cvolume cv;
+
+ TRACE("(%p)->(%d, %p)\n", This, count, levels);
+
+ if (!levels)
+ return E_POINTER;
+
+ if (count != This->ss.channels)
+ return E_INVALIDARG;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr))
+ goto out;
+
+ if (pulse_stream_volume) {
+ cv.channels = count;
+ for (i = 0; i < cv.channels; ++i)
+ cv.values[i] = levels[i] * (float)PA_VOLUME_NORM;
+ if (This->dataflow == eRender)
+ o = pa_context_set_sink_input_volume(pulse_ctx, pa_stream_get_index(This->stream), &cv, pulse_ctx_op_cb, &success);
+ else
+ o = pa_context_set_source_volume_by_index(pulse_ctx, pa_stream_get_device_index(This->stream), &cv, pulse_ctx_op_cb, &success);
+ if (o) {
+ while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ pa_operation_unref(o);
+ }
+ if (!success)
+ hr = AUDCLNT_E_BUFFER_ERROR;
+ } else {
+ int i;
+ for (i = 0; i < count; ++i)
+ This->vol[i] = levels[i];
+ }
+
+out:
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+}
+
+static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
+ IAudioStreamVolume *iface, UINT32 count, float *levels)
+{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+ pa_operation *o;
+ HRESULT hr;
+ struct pulse_info_cb_data info;
+
+ TRACE("(%p)->(%d, %p)\n", This, count, levels);
+
+ if (!levels)
+ return E_POINTER;
+
+ if (count != This->ss.channels)
+ return E_INVALIDARG;
+
+ pthread_mutex_lock(&pulse_lock);
+ hr = pulse_stream_valid(This);
+ if (FAILED(hr))
+ goto out;
+
+ if (pulse_stream_volume) {
+ info.n = count;
+ info.levels = levels;
+ if (This->dataflow == eRender)
+ o = pa_context_get_sink_input_info(pulse_ctx, pa_stream_get_index(This->stream), pulse_sink_input_info_cb, &info);
+ else
+ o = pa_context_get_source_info_by_index(pulse_ctx, pa_stream_get_device_index(This->stream), pulse_source_info_cb, &info);
+ if (o) {
+ while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ pa_operation_unref(o);
+ } else
+ hr = AUDCLNT_E_BUFFER_ERROR;
+ } else {
+ int i;
+ for (i = 0; i < count; ++i)
+ levels[i] = This->vol[i];
+ }
+
+out:
+ pthread_mutex_unlock(&pulse_lock);
+ return hr;
+}
+
+static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
+ IAudioStreamVolume *iface, UINT32 index, float level)
+{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+ HRESULT hr;
+ float volumes[PA_CHANNELS_MAX];
+
+ TRACE("(%p)->(%d, %f)\n", This, index, level);
+
+ if (level < 0.f || level > 1.f)
+ return E_INVALIDARG;
+
+ if (index >= This->ss.channels)
+ return E_INVALIDARG;
+
+ hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes);
+ volumes[index] = level;
+ if (SUCCEEDED(hr))
+ hr = AudioStreamVolume_SetAllVolumes(iface, This->ss.channels, volumes);
+ return hr;
+}
+
+static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
+ IAudioStreamVolume *iface, UINT32 index, float *level)
+{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+ float volumes[PA_CHANNELS_MAX];
+ HRESULT hr;
+
+ TRACE("(%p)->(%d, %p)\n", This, index, level);
+
+ if (!level)
+ return E_POINTER;
+
+ if (index >= This->ss.channels)
+ return E_INVALIDARG;
+
+ hr = AudioStreamVolume_GetAllVolumes(iface, This->ss.channels, volumes);
+ if (SUCCEEDED(hr))
+ *level = volumes[index];
+ return hr;
+}
+
+static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
+{
+ AudioStreamVolume_QueryInterface,
+ AudioStreamVolume_AddRef,
+ AudioStreamVolume_Release,
+ AudioStreamVolume_GetChannelCount,
+ AudioStreamVolume_SetChannelVolume,
+ AudioStreamVolume_GetChannelVolume,
+ AudioStreamVolume_SetAllVolumes,
+ AudioStreamVolume_GetAllVolumes
+};
+
HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
IAudioSessionManager2 **out)
{
--
2.3.5
From a51bea80adf8ea885943c4d2e9ff3c0f2260a5de Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:30 +0100
Subject: winepulse: Add session support
---
Copied verbatim from winealsa
---
dlls/winepulse.drv/mmdevdrv.c | 849 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 848 insertions(+), 1 deletion(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index b7414c2..64ee62e 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -69,6 +69,7 @@ static pa_mainloop *pulse_ml;
static HANDLE pulse_thread;
static pthread_mutex_t pulse_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER;
+static struct list g_sessions = LIST_INIT(g_sessions);
/* Mixer format + period times */
static WAVEFORMATEXTENSIBLE pulse_fmt[2];
@@ -105,6 +106,31 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
typedef struct ACImpl ACImpl;
+typedef struct _AudioSession {
+ GUID guid;
+ struct list clients;
+
+ IMMDevice *device;
+
+ float master_vol;
+ UINT32 channel_count;
+ float *channel_vols;
+ BOOL mute;
+
+ struct list entry;
+} AudioSession;
+
+typedef struct _AudioSessionWrapper {
+ IAudioSessionControl2 IAudioSessionControl2_iface;
+ IChannelAudioVolume IChannelAudioVolume_iface;
+ ISimpleAudioVolume ISimpleAudioVolume_iface;
+
+ LONG ref;
+
+ ACImpl *client;
+ AudioSession *session;
+} AudioSessionWrapper;
+
typedef struct _ACPacket {
struct list entry;
UINT64 qpcpos;
@@ -139,6 +165,8 @@ struct ACImpl {
INT64 clock_lastpos, clock_written;
pa_usec_t clock_pulse;
+ AudioSession *session;
+ AudioSessionWrapper *session_wrapper;
struct list packet_free_head;
struct list packet_filled_head;
};
@@ -148,10 +176,15 @@ static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0};
static const IAudioClientVtbl AudioClient_Vtbl;
static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
+static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
+static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
+static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
static const IAudioClockVtbl AudioClock_Vtbl;
static const IAudioClock2Vtbl AudioClock2_Vtbl;
static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
+static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
+
static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
{
return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
@@ -167,6 +200,21 @@ static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
}
+static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
+{
+ return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
+}
+
+static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
+{
+ return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
+}
+
+static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
+{
+ return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
+}
+
static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
{
return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
@@ -897,6 +945,85 @@ static DWORD get_channel_mask(unsigned int channels)
return 0;
}
+static void session_init_vols(AudioSession *session, UINT channels)
+{
+ if (session->channel_count < channels) {
+ UINT i;
+
+ if (session->channel_vols)
+ session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
+ session->channel_vols, sizeof(float) * channels);
+ else
+ session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
+ sizeof(float) * channels);
+ if (!session->channel_vols)
+ return;
+
+ for(i = session->channel_count; i < channels; ++i)
+ session->channel_vols[i] = 1.f;
+
+ session->channel_count = channels;
+ }
+}
+
+static AudioSession *create_session(const GUID *guid, IMMDevice *device,
+ UINT num_channels)
+{
+ AudioSession *ret;
+
+ ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
+ if (!ret)
+ return NULL;
+
+ memcpy(&ret->guid, guid, sizeof(GUID));
+
+ ret->device = device;
+
+ list_init(&ret->clients);
+
+ list_add_head(&g_sessions, &ret->entry);
+
+ session_init_vols(ret, num_channels);
+
+ ret->master_vol = 1.f;
+
+ return ret;
+}
+
+/* if channels == 0, then this will return or create a session with
+ * matching dataflow and GUID. otherwise, channels must also match */
+static HRESULT get_audio_session(const GUID *sessionguid,
+ IMMDevice *device, UINT channels, AudioSession **out)
+{
+ AudioSession *session;
+
+ if (!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)) {
+ *out = create_session(&GUID_NULL, device, channels);
+ if (!*out)
+ return E_OUTOFMEMORY;
+
+ return S_OK;
+ }
+
+ *out = NULL;
+ LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry) {
+ if (session->device == device &&
+ IsEqualGUID(sessionguid, &session->guid)) {
+ session_init_vols(session, channels);
+ *out = session;
+ break;
+ }
+ }
+
+ if (!*out) {
+ *out = create_session(sessionguid, device, channels);
+ if (!*out)
+ return E_OUTOFMEMORY;
+ }
+
+ return S_OK;
+}
+
static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
{
pa_channel_map_init(&This->map);
@@ -1083,6 +1210,10 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
assert(!capture_packets || data - This->bufsize_bytes == This->tmp_buffer);
}
}
+ if (SUCCEEDED(hr))
+ hr = get_audio_session(sessionguid, This->parent, fmt->nChannels, &This->session);
+ if (SUCCEEDED(hr))
+ list_add_tail(&This->session->clients, &This->entry);
exit:
if (FAILED(hr)) {
@@ -1474,6 +1605,20 @@ static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
*ppv = &This->IAudioClock_iface;
} else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) {
*ppv = &This->IAudioStreamVolume_iface;
+ } else if (IsEqualIID(riid, &IID_IAudioSessionControl) ||
+ IsEqualIID(riid, &IID_IChannelAudioVolume) ||
+ IsEqualIID(riid, &IID_ISimpleAudioVolume)) {
+ if (!This->session_wrapper) {
+ This->session_wrapper = AudioSessionWrapper_Create(This);
+ if (!This->session_wrapper)
+ return E_OUTOFMEMORY;
+ }
+ if (IsEqualIID(riid, &IID_IAudioSessionControl))
+ *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
+ else if (IsEqualIID(riid, &IID_IChannelAudioVolume))
+ *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
+ else if (IsEqualIID(riid, &IID_ISimpleAudioVolume))
+ *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
}
if (*ppv) {
@@ -2158,9 +2303,711 @@ static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
AudioStreamVolume_GetAllVolumes
};
+static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
+{
+ AudioSessionWrapper *ret;
+
+ ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+ sizeof(AudioSessionWrapper));
+ if (!ret)
+ return NULL;
+
+ ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
+ ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
+ ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
+
+ ret->ref = !client;
+
+ ret->client = client;
+ if (client) {
+ ret->session = client->session;
+ AudioClient_AddRef(&client->IAudioClient_iface);
+ }
+
+ return ret;
+}
+
+static HRESULT WINAPI AudioSessionControl_QueryInterface(
+ IAudioSessionControl2 *iface, REFIID riid, void **ppv)
+{
+ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
+
+ if (!ppv)
+ return E_POINTER;
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown) ||
+ IsEqualIID(riid, &IID_IAudioSessionControl) ||
+ IsEqualIID(riid, &IID_IAudioSessionControl2))
+ *ppv = iface;
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+
+ WARN("Unknown interface %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+ ULONG ref;
+ ref = InterlockedIncrement(&This->ref);
+ TRACE("(%p) Refcount now %u\n", This, ref);
+ return ref;
+}
+
+static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+ ULONG ref;
+ ref = InterlockedDecrement(&This->ref);
+ TRACE("(%p) Refcount now %u\n", This, ref);
+ if (!ref) {
+ if (This->client) {
+ This->client->session_wrapper = NULL;
+ AudioClient_Release(&This->client->IAudioClient_iface);
+ }
+ HeapFree(GetProcessHeap(), 0, This);
+ }
+ return ref;
+}
+
+static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
+ AudioSessionState *state)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+ ACImpl *client;
+
+ TRACE("(%p)->(%p)\n", This, state);
+
+ if (!state)
+ return NULL_PTR_ERR;
+
+ pthread_mutex_lock(&pulse_lock);
+ if (list_empty(&This->session->clients)) {
+ *state = AudioSessionStateExpired;
+ goto out;
+ }
+ LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry) {
+ if (client->started) {
+ *state = AudioSessionStateActive;
+ goto out;
+ }
+ }
+ *state = AudioSessionStateInactive;
+
+out:
+ pthread_mutex_unlock(&pulse_lock);
+ return S_OK;
+}
+
+static HRESULT WINAPI AudioSessionControl_GetDisplayName(
+ IAudioSessionControl2 *iface, WCHAR **name)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+
+ FIXME("(%p)->(%p) - stub\n", This, name);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI AudioSessionControl_SetDisplayName(
+ IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+
+ FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI AudioSessionControl_GetIconPath(
+ IAudioSessionControl2 *iface, WCHAR **path)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+
+ FIXME("(%p)->(%p) - stub\n", This, path);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI AudioSessionControl_SetIconPath(
+ IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+
+ FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
+ IAudioSessionControl2 *iface, GUID *group)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+
+ FIXME("(%p)->(%p) - stub\n", This, group);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
+ IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+
+ FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
+ debugstr_guid(session));
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
+ IAudioSessionControl2 *iface, IAudioSessionEvents *events)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+
+ FIXME("(%p)->(%p) - stub\n", This, events);
+
+ return S_OK;
+}
+
+static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
+ IAudioSessionControl2 *iface, IAudioSessionEvents *events)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+
+ FIXME("(%p)->(%p) - stub\n", This, events);
+
+ return S_OK;
+}
+
+static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
+ IAudioSessionControl2 *iface, WCHAR **id)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+
+ FIXME("(%p)->(%p) - stub\n", This, id);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
+ IAudioSessionControl2 *iface, WCHAR **id)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+
+ FIXME("(%p)->(%p) - stub\n", This, id);
+
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI AudioSessionControl_GetProcessId(
+ IAudioSessionControl2 *iface, DWORD *pid)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+
+ TRACE("(%p)->(%p)\n", This, pid);
+
+ if (!pid)
+ return E_POINTER;
+
+ *pid = GetCurrentProcessId();
+
+ return S_OK;
+}
+
+static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
+ IAudioSessionControl2 *iface)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+
+ TRACE("(%p)\n", This);
+
+ return S_FALSE;
+}
+
+static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
+ IAudioSessionControl2 *iface, BOOL optout)
+{
+ AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
+
+ TRACE("(%p)->(%d)\n", This, optout);
+
+ return S_OK;
+}
+
+static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
+{
+ AudioSessionControl_QueryInterface,
+ AudioSessionControl_AddRef,
+ AudioSessionControl_Release,
+ AudioSessionControl_GetState,
+ AudioSessionControl_GetDisplayName,
+ AudioSessionControl_SetDisplayName,
+ AudioSessionControl_GetIconPath,
+ AudioSessionControl_SetIconPath,
+ AudioSessionControl_GetGroupingParam,
+ AudioSessionControl_SetGroupingParam,
+ AudioSessionControl_RegisterAudioSessionNotification,
+ AudioSessionControl_UnregisterAudioSessionNotification,
+ AudioSessionControl_GetSessionIdentifier,
+ AudioSessionControl_GetSessionInstanceIdentifier,
+ AudioSessionControl_GetProcessId,
+ AudioSessionControl_IsSystemSoundsSession,
+ AudioSessionControl_SetDuckingPreference
+};
+
+typedef struct _SessionMgr {
+ IAudioSessionManager2 IAudioSessionManager2_iface;
+
+ LONG ref;
+
+ IMMDevice *device;
+} SessionMgr;
+
+static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
+ REFIID riid, void **ppv)
+{
+ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
+
+ if (!ppv)
+ return E_POINTER;
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown) ||
+ IsEqualIID(riid, &IID_IAudioSessionManager) ||
+ IsEqualIID(riid, &IID_IAudioSessionManager2))
+ *ppv = iface;
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+
+ WARN("Unknown interface %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
+{
+ return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
+}
+
+static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
+{
+ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
+ ULONG ref;
+ ref = InterlockedIncrement(&This->ref);
+ TRACE("(%p) Refcount now %u\n", This, ref);
+ return ref;
+}
+
+static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
+{
+ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
+ ULONG ref;
+ ref = InterlockedDecrement(&This->ref);
+ TRACE("(%p) Refcount now %u\n", This, ref);
+ if (!ref)
+ HeapFree(GetProcessHeap(), 0, This);
+ return ref;
+}
+
+static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
+ IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
+ IAudioSessionControl **out)
+{
+ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
+ AudioSession *session;
+ AudioSessionWrapper *wrapper;
+ HRESULT hr;
+
+ TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
+ flags, out);
+
+ hr = get_audio_session(session_guid, This->device, 0, &session);
+ if (FAILED(hr))
+ return hr;
+
+ wrapper = AudioSessionWrapper_Create(NULL);
+ if (!wrapper)
+ return E_OUTOFMEMORY;
+
+ wrapper->session = session;
+
+ *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
+ IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
+ ISimpleAudioVolume **out)
+{
+ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
+ AudioSession *session;
+ AudioSessionWrapper *wrapper;
+ HRESULT hr;
+
+ TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
+ flags, out);
+
+ hr = get_audio_session(session_guid, This->device, 0, &session);
+ if (FAILED(hr))
+ return hr;
+
+ wrapper = AudioSessionWrapper_Create(NULL);
+ if (!wrapper)
+ return E_OUTOFMEMORY;
+
+ wrapper->session = session;
+
+ *out = &wrapper->ISimpleAudioVolume_iface;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
+ IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
+{
+ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
+ FIXME("(%p)->(%p) - stub\n", This, out);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
+ IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
+{
+ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
+ FIXME("(%p)->(%p) - stub\n", This, notification);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
+ IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
+{
+ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
+ FIXME("(%p)->(%p) - stub\n", This, notification);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
+ IAudioSessionManager2 *iface, const WCHAR *session_id,
+ IAudioVolumeDuckNotification *notification)
+{
+ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
+ FIXME("(%p)->(%p) - stub\n", This, notification);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
+ IAudioSessionManager2 *iface,
+ IAudioVolumeDuckNotification *notification)
+{
+ SessionMgr *This = impl_from_IAudioSessionManager2(iface);
+ FIXME("(%p)->(%p) - stub\n", This, notification);
+ return E_NOTIMPL;
+}
+
+static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
+{
+ AudioSessionManager_QueryInterface,
+ AudioSessionManager_AddRef,
+ AudioSessionManager_Release,
+ AudioSessionManager_GetAudioSessionControl,
+ AudioSessionManager_GetSimpleAudioVolume,
+ AudioSessionManager_GetSessionEnumerator,
+ AudioSessionManager_RegisterSessionNotification,
+ AudioSessionManager_UnregisterSessionNotification,
+ AudioSessionManager_RegisterDuckNotification,
+ AudioSessionManager_UnregisterDuckNotification
+};
+
+static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
+ ISimpleAudioVolume *iface, REFIID riid, void **ppv)
+{
+ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
+
+ if (!ppv)
+ return E_POINTER;
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown) ||
+ IsEqualIID(riid, &IID_ISimpleAudioVolume))
+ *ppv = iface;
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+
+ WARN("Unknown interface %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
+{
+ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
+ return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
+}
+
+static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
+{
+ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
+ return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
+}
+
+static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
+ ISimpleAudioVolume *iface, float level, const GUID *context)
+{
+ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
+ AudioSession *session = This->session;
+
+ TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
+
+ if (level < 0.f || level > 1.f)
+ return E_INVALIDARG;
+
+ if (context)
+ FIXME("Notifications not supported yet\n");
+
+ TRACE("Pulseaudio does not support session volume control\n");
+
+ pthread_mutex_lock(&pulse_lock);
+ session->master_vol = level;
+ pthread_mutex_unlock(&pulse_lock);
+
+ return S_OK;
+}
+
+static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
+ ISimpleAudioVolume *iface, float *level)
+{
+ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
+ AudioSession *session = This->session;
+
+ TRACE("(%p)->(%p)\n", session, level);
+
+ if (!level)
+ return NULL_PTR_ERR;
+
+ *level = session->master_vol;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
+ BOOL mute, const GUID *context)
+{
+ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
+ AudioSession *session = This->session;
+
+ TRACE("(%p)->(%u, %p)\n", session, mute, context);
+
+ if (context)
+ FIXME("Notifications not supported yet\n");
+
+ session->mute = mute;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
+ BOOL *mute)
+{
+ AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
+ AudioSession *session = This->session;
+
+ TRACE("(%p)->(%p)\n", session, mute);
+
+ if (!mute)
+ return NULL_PTR_ERR;
+
+ *mute = session->mute;
+
+ return S_OK;
+}
+
+static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
+{
+ SimpleAudioVolume_QueryInterface,
+ SimpleAudioVolume_AddRef,
+ SimpleAudioVolume_Release,
+ SimpleAudioVolume_SetMasterVolume,
+ SimpleAudioVolume_GetMasterVolume,
+ SimpleAudioVolume_SetMute,
+ SimpleAudioVolume_GetMute
+};
+
+static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
+ IChannelAudioVolume *iface, REFIID riid, void **ppv)
+{
+ TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
+
+ if (!ppv)
+ return E_POINTER;
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, &IID_IUnknown) ||
+ IsEqualIID(riid, &IID_IChannelAudioVolume))
+ *ppv = iface;
+ if (*ppv) {
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+ }
+
+ WARN("Unknown interface %s\n", debugstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
+{
+ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
+ return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
+}
+
+static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
+{
+ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
+ return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
+}
+
+static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
+ IChannelAudioVolume *iface, UINT32 *out)
+{
+ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
+ AudioSession *session = This->session;
+
+ TRACE("(%p)->(%p)\n", session, out);
+
+ if (!out)
+ return NULL_PTR_ERR;
+
+ *out = session->channel_count;
+
+ return S_OK;
+}
+
+static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
+ IChannelAudioVolume *iface, UINT32 index, float level,
+ const GUID *context)
+{
+ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
+ AudioSession *session = This->session;
+
+ TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
+ wine_dbgstr_guid(context));
+
+ if (level < 0.f || level > 1.f)
+ return E_INVALIDARG;
+
+ if (index >= session->channel_count)
+ return E_INVALIDARG;
+
+ if (context)
+ FIXME("Notifications not supported yet\n");
+
+ TRACE("Pulseaudio does not support session volume control\n");
+
+ pthread_mutex_lock(&pulse_lock);
+ session->channel_vols[index] = level;
+ pthread_mutex_unlock(&pulse_lock);
+
+ return S_OK;
+}
+
+static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
+ IChannelAudioVolume *iface, UINT32 index, float *level)
+{
+ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
+ AudioSession *session = This->session;
+
+ TRACE("(%p)->(%d, %p)\n", session, index, level);
+
+ if (!level)
+ return NULL_PTR_ERR;
+
+ if (index >= session->channel_count)
+ return E_INVALIDARG;
+
+ *level = session->channel_vols[index];
+
+ return S_OK;
+}
+
+static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
+ IChannelAudioVolume *iface, UINT32 count, const float *levels,
+ const GUID *context)
+{
+ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
+ AudioSession *session = This->session;
+ int i;
+
+ TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
+ wine_dbgstr_guid(context));
+
+ if (!levels)
+ return NULL_PTR_ERR;
+
+ if (count != session->channel_count)
+ return E_INVALIDARG;
+
+ if (context)
+ FIXME("Notifications not supported yet\n");
+
+ TRACE("Pulseaudio does not support session volume control\n");
+
+ pthread_mutex_lock(&pulse_lock);
+ for(i = 0; i < count; ++i)
+ session->channel_vols[i] = levels[i];
+ pthread_mutex_unlock(&pulse_lock);
+ return S_OK;
+}
+
+static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
+ IChannelAudioVolume *iface, UINT32 count, float *levels)
+{
+ AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
+ AudioSession *session = This->session;
+ int i;
+
+ TRACE("(%p)->(%d, %p)\n", session, count, levels);
+
+ if (!levels)
+ return NULL_PTR_ERR;
+
+ if (count != session->channel_count)
+ return E_INVALIDARG;
+
+ for(i = 0; i < count; ++i)
+ levels[i] = session->channel_vols[i];
+
+ return S_OK;
+}
+
+static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
+{
+ ChannelAudioVolume_QueryInterface,
+ ChannelAudioVolume_AddRef,
+ ChannelAudioVolume_Release,
+ ChannelAudioVolume_GetChannelCount,
+ ChannelAudioVolume_SetChannelVolume,
+ ChannelAudioVolume_GetChannelVolume,
+ ChannelAudioVolume_SetAllVolumes,
+ ChannelAudioVolume_GetAllVolumes
+};
+
HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
IAudioSessionManager2 **out)
{
+ SessionMgr *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
*out = NULL;
- return E_NOTIMPL;
+ if (!This)
+ return E_OUTOFMEMORY;
+ This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
+ This->device = device;
+ This->ref = 1;
+ *out = &This->IAudioSessionManager2_iface;
+ return S_OK;
}
--
2.3.5
From 7b16daf65cd65baed1fffaf329515bcfbfaa3ef9 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:30 +0100
Subject: fix fdels trailing whitespaces
Happy? :P
---
dlls/winepulse.drv/mmdevdrv.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 64ee62e..5a71a3d 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -1898,7 +1898,7 @@ static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
TRACE("(%p)->(%p)\n", This, frames);
if (!frames)
return E_POINTER;
-
+
pthread_mutex_lock(&pulse_lock);
ACImpl_GetCapturePad(This, NULL);
p = This->locked_ptr;
@@ -1998,7 +1998,7 @@ static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
else
*pos += This->pad;
}
-
+
/* Make time never go backwards */
if (*pos < This->clock_lastpos)
*pos = This->clock_lastpos;
--
2.3.5
From ac48dcb8a17a6be7e6459697173a086cc3557743 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:30 +0100
Subject: winepulse v12
Changes since v11:
- Fix incorrect assertions which may fail on moving a capture device
- Whitespace apply fixes
Changes since v10:
- Make some members static
- Fix small memory leak in GetService
---
dlls/winepulse.drv/mmdevdrv.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 5a71a3d..960af3c 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -596,10 +596,11 @@ static void pulse_rd_loop(ACImpl *This, size_t bytes)
dst = p->data;
while (rem) {
pa_stream_peek(This->stream, (const void**)&src, &src_len);
- assert(src_len && src_len <= bytes);
+ assert(src_len);
assert(This->peek_ofs < src_len);
src += This->peek_ofs;
src_len -= This->peek_ofs;
+ assert(src_len <= bytes);
copy = rem;
if (copy > src_len)
@@ -627,9 +628,10 @@ static void pulse_rd_drop(ACImpl *This, size_t bytes)
while (rem) {
const void *src;
pa_stream_peek(This->stream, &src, &src_len);
- assert(src_len && src_len <= bytes);
+ assert(src_len);
assert(This->peek_ofs < src_len);
src_len -= This->peek_ofs;
+ assert(src_len <= bytes);
copy = rem;
if (copy > src_len)
--
2.3.5
From 258d4d96f173b2617cd8dbc1081116902730f1eb Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:30 +0100
Subject: winepulse v15: Add support for missing formats, and silence an error
for missing format tags
---
dlls/winepulse.drv/mmdevdrv.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 960af3c..f52f119 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -1109,7 +1109,22 @@ static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
}
break;
}
- default: FIXME("Unhandled tag %x\n", fmt->wFormatTag);
+ case WAVE_FORMAT_ALAW:
+ case WAVE_FORMAT_MULAW:
+ if (fmt->wBitsPerSample != 8) {
+ FIXME("Unsupported bpp %u for LAW\n", fmt->wBitsPerSample);
+ return AUDCLNT_E_UNSUPPORTED_FORMAT;
+ }
+ if (fmt->nChannels != 1 && fmt->nChannels != 2) {
+ FIXME("Unsupported channels %u for LAW\n", fmt->nChannels);
+ return AUDCLNT_E_UNSUPPORTED_FORMAT;
+ }
+ This->ss.format = fmt->wFormatTag == WAVE_FORMAT_MULAW ? PA_SAMPLE_ULAW : PA_SAMPLE_ALAW;
+ pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
+ break;
+ default:
+ WARN("Unhandled tag %x\n", fmt->wFormatTag);
+ return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
This->ss.channels = This->map.channels;
if (!pa_channel_map_valid(&This->map) || This->ss.format == PA_SAMPLE_INVALID) {
--
2.3.5
From 2713c69df30327f009a9da1277e02fbe2cce45b9 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:30 +0100
Subject: winepulse v16: Add official warning wine doesn't want to support
winepulse
And give an alternative place to ask for support.
I wish it didn't have to come to this.
---
dlls/winepulse.drv/mmdevdrv.c | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index f52f119..76a2e0e 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -59,6 +59,7 @@
#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
WINE_DEFAULT_DEBUG_CHANNEL(pulse);
+WINE_DECLARE_DEBUG_CHANNEL(winediag);
static const REFERENCE_TIME MinimumPeriod = 30000;
static const REFERENCE_TIME DefaultPeriod = 100000;
@@ -81,6 +82,8 @@ 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 HANDLE warn_once;
+
BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
{
if (reason == DLL_PROCESS_ATTACH) {
@@ -99,7 +102,10 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
}
if (pulse_ml)
pa_mainloop_quit(pulse_ml, 0);
- CloseHandle(pulse_thread);
+ if (pulse_thread)
+ CloseHandle(pulse_thread);
+ if (warn_once)
+ CloseHandle(warn_once);
}
return TRUE;
}
@@ -758,6 +764,10 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys,
int WINAPI AUDDRV_GetPriority(void)
{
HRESULT hr;
+ if (getenv("WINENOPULSE")) {
+ FIXME_(winediag)("winepulse has been temporarily disabled through the environment\n");
+ return 0;
+ }
pthread_mutex_lock(&pulse_lock);
hr = pulse_connect();
pthread_mutex_unlock(&pulse_lock);
@@ -771,7 +781,18 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev,
ACImpl *This;
int i;
- TRACE("%p %p %d %p\n", key, dev, dataflow, out);
+ /* Give one visible warning per session
+ * Sadly wine has chosen not to accept the winepulse patch, so support ourselves
+ */
+ if (!warn_once && (warn_once = CreateEventA(0, 0, 0, "__winepulse_warn_event")) && GetLastError() != ERROR_ALREADY_EXISTS) {
+ FIXME_(winediag)("Winepulse is not officially supported by the wine project\n");
+ FIXME_(winediag)("For sound related feedback and support, please visit http://ubuntuforums.org/showthread.php?t=1960599\n");
+ } else {
+ WARN_(winediag)("Winepulse is not officially supported by the wine project\n");
+ WARN_(winediag)("For sound related feedback and support, please visit http://ubuntuforums.org/showthread.php?t=1960599\n");
+ }
+
+ TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
if (dataflow != eRender && dataflow != eCapture)
return E_UNEXPECTED;
--
2.3.5
From f862357622e7e4c6ddff3fb8e43aaaf050d66323 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <m.b.lankhorst@gmail.com>
Date: Mon, 23 Mar 2015 09:14:30 +0100
Subject: winepulse v17: Fix winmm tests
Handle dwChannelMask = SPEAKER_ALL better so WAVE_FORMAT_EXTENSIBLE tests pass too
---
dlls/winepulse.drv/mmdevdrv.c | 72 +++++++++++++++++++++++++++++++++----------
1 file changed, 56 insertions(+), 16 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 76a2e0e..6e75674 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -1066,6 +1066,8 @@ static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
This->ss.format = PA_SAMPLE_U8;
else if (fmt->wBitsPerSample == 16)
This->ss.format = PA_SAMPLE_S16LE;
+ else
+ return AUDCLNT_E_UNSUPPORTED_FORMAT;
pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
break;
case WAVE_FORMAT_EXTENSIBLE: {
@@ -1102,13 +1104,16 @@ static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
This->ss.format = PA_SAMPLE_S24_32LE;
else if (valid == 32)
This->ss.format = PA_SAMPLE_S32LE;
- default:
break;
+ default:
+ return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
}
This->map.channels = fmt->nChannels;
- if (!mask)
+ if (!mask || mask == SPEAKER_ALL)
mask = get_channel_mask(fmt->nChannels);
+ else if (mask == ~0U && fmt->nChannels == 1)
+ mask = SPEAKER_FRONT_CENTER;
for (j = 0; j < sizeof(pulse_pos_from_wfx)/sizeof(*pulse_pos_from_wfx) && i < fmt->nChannels; ++j) {
if (mask & (1 << j))
This->map.map[i++] = pulse_pos_from_wfx[j];
@@ -1118,14 +1123,9 @@ static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
if (mask == SPEAKER_FRONT_CENTER)
This->map.map[0] = PA_CHANNEL_POSITION_MONO;
- if ((mask & SPEAKER_ALL) && i < fmt->nChannels) {
- This->map.map[i++] = PA_CHANNEL_POSITION_MONO;
- FIXME("Is the 'all' channel mapped correctly?\n");
- }
-
if (i < fmt->nChannels || (mask & SPEAKER_RESERVED)) {
This->map.channels = 0;
- ERR("Invalid channel mask: %i/%i and %x\n", i, fmt->nChannels, mask);
+ ERR("Invalid channel mask: %i/%i and %x(%x)\n", i, fmt->nChannels, mask, wfe->dwChannelMask);
break;
}
break;
@@ -1383,15 +1383,55 @@ static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
return E_INVALIDARG;
if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
- if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
- fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
- return E_INVALIDARG;
-
- dump_fmt(fmt);
-
+ switch (fmt->wFormatTag) {
+ case WAVE_FORMAT_EXTENSIBLE:
+ if (fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
+ return E_INVALIDARG;
+ dump_fmt(fmt);
+ break;
+ case WAVE_FORMAT_ALAW:
+ case WAVE_FORMAT_MULAW:
+ case WAVE_FORMAT_IEEE_FLOAT:
+ case WAVE_FORMAT_PCM:
+ dump_fmt(fmt);
+ break;
+ default:
+ dump_fmt(fmt);
+ return AUDCLNT_E_UNSUPPORTED_FORMAT;
+ }
+ if (fmt->nChannels == 0)
+ return AUDCLNT_E_UNSUPPORTED_FORMAT;
closest = clone_format(fmt);
- if (!closest)
- hr = E_OUTOFMEMORY;
+ if (!closest) {
+ if (out)
+ *out = NULL;
+ return E_OUTOFMEMORY;
+ }
+
+ if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
+ UINT32 mask = 0, i, channels = 0;
+ WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)closest;
+
+ if ((fmt->nChannels > 1 && ext->dwChannelMask == SPEAKER_ALL) ||
+ (fmt->nChannels == 1 && ext->dwChannelMask == ~0U)) {
+ mask = ext->dwChannelMask;
+ channels = fmt->nChannels;
+ } else if (ext->dwChannelMask) {
+ for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) {
+ if (i & ext->dwChannelMask) {
+ mask |= i;
+ channels++;
+ }
+ }
+ if (channels < fmt->nChannels)
+ mask = get_channel_mask(fmt->nChannels);
+ } else
+ mask = ext->dwChannelMask;
+ if (ext->dwChannelMask != mask) {
+ ext->dwChannelMask = mask;
+ hr = S_FALSE;
+ }
+ }
if (hr == S_OK || !out) {
CoTaskMemFree(closest);
--
2.3.5
From 0ebb3ae5b960a141661e39540f646e175880fa6c Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:30 +0100
Subject: winepulse v18: Latency and compilation improvements
Changes since v17:
- Remove clock_pulse interpolation
* Couldn't work, sadly
- Allow 2 * MinimumPeriod for shared buffers
- Fix all compiler warnings when compiling with 64-bits
- Dynamically select low latency mode if less than 2 default periods are request
* This requires the rtkit patch to be useful
---
dlls/winepulse.drv/mmdevdrv.c | 55 +++++++++++++++++--------------------------
1 file changed, 22 insertions(+), 33 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 6e75674..8e76826 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -169,7 +169,6 @@ struct ACImpl {
pa_channel_map map;
INT64 clock_lastpos, clock_written;
- pa_usec_t clock_pulse;
AudioSession *session;
AudioSessionWrapper *session_wrapper;
@@ -518,7 +517,6 @@ static void pulse_attr_update(pa_stream *s, void *user) {
static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata)
{
ACImpl *This = userdata;
- pa_usec_t time;
UINT32 oldpad = This->pad;
if (bytes < This->bufsize_bytes)
@@ -528,13 +526,8 @@ static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata)
assert(oldpad >= This->pad);
- if (0 && This->pad && pa_stream_get_time(This->stream, &time) >= 0)
- This->clock_pulse = time;
- else
- This->clock_pulse = PA_USEC_INVALID;
-
This->clock_written += oldpad - This->pad;
- TRACE("New pad: %u (-%u)\n", This->pad / pa_frame_size(&This->ss), (oldpad - This->pad) / pa_frame_size(&This->ss));
+ TRACE("New pad: %zu (-%zu)\n", This->pad / pa_frame_size(&This->ss), (oldpad - This->pad) / pa_frame_size(&This->ss));
if (This->event)
SetEvent(This->event);
@@ -542,8 +535,6 @@ static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata)
static void pulse_underflow_callback(pa_stream *s, void *userdata)
{
- ACImpl *This = userdata;
- This->clock_pulse = PA_USEC_INVALID;
WARN("Underflow\n");
}
@@ -562,12 +553,8 @@ static void pulse_latency_callback(pa_stream *s, void *userdata)
static void pulse_started_callback(pa_stream *s, void *userdata)
{
ACImpl *This = userdata;
- pa_usec_t time;
TRACE("(Re)started playing\n");
- assert(This->clock_pulse == PA_USEC_INVALID);
- if (0 && pa_stream_get_time(This->stream, &time) >= 0)
- This->clock_pulse = time;
if (This->event)
SetEvent(This->event);
}
@@ -578,7 +565,7 @@ static void pulse_rd_loop(ACImpl *This, size_t bytes)
ACPacket *p, *next;
LARGE_INTEGER stamp, freq;
BYTE *dst, *src;
- UINT32 src_len, copy, rem = This->capture_period;
+ size_t src_len, copy, rem = This->capture_period;
if (!(p = (ACPacket*)list_head(&This->packet_free_head))) {
p = (ACPacket*)list_head(&This->packet_filled_head);
if (!p->discont) {
@@ -630,7 +617,7 @@ static void pulse_rd_loop(ACImpl *This, size_t bytes)
static void pulse_rd_drop(ACImpl *This, size_t bytes)
{
while (bytes >= This->capture_period) {
- UINT32 src_len, copy, rem = This->capture_period;
+ size_t src_len, copy, rem = This->capture_period;
while (rem) {
const void *src;
pa_stream_peek(This->stream, &src, &src_len);
@@ -660,7 +647,7 @@ static void pulse_rd_callback(pa_stream *s, size_t bytes, void *userdata)
{
ACImpl *This = userdata;
- TRACE("Readable total: %u, fragsize: %u\n", bytes, pa_stream_get_buffer_attr(s)->fragsize);
+ TRACE("Readable total: %zu, fragsize: %u\n", bytes, pa_stream_get_buffer_attr(s)->fragsize);
assert(bytes >= This->peek_ofs);
bytes -= This->peek_ofs;
if (bytes < This->capture_period)
@@ -815,7 +802,6 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev,
This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
This->dataflow = dataflow;
This->parent = dev;
- This->clock_pulse = PA_USEC_INVALID;
for (i = 0; i < PA_CHANNELS_MAX; ++i)
This->vol[i] = 1.f;
IMMDevice_AddRef(This->parent);
@@ -1199,7 +1185,19 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
goto exit;
if (mode == AUDCLNT_SHAREMODE_SHARED) {
- period = pulse_def_period[This->dataflow == eCapture];
+ REFERENCE_TIME def = pulse_def_period[This->dataflow == eCapture];
+ REFERENCE_TIME min = pulse_min_period[This->dataflow == eCapture];
+
+ /* Switch to low latency mode if below 2 default periods,
+ * which is 20 ms by default, this will increase the amount
+ * of interrupts but allows very low latency. In dsound I
+ * managed to get a total latency of ~8ms, which is well below
+ * default
+ */
+ if (duration < 2 * def)
+ period = min;
+ else
+ period = def;
if (duration < 2 * period)
duration = 2 * period;
}
@@ -1510,7 +1508,6 @@ static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
pthread_mutex_unlock(&pulse_lock);
return AUDCLNT_E_NOT_STOPPED;
}
- This->clock_pulse = PA_USEC_INVALID;
if (pa_stream_is_corked(This->stream)) {
o = pa_stream_cork(This->stream, 0, pulse_op_cb, &success);
@@ -1566,7 +1563,6 @@ static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
}
if (SUCCEEDED(hr)) {
This->started = FALSE;
- This->clock_pulse = PA_USEC_INVALID;
}
pthread_mutex_unlock(&pulse_lock);
return hr;
@@ -1764,7 +1760,8 @@ static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
UINT32 frames, BYTE **data)
{
ACImpl *This = impl_from_IAudioRenderClient(iface);
- UINT32 avail, pad, req, bytes = frames * pa_frame_size(&This->ss);
+ size_t avail, req, bytes = frames * pa_frame_size(&This->ss);
+ UINT32 pad;
HRESULT hr = S_OK;
int ret = -1;
@@ -1789,7 +1786,7 @@ static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
avail = This->bufsize_frames - pad;
if (avail < frames || bytes > This->bufsize_bytes) {
pthread_mutex_unlock(&pulse_lock);
- WARN("Wanted to write %u, but only %u available\n", frames, avail);
+ WARN("Wanted to write %u, but only %zu available\n", frames, avail);
return AUDCLNT_E_BUFFER_TOO_LARGE;
}
@@ -1797,7 +1794,7 @@ static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
req = bytes;
ret = pa_stream_begin_write(This->stream, &This->locked_ptr, &req);
if (ret < 0 || req < bytes) {
- FIXME("%p Not using pulse locked data: %i %u/%u %u/%u\n", This, ret, req/pa_frame_size(&This->ss), frames, pad, This->bufsize_frames);
+ FIXME("%p Not using pulse locked data: %i %zu/%u %u/%u\n", This, ret, req/pa_frame_size(&This->ss), frames, pad, This->bufsize_frames);
if (ret >= 0)
pa_stream_cancel_write(This->stream);
*data = This->tmp_buffer;
@@ -1845,7 +1842,7 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
pa_stream_write(This->stream, This->tmp_buffer, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
This->pad += written_bytes;
This->locked_ptr = NULL;
- TRACE("Released %u, pad %u\n", written_frames, This->pad / pa_frame_size(&This->ss));
+ TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss));
assert(This->pad <= This->bufsize_bytes);
pthread_mutex_unlock(&pulse_lock);
return S_OK;
@@ -2053,7 +2050,6 @@ static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
UINT64 *qpctime)
{
ACImpl *This = impl_from_IAudioClock(iface);
- pa_usec_t time;
HRESULT hr;
TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
@@ -2069,13 +2065,6 @@ static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
}
*pos = This->clock_written;
- if (This->clock_pulse != PA_USEC_INVALID && pa_stream_get_time(This->stream, &time) >= 0) {
- UINT32 delta = pa_usec_to_bytes(time - This->clock_pulse, &This->ss);
- if (delta < This->pad)
- *pos += delta;
- else
- *pos += This->pad;
- }
/* Make time never go backwards */
if (*pos < This->clock_lastpos)
--
2.3.5
From fe922dd1ee4380e3b3fa0e6e721675a12df0bbce Mon Sep 17 00:00:00 2001
From: Juergen Tretthahn <orson@orson.at>
Date: Mon, 23 Mar 2015 09:14:30 +0100
Subject: winepulse: API Compatibility with 1.5.2 onward, v2
V2: Add winepulse.drv.spec to commit too
V1: Original version
Commit e87cb774d131963d2642d075977571585ec5da8d changed the driver api
leave this commit out to build for builds prior to this
Not needed for: 1.5.1, 1.5 and 1.4 builds
---
dlls/winepulse.drv/mmdevdrv.c | 34 ++++++++++++++++++++++------------
dlls/winepulse.drv/winepulse.drv.spec | 2 +-
2 files changed, 23 insertions(+), 13 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 8e76826..b374b53 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -82,6 +82,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 GUID pulse_render_guid =
+{ 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
+static GUID pulse_capture_guid =
+{ 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
+
static HANDLE warn_once;
BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
@@ -716,7 +721,7 @@ static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
return S_OK;
}
-HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys,
+HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **keys,
UINT *num, UINT *def_index)
{
HRESULT hr = S_OK;
@@ -730,20 +735,21 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, void ***keys,
*num = 1;
*def_index = 0;
- *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *));
+ *ids = HeapAlloc(GetProcessHeap(), 0, sizeof(**ids));
if (!*ids)
return E_OUTOFMEMORY;
+ (*ids)[0] = defaultW;
- (*ids)[0] = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
- if (!(*ids)[0]) {
+ *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(**keys));
+ if (!*keys) {
HeapFree(GetProcessHeap(), 0, *ids);
+ *ids = NULL;
return E_OUTOFMEMORY;
}
-
- lstrcpyW((*ids)[0], defaultW);
-
- *keys = HeapAlloc(GetProcessHeap(), 0, sizeof(void *));
- (*keys)[0] = NULL;
+ if (flow == eRender)
+ (*keys)[0] = pulse_render_guid;
+ else
+ (*keys)[0] = pulse_capture_guid;
return S_OK;
}
@@ -761,12 +767,12 @@ int WINAPI AUDDRV_GetPriority(void)
return SUCCEEDED(hr) ? 3 : 0;
}
-HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev,
- EDataFlow dataflow, IAudioClient **out)
+HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
{
HRESULT hr;
ACImpl *This;
int i;
+ EDataFlow dataflow;
/* Give one visible warning per session
* Sadly wine has chosen not to accept the winepulse patch, so support ourselves
@@ -780,7 +786,11 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(void *key, IMMDevice *dev,
}
TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
- if (dataflow != eRender && dataflow != eCapture)
+ if (IsEqualGUID(guid, &pulse_render_guid))
+ dataflow = eRender;
+ else if (IsEqualGUID(guid, &pulse_capture_guid))
+ dataflow = eCapture;
+ else
return E_UNEXPECTED;
*out = NULL;
diff --git a/dlls/winepulse.drv/winepulse.drv.spec b/dlls/winepulse.drv/winepulse.drv.spec
index a089166..612bf46 100644
--- a/dlls/winepulse.drv/winepulse.drv.spec
+++ b/dlls/winepulse.drv/winepulse.drv.spec
@@ -1,5 +1,5 @@
# MMDevAPI driver functions
@ stdcall -private GetPriority() AUDDRV_GetPriority
@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs
-@ stdcall -private GetAudioEndpoint(ptr ptr long ptr) AUDDRV_GetAudioEndpoint
+@ stdcall -private GetAudioEndpoint(ptr ptr ptr) AUDDRV_GetAudioEndpoint
@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager
--
2.3.5
From a9e98ef47daf2285185ab7097e97c137debe9b1f Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:30 +0100
Subject: winepulse: Fix low latency support
Some games request a 20 ms buffer and will only fill 20 ms.
Since 10 ms periods are too long in this case for winepulse,
fill change period size to 5 ms and force a trigger if
there's still data left to fill.
---
dlls/winepulse.drv/mmdevdrv.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index b374b53..7c07f54 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -1205,11 +1205,15 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
* default
*/
if (duration < 2 * def)
- period = min;
+ period = min;
else
- period = def;
+ period = def;
if (duration < 2 * period)
duration = 2 * period;
+
+ /* Uh oh, really low latency requested.. */
+ if (duration <= 2 * period)
+ period /= 2;
}
period_bytes = pa_frame_size(&This->ss) * MulDiv(period, This->ss.rate, 10000000);
@@ -1820,6 +1824,7 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
{
ACImpl *This = impl_from_IAudioRenderClient(iface);
UINT32 written_bytes = written_frames * pa_frame_size(&This->ss);
+ UINT32 period;
TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
@@ -1854,6 +1859,11 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
This->locked_ptr = NULL;
TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss));
assert(This->pad <= This->bufsize_bytes);
+
+ period = pa_stream_get_buffer_attr(This->stream)->minreq;
+ /* Require a minimum of 3 periods filled, if possible */
+ if (This->event && This->pad + period <= This->bufsize_bytes && This->pad < period * 3)
+ SetEvent(This->event);
pthread_mutex_unlock(&pulse_lock);
return S_OK;
}
--
2.3.5
From 2dc7cdd1fd5f43be87625e32d3497cf96967b5b6 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:31 +0100
Subject: winepulse: drop realtime priority before thread destruction
prevents having to handle a kernel RT Watchdog Timeout.
---
dlls/winepulse.drv/mmdevdrv.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 7c07f54..ba68102 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -101,6 +101,8 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
}
DisableThreadLibraryCalls(dll);
} else if (reason == DLL_PROCESS_DETACH) {
+ if (pulse_thread)
+ SetThreadPriority(pulse_thread, 0);
if (pulse_ctx) {
pa_context_disconnect(pulse_ctx);
pa_context_unref(pulse_ctx);
--
2.3.5
From 91bc892624e1888b27bc56480cacd4225e2c2e42 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:31 +0100
Subject: winepulse: remove bogus SetEvent from pulse_started_callback
---
dlls/winepulse.drv/mmdevdrv.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index ba68102..68de00c 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -559,11 +559,7 @@ static void pulse_latency_callback(pa_stream *s, void *userdata)
static void pulse_started_callback(pa_stream *s, void *userdata)
{
- ACImpl *This = userdata;
-
TRACE("(Re)started playing\n");
- if (This->event)
- SetEvent(This->event);
}
static void pulse_rd_loop(ACImpl *This, size_t bytes)
--
2.3.5
From 3eb1b0d76500d27b5014c49efe0d0c31238990d6 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:31 +0100
Subject: winepulse: disable the setevent part of the latency hack
If you get playback glitches in skyrim or other games as a result of
this patch, PLEASE REPORT TO ME!
---
dlls/winepulse.drv/mmdevdrv.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 68de00c..643d55e 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -1822,7 +1822,7 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
{
ACImpl *This = impl_from_IAudioRenderClient(iface);
UINT32 written_bytes = written_frames * pa_frame_size(&This->ss);
- UINT32 period;
+// UINT32 period;
TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
@@ -1858,10 +1858,10 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss));
assert(This->pad <= This->bufsize_bytes);
- period = pa_stream_get_buffer_attr(This->stream)->minreq;
+// period = pa_stream_get_buffer_attr(This->stream)->minreq;
/* Require a minimum of 3 periods filled, if possible */
- if (This->event && This->pad + period <= This->bufsize_bytes && This->pad < period * 3)
- SetEvent(This->event);
+// if (This->event && This->pad + period <= This->bufsize_bytes && This->pad < period * 3)
+// SetEvent(This->event);
pthread_mutex_unlock(&pulse_lock);
return S_OK;
}
--
2.3.5
From fb72f746d78db232572e425184150f1454667475 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:31 +0100
Subject: winepulse v20: fix the checks in IsFormatSupported
Thanks to DGhost001 for reporting and isolating the issue.
---
dlls/winepulse.drv/mmdevdrv.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 643d55e..86dd10a 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -1443,6 +1443,10 @@ static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
}
}
+ if (fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
+ fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec)
+ hr = S_FALSE;
+
if (hr == S_OK || !out) {
CoTaskMemFree(closest);
if (out)
--
2.3.5
From 2b9a31995b83984b15d65726b8e503bdedb65ae4 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:31 +0100
Subject: winepulse: fixup IsFormatSupported calls
---
dlls/mmdevapi/tests/render.c | 164 +++++++++++++++++++++++++++++++++++++++
dlls/winepulse.drv/mmdevdrv.c | 176 ++++++++++++++++++++++++++++--------------
2 files changed, 283 insertions(+), 57 deletions(-)
diff --git a/dlls/mmdevapi/tests/render.c b/dlls/mmdevapi/tests/render.c
index a6f0c09..245c244 100644
--- a/dlls/mmdevapi/tests/render.c
+++ b/dlls/mmdevapi/tests/render.c
@@ -467,6 +467,169 @@ static void test_formats(AUDCLNT_SHAREMODE mode)
}
}
+static void test_formats2(void)
+{
+ IAudioClient *ac;
+ HRESULT hr;
+ WAVEFORMATEX *pwfx, *pwfx2;
+ WAVEFORMATEXTENSIBLE *pwfe, wfe, *pwfe2;
+
+ hr = IMMDevice_Activate(dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER,
+ NULL, (void**)&ac);
+
+ ok(hr == S_OK, "Activation failed with %08x\n", hr);
+ if (hr != S_OK)
+ return;
+
+ hr = IAudioClient_GetMixFormat(ac, &pwfx);
+ ok(hr == S_OK, "GetMixFormat failed: %08x\n", hr);
+ if (hr != S_OK)
+ return;
+
+ ok(pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE, "Invalid wFormatTag\n");
+ if (pwfx->wFormatTag != WAVE_FORMAT_EXTENSIBLE) {
+ CoTaskMemFree(pwfx);
+ return;
+ }
+
+ pwfe = (WAVEFORMATEXTENSIBLE*)pwfx;
+ ok(pwfe->Samples.wValidBitsPerSample, "wValidBitsPerSample should be non-zero\n");
+
+ if (pwfx->nChannels > 2) {
+ trace("Limiting channels to 2\n");
+ pwfx->nChannels = 2;
+ pwfx->nBlockAlign = pwfx->wBitsPerSample / 8 * pwfx->nChannels;
+ pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
+ pwfe->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
+ }
+
+ wfe = *pwfe;
+ pwfx->nAvgBytesPerSec = pwfx->nBlockAlign = 0;
+
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL);
+ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED,
+ "Exclusive IsFormatSupported with nAvgBytesPerSec=0 and nBlockAlign=0 returned %08x\n", hr);
+
+ pwfx2 = NULL;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2);
+ ok((hr == E_INVALIDARG || hr == AUDCLNT_E_UNSUPPORTED_FORMAT) && !pwfx2,
+ "Shared IsFormatSupported with nAvgBytesPerSec=0 and nBlockAlign=0 returned %08x %p\n", hr, pwfx2);
+ CoTaskMemFree(pwfx2);
+
+ pwfx->wFormatTag = WAVE_FORMAT_PCM;
+ pwfx2 = NULL;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2);
+ ok((hr == S_OK || hr == AUDCLNT_E_UNSUPPORTED_FORMAT) && !pwfx2,
+ "Shared IsFormatSupported with nAvgBytesPerSec=0 and nBlockAlign=0 returned %08x %p\n", hr, pwfx2);
+ CoTaskMemFree(pwfx2);
+
+ *pwfe = wfe;
+ pwfe->dwChannelMask = 0;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL);
+ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED,
+ "Exclusive IsFormatSupported with dwChannelMask=0 returned %08x\n", hr);
+
+ pwfx2 = NULL;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2);
+ ok(hr == S_OK,
+ "Shared IsFormatSupported with dwChannelMask=0 returned %08x\n", hr);
+ CoTaskMemFree(pwfx2);
+
+
+ pwfe->dwChannelMask = 0x3ffff;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL);
+ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED,
+ "Exclusive IsFormatSupported with dwChannelMask=0x3ffff returned %08x\n", hr);
+
+ pwfx2 = NULL;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2);
+ ok(hr == S_OK && !pwfx2,
+ "Shared IsFormatSupported with dwChannelMask=0x3ffff returned %08x %p\n", hr, pwfx2);
+ CoTaskMemFree(pwfx2);
+
+
+ pwfe->dwChannelMask = 0x40000000;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL);
+ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED,
+ "Exclusive IsFormatSupported with dwChannelMask=0x40000000 returned %08x\n", hr);
+
+ pwfx2 = NULL;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2);
+ ok(hr == S_OK && !pwfx2,
+ "Shared IsFormatSupported with dwChannelMask=0x40000000 returned %08x %p\n", hr, pwfx2);
+ CoTaskMemFree(pwfx2);
+
+ pwfe->dwChannelMask = SPEAKER_ALL | SPEAKER_RESERVED;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL);
+ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED,
+ "Exclusive IsFormatSupported with dwChannelMask=SPEAKER_ALL | SPEAKER_RESERVED returned %08x\n", hr);
+
+ pwfx2 = NULL;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2);
+ ok(hr == S_OK && !pwfx2,
+ "Shared IsFormatSupported with dwChannelMask=SPEAKER_ALL | SPEAKER_RESERVED returned %08x %p\n", hr, pwfx2);
+ CoTaskMemFree(pwfx2);
+
+ *pwfe = wfe;
+ pwfe->Samples.wValidBitsPerSample = 0;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_EXCLUSIVE, pwfx, NULL);
+ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT || hr == AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED,
+ "Exclusive IsFormatSupported with wValidBitsPerSample=0 returned %08x\n", hr);
+
+ pwfx2 = NULL;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2);
+ ok((hr == S_FALSE || hr == AUDCLNT_E_UNSUPPORTED_FORMAT) && pwfx2,
+ "Shared IsFormatSupported with wValidBitsPerSample=0 returned %08x %p\n", hr, pwfx2);
+ if (pwfx2) {
+ pwfe2 = (WAVEFORMATEXTENSIBLE*)pwfx2;
+ ok(pwfe2->Samples.wValidBitsPerSample == pwfx->wBitsPerSample,
+ "Shared IsFormatSupported had wValidBitsPerSample set to %u, not %u\n",
+ pwfe2->Samples.wValidBitsPerSample, pwfx->wBitsPerSample);
+ CoTaskMemFree(pwfx2);
+ }
+
+ pwfx2 = NULL;
+ pwfe->Samples.wValidBitsPerSample = pwfx->wBitsPerSample + 1;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2);
+ ok((hr == E_INVALIDARG || hr == AUDCLNT_E_UNSUPPORTED_FORMAT) && !pwfx2,
+ "Shared IsFormatSupported with wValidBitsPerSample += 1 returned %08x %p\n", hr, pwfx2);
+
+ *pwfe = wfe;
+ memset(&pwfe->SubFormat, 0xff, 16);
+ pwfx2 = NULL;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2);
+ ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT && !pwfx2,
+ "Shared IsFormatSupported with SubFormat=-1 returned %08x %p\n", hr, pwfx2);
+ CoTaskMemFree(pwfx2);
+
+ *pwfe = wfe;
+ pwfx2 = NULL;
+ pwfe->Samples.wValidBitsPerSample = pwfx->wBitsPerSample = 256;
+ pwfx->nBlockAlign = pwfx->wBitsPerSample / 8 * pwfx->nChannels;
+ pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2);
+ ok((hr == E_INVALIDARG || hr == AUDCLNT_E_UNSUPPORTED_FORMAT) && !pwfx2,
+ "Shared IsFormatSupported with wBitsPerSample=256 returned %08x %p\n", hr, pwfx2);
+ CoTaskMemFree(pwfx2);
+
+ *pwfe = wfe;
+ pwfx2 = NULL;
+ pwfe->Samples.wValidBitsPerSample = pwfx->wBitsPerSample - 1;
+ hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, pwfx, &pwfx2);
+ ok(hr == S_FALSE && pwfx2,
+ "Shared IsFormatSupported with wValidBitsPerSample-=1 returned %08x %p\n", hr, pwfx2);
+ if (pwfx2) {
+ pwfe2 = (WAVEFORMATEXTENSIBLE*)pwfx2;
+ ok(pwfe2->Samples.wValidBitsPerSample == pwfx->wBitsPerSample,
+ "Shared IsFormatSupported had wValidBitsPerSample set to %u, not %u\n",
+ pwfe2->Samples.wValidBitsPerSample, pwfx->wBitsPerSample);
+ CoTaskMemFree(pwfx2);
+ }
+
+ CoTaskMemFree(pwfx);
+ IAudioClient_Release(ac);
+}
+
static void test_references(void)
{
IAudioClient *ac;
@@ -2244,6 +2407,7 @@ START_TEST(render)
test_audioclient();
test_formats(AUDCLNT_SHAREMODE_EXCLUSIVE);
test_formats(AUDCLNT_SHAREMODE_SHARED);
+ test_formats2();
test_references();
test_marshal();
trace("Output to a MS-DOS console is particularly slow and disturbs timing.\n");
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 86dd10a..554a9fc 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -1104,10 +1104,8 @@ static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
}
}
This->map.channels = fmt->nChannels;
- if (!mask || mask == SPEAKER_ALL)
+ if (!mask || (mask & (SPEAKER_ALL|SPEAKER_RESERVED)))
mask = get_channel_mask(fmt->nChannels);
- else if (mask == ~0U && fmt->nChannels == 1)
- mask = SPEAKER_FRONT_CENTER;
for (j = 0; j < sizeof(pulse_pos_from_wfx)/sizeof(*pulse_pos_from_wfx) && i < fmt->nChannels; ++j) {
if (mask & (1 << j))
This->map.map[i++] = pulse_pos_from_wfx[j];
@@ -1381,83 +1379,147 @@ static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
ACImpl *This = impl_from_IAudioClient(iface);
HRESULT hr = S_OK;
WAVEFORMATEX *closest = NULL;
+ BOOL exclusive;
TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
- if (!fmt || (mode == AUDCLNT_SHAREMODE_SHARED && !out))
+ if (!fmt)
return E_POINTER;
if (out)
*out = NULL;
- if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
+
+ if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
+ exclusive = 1;
+ out = NULL;
+ } else if (mode == AUDCLNT_SHAREMODE_SHARED) {
+ exclusive = 0;
+ if (!out)
+ return E_POINTER;
+ } else
return E_INVALIDARG;
- if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
- return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
- switch (fmt->wFormatTag) {
- case WAVE_FORMAT_EXTENSIBLE:
- if (fmt->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
- return E_INVALIDARG;
- dump_fmt(fmt);
- break;
- case WAVE_FORMAT_ALAW:
- case WAVE_FORMAT_MULAW:
- case WAVE_FORMAT_IEEE_FLOAT:
- case WAVE_FORMAT_PCM:
- dump_fmt(fmt);
- break;
- default:
- dump_fmt(fmt);
- return AUDCLNT_E_UNSUPPORTED_FORMAT;
- }
+
if (fmt->nChannels == 0)
return AUDCLNT_E_UNSUPPORTED_FORMAT;
+
closest = clone_format(fmt);
- if (!closest) {
- if (out)
- *out = NULL;
+ if (!closest)
return E_OUTOFMEMORY;
- }
- if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
- UINT32 mask = 0, i, channels = 0;
+ dump_fmt(fmt);
+
+ switch (fmt->wFormatTag) {
+ case WAVE_FORMAT_EXTENSIBLE: {
WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)closest;
- if ((fmt->nChannels > 1 && ext->dwChannelMask == SPEAKER_ALL) ||
- (fmt->nChannels == 1 && ext->dwChannelMask == ~0U)) {
- mask = ext->dwChannelMask;
- channels = fmt->nChannels;
- } else if (ext->dwChannelMask) {
- for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) {
- if (i & ext->dwChannelMask) {
- mask |= i;
- channels++;
+ if ((fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) &&
+ fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE)) ||
+ fmt->nBlockAlign != fmt->wBitsPerSample / 8 * fmt->nChannels ||
+ ext->Samples.wValidBitsPerSample > fmt->wBitsPerSample ||
+ fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec) {
+ hr = E_INVALIDARG;
+ break;
+ }
+
+ if (exclusive) {
+ UINT32 mask = 0, i, channels = 0;
+
+ if (!(ext->dwChannelMask & (SPEAKER_ALL | SPEAKER_RESERVED))) {
+ for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) {
+ if (i & ext->dwChannelMask) {
+ mask |= i;
+ channels++;
+ }
+ }
+
+ if (channels != fmt->nChannels || (ext->dwChannelMask & ~mask)) {
+ hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
+ break;
}
+ } else {
+ hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
+ break;
}
- if (channels < fmt->nChannels)
- mask = get_channel_mask(fmt->nChannels);
- } else
- mask = ext->dwChannelMask;
- if (ext->dwChannelMask != mask) {
- ext->dwChannelMask = mask;
- hr = S_FALSE;
}
+
+ if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
+ if (fmt->wBitsPerSample != 32) {
+ hr = E_INVALIDARG;
+ break;
+ }
+
+ if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample) {
+ hr = S_FALSE;
+ ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
+ }
+ } else if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
+ if (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8) {
+ hr = E_INVALIDARG;
+ break;
+ }
+
+ if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample &&
+ !(fmt->wBitsPerSample == 32 &&
+ ext->Samples.wValidBitsPerSample == 24)) {
+ hr = S_FALSE;
+ ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
+ break;
+ }
+ } else {
+ hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
+ break;
+ }
+
+ break;
}
- if (fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
- fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec)
- hr = S_FALSE;
+ case WAVE_FORMAT_ALAW:
+ case WAVE_FORMAT_MULAW:
+ if (fmt->wBitsPerSample != 8) {
+ hr = E_INVALIDARG;
+ break;
+ }
+ /* Fall-through */
+ case WAVE_FORMAT_IEEE_FLOAT:
+ if (fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT && fmt->wBitsPerSample != 32) {
+ hr = E_INVALIDARG;
+ break;
+ }
+ /* Fall-through */
+ case WAVE_FORMAT_PCM:
+ if (fmt->wFormatTag == WAVE_FORMAT_PCM &&
+ (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8)) {
+ hr = E_INVALIDARG;
+ break;
+ }
+
+ if (fmt->nChannels > 2) {
+ hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
+ break;
+ }
+ /*
+ * fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be
+ * ignored, invalid values are happily accepted.
+ */
+ break;
+ default:
+ hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
+ break;
+ }
- if (hr == S_OK || !out) {
+ if (exclusive && hr != S_OK) {
+ hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
CoTaskMemFree(closest);
- if (out)
- *out = NULL;
- } else if (closest) {
- closest->nBlockAlign =
- closest->nChannels * closest->wBitsPerSample / 8;
- closest->nAvgBytesPerSec =
- closest->nBlockAlign * closest->nSamplesPerSec;
+ } else if (hr != S_FALSE)
+ CoTaskMemFree(closest);
+ else
*out = closest;
- }
+
+ /* Winepulse does not currently support exclusive mode, if you know of an
+ * application that uses it, I will correct this..
+ */
+ if (hr == S_OK && exclusive)
+ return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
TRACE("returning: %08x %p\n", hr, out ? *out : NULL);
return hr;
--
2.3.5
From 506b3e336950444f268c2c035e618b33106fbe0e Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:31 +0100
Subject: winepulse v21: return early if padding didn't update
---
dlls/winepulse.drv/mmdevdrv.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 554a9fc..a4575d5 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -531,7 +531,10 @@ static void pulse_wr_callback(pa_stream *s, size_t bytes, void *userdata)
else
This->pad = 0;
- assert(oldpad >= This->pad);
+ if (oldpad == This->pad)
+ return;
+
+ assert(oldpad > This->pad);
This->clock_written += oldpad - This->pad;
TRACE("New pad: %zu (-%zu)\n", This->pad / pa_frame_size(&This->ss), (oldpad - This->pad) / pa_frame_size(&This->ss));
--
2.3.5
From 1ee2a8772c80c706bdf695b3f3e0d616fc475557 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:31 +0100
Subject: winepulse: fix unneeded free in write..
---
dlls/winepulse.drv/mmdevdrv.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index a4575d5..3ca68fd 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -1886,6 +1886,10 @@ static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
return hr;
}
+static void pulse_free_noop(void *buf)
+{
+}
+
static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
{
@@ -1921,7 +1925,7 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
if (This->locked_ptr)
pa_stream_write(This->stream, This->locked_ptr, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
else
- pa_stream_write(This->stream, This->tmp_buffer, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
+ pa_stream_write(This->stream, This->tmp_buffer, written_bytes, pulse_free_noop, 0, PA_SEEK_RELATIVE);
This->pad += written_bytes;
This->locked_ptr = NULL;
TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss));
--
2.3.5
From 90037e6b7fe175bd2599d714474ee5c5b2df4613 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:31 +0100
Subject: winepulse v23: fixup a invalid free in mmdevapi
array members of ids should be dynamically allocated, judging from valgrind output.
---
dlls/winepulse.drv/mmdevdrv.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 3ca68fd..5b041a2 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -726,6 +726,8 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **
UINT *num, UINT *def_index)
{
HRESULT hr = S_OK;
+ WCHAR *id;
+
TRACE("%d %p %p %p\n", flow, ids, num, def_index);
pthread_mutex_lock(&pulse_lock);
@@ -737,16 +739,22 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **
*def_index = 0;
*ids = HeapAlloc(GetProcessHeap(), 0, sizeof(**ids));
+ *keys = NULL;
if (!*ids)
return E_OUTOFMEMORY;
- (*ids)[0] = defaultW;
+ (*ids)[0] = id = HeapAlloc(GetProcessHeap(), 0, sizeof(defaultW));
*keys = HeapAlloc(GetProcessHeap(), 0, sizeof(**keys));
- if (!*keys) {
+ if (!*keys || !id) {
+ HeapFree(GetProcessHeap(), 0, id);
+ HeapFree(GetProcessHeap(), 0, *keys);
HeapFree(GetProcessHeap(), 0, *ids);
*ids = NULL;
+ *keys = NULL;
return E_OUTOFMEMORY;
}
+ memcpy(id, defaultW, sizeof(defaultW));
+
if (flow == eRender)
(*keys)[0] = pulse_render_guid;
else
--
2.3.5
From 3316329fbc09f86adb4e93da9f5454429b04742e Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:32 +0100
Subject: winepulse: use a pi-mutex for serialization.
The winepulse thread is realtime, to prevent blocking it indefinitely
use priority inheritance. Only initialize and release are potentially
unsafe, the rest should be ok with -rt.
---
dlls/winepulse.drv/mmdevdrv.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 5b041a2..a09ce7f 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -22,6 +22,8 @@
#define NONAMELESSUNION
#define COBJMACROS
+#define _GNU_SOURCE
+
#include "config.h"
#include <poll.h>
#include <pthread.h>
@@ -30,6 +32,7 @@
#include <unistd.h>
#include <math.h>
#include <stdio.h>
+#include <errno.h>
#include <pulse/pulseaudio.h>
@@ -68,7 +71,7 @@ static pa_context *pulse_ctx;
static pa_mainloop *pulse_ml;
static HANDLE pulse_thread;
-static pthread_mutex_t pulse_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t pulse_lock;
static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER;
static struct list g_sessions = LIST_INIT(g_sessions);
@@ -93,6 +96,8 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
{
if (reason == DLL_PROCESS_ATTACH) {
HKEY key;
+ pthread_mutexattr_t attr;
+
if (RegOpenKeyW(HKEY_CURRENT_USER, pulse_keyW, &key) == ERROR_SUCCESS) {
DWORD size = sizeof(pulse_stream_volume);
RegQueryValueExW(key, pulse_streamW, 0, NULL,
@@ -100,6 +105,12 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
RegCloseKey(key);
}
DisableThreadLibraryCalls(dll);
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
+
+ if (pthread_mutex_init(&pulse_lock, &attr) != 0)
+ pthread_mutex_init(&pulse_lock, NULL);
} else if (reason == DLL_PROCESS_DETACH) {
if (pulse_thread)
SetThreadPriority(pulse_thread, 0);
--
2.3.5
From 97b9e0b25dbf511fd91449ac36154065b46d278a Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:32 +0100
Subject: winepulse: add support for IMarshal
Fixes bug 32161 for winepulse. Based On Jeff Klein's patches for the
other drivers.
---
dlls/winepulse.drv/mmdevdrv.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index a09ce7f..f052a08 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -169,6 +169,7 @@ struct ACImpl {
IAudioClock IAudioClock_iface;
IAudioClock2 IAudioClock2_iface;
IAudioStreamVolume IAudioStreamVolume_iface;
+ IUnknown *marshal;
IMMDevice *parent;
struct list entry;
float vol[PA_CHANNELS_MAX];
@@ -834,6 +835,12 @@ 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;
+
+ hr = CoCreateFreeThreadedMarshaler((IUnknown*)This, &This->marshal);
+ if (hr) {
+ HeapFree(GetProcessHeap(), 0, This);
+ return hr;
+ }
IMMDevice_AddRef(This->parent);
*out = &This->IAudioClient_iface;
@@ -845,10 +852,13 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
REFIID riid, void **ppv)
{
+ ACImpl *This = impl_from_IAudioClient(iface);
+
TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
if (!ppv)
return E_POINTER;
+
*ppv = NULL;
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClient))
*ppv = iface;
@@ -856,6 +866,10 @@ static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient *iface,
IUnknown_AddRef((IUnknown*)*ppv);
return S_OK;
}
+
+ if (IsEqualIID(riid, &IID_IMarshal))
+ return IUnknown_QueryInterface(This->marshal, riid, ppv);
+
WARN("Unknown interface %s\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
@@ -888,6 +902,7 @@ static ULONG WINAPI AudioClient_Release(IAudioClient *iface)
list_remove(&This->entry);
pthread_mutex_unlock(&pulse_lock);
}
+ IUnknown_Release(This->marshal);
IMMDevice_Release(This->parent);
HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
HeapFree(GetProcessHeap(), 0, This);
@@ -1826,6 +1841,7 @@ static const IAudioClientVtbl AudioClient_Vtbl =
static HRESULT WINAPI AudioRenderClient_QueryInterface(
IAudioRenderClient *iface, REFIID riid, void **ppv)
{
+ ACImpl *This = impl_from_IAudioRenderClient(iface);
TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
if (!ppv)
@@ -1840,6 +1856,9 @@ static HRESULT WINAPI AudioRenderClient_QueryInterface(
return S_OK;
}
+ if (IsEqualIID(riid, &IID_IMarshal))
+ return IUnknown_QueryInterface(This->marshal, riid, ppv);
+
WARN("Unknown interface %s\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
@@ -1969,6 +1988,7 @@ static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
static HRESULT WINAPI AudioCaptureClient_QueryInterface(
IAudioCaptureClient *iface, REFIID riid, void **ppv)
{
+ ACImpl *This = impl_from_IAudioCaptureClient(iface);
TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
if (!ppv)
@@ -1983,6 +2003,9 @@ static HRESULT WINAPI AudioCaptureClient_QueryInterface(
return S_OK;
}
+ if (IsEqualIID(riid, &IID_IMarshal))
+ return IUnknown_QueryInterface(This->marshal, riid, ppv);
+
WARN("Unknown interface %s\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
@@ -2125,6 +2148,9 @@ static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
return S_OK;
}
+ if (IsEqualIID(riid, &IID_IMarshal))
+ return IUnknown_QueryInterface(This->marshal, riid, ppv);
+
WARN("Unknown interface %s\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
@@ -2260,6 +2286,8 @@ static const IAudioClock2Vtbl AudioClock2_Vtbl =
static HRESULT WINAPI AudioStreamVolume_QueryInterface(
IAudioStreamVolume *iface, REFIID riid, void **ppv)
{
+ ACImpl *This = impl_from_IAudioStreamVolume(iface);
+
TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
if (!ppv)
@@ -2274,6 +2302,9 @@ static HRESULT WINAPI AudioStreamVolume_QueryInterface(
return S_OK;
}
+ if (IsEqualIID(riid, &IID_IMarshal))
+ return IUnknown_QueryInterface(This->marshal, riid, ppv);
+
WARN("Unknown interface %s\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
--
2.3.5
From 7c6fe123275e2b798570d58a7a37325550dfa004 Mon Sep 17 00:00:00 2001
From: Mark Harmstone <mark@harmstone.com>
Date: Mon, 23 Mar 2015 09:14:32 +0100
Subject: winepulse: handle stream create failing correctly
---
dlls/winepulse.drv/mmdevdrv.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index f052a08..e755e8a 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -701,6 +701,12 @@ static HRESULT pulse_stream_connect(ACImpl *This, UINT32 period_bytes) {
ret = InterlockedIncrement(&number);
sprintf(buffer, "audio stream #%i", ret);
This->stream = pa_stream_new(pulse_ctx, buffer, &This->ss, &This->map);
+
+ if (!This->stream) {
+ WARN("pa_stream_new returned error %i\n", pa_context_errno(pulse_ctx));
+ return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
+ }
+
pa_stream_set_state_callback(This->stream, pulse_stream_state, This);
pa_stream_set_buffer_attr_callback(This->stream, pulse_attr_update, This);
pa_stream_set_moved_callback(This->stream, pulse_attr_update, This);
--
2.3.5
From 2d0da2cebe4cd718a68b05dfb6cfa717747ff11d Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Mon, 23 Mar 2015 09:14:32 +0100
Subject: winepulse: Trivial cleanups and changes for consistency with other
drivers
Removes some C++ comments, the FIXME's and fixes indent some.
---
dlls/winepulse.drv/mmdevdrv.c | 86 +++++++++++++++++--------------------------
1 file changed, 33 insertions(+), 53 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index e755e8a..62d040a 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -16,8 +16,6 @@
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- *
- * Pulseaudio driver support.. hell froze over
*/
#define NONAMELESSUNION
@@ -62,7 +60,14 @@
#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
WINE_DEFAULT_DEBUG_CHANNEL(pulse);
-WINE_DECLARE_DEBUG_CHANNEL(winediag);
+
+/* From <dlls/mmdevapi/mmdevapi.h> */
+enum DriverPriority {
+ Priority_Unavailable = 0,
+ Priority_Low,
+ Priority_Neutral,
+ Priority_Preferred
+};
static const REFERENCE_TIME MinimumPeriod = 30000;
static const REFERENCE_TIME DefaultPeriod = 100000;
@@ -90,8 +95,6 @@ static GUID pulse_render_guid =
static GUID pulse_capture_guid =
{ 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
-static HANDLE warn_once;
-
BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
{
if (reason == DLL_PROCESS_ATTACH) {
@@ -122,8 +125,6 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
pa_mainloop_quit(pulse_ml, 0);
if (pulse_thread)
CloseHandle(pulse_thread);
- if (warn_once)
- CloseHandle(warn_once);
}
return TRUE;
}
@@ -507,6 +508,11 @@ static HRESULT pulse_stream_valid(ACImpl *This) {
return S_OK;
}
+static void silence_buffer(pa_sample_format_t format, BYTE *buffer, UINT32 bytes)
+{
+ memset(buffer, format == PA_SAMPLE_U8 ? 0x80 : 0, bytes);
+}
+
static void dump_attr(const pa_buffer_attr *attr) {
TRACE("maxlength: %u\n", attr->maxlength);
TRACE("minreq: %u\n", attr->minreq);
@@ -784,14 +790,10 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, const WCHAR ***ids, GUID **
int WINAPI AUDDRV_GetPriority(void)
{
HRESULT hr;
- if (getenv("WINENOPULSE")) {
- FIXME_(winediag)("winepulse has been temporarily disabled through the environment\n");
- return 0;
- }
pthread_mutex_lock(&pulse_lock);
hr = pulse_connect();
pthread_mutex_unlock(&pulse_lock);
- return SUCCEEDED(hr) ? 3 : 0;
+ return SUCCEEDED(hr) ? Priority_Preferred : Priority_Unavailable;
}
HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
@@ -801,17 +803,6 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
int i;
EDataFlow dataflow;
- /* Give one visible warning per session
- * Sadly wine has chosen not to accept the winepulse patch, so support ourselves
- */
- if (!warn_once && (warn_once = CreateEventA(0, 0, 0, "__winepulse_warn_event")) && GetLastError() != ERROR_ALREADY_EXISTS) {
- FIXME_(winediag)("Winepulse is not officially supported by the wine project\n");
- FIXME_(winediag)("For sound related feedback and support, please visit http://ubuntuforums.org/showthread.php?t=1960599\n");
- } else {
- WARN_(winediag)("Winepulse is not officially supported by the wine project\n");
- WARN_(winediag)("For sound related feedback and support, please visit http://ubuntuforums.org/showthread.php?t=1960599\n");
- }
-
TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
if (IsEqualGUID(guid, &pulse_render_guid))
dataflow = eRender;
@@ -1165,22 +1156,22 @@ static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
}
break;
}
- case WAVE_FORMAT_ALAW:
- case WAVE_FORMAT_MULAW:
- if (fmt->wBitsPerSample != 8) {
- FIXME("Unsupported bpp %u for LAW\n", fmt->wBitsPerSample);
- return AUDCLNT_E_UNSUPPORTED_FORMAT;
- }
- if (fmt->nChannels != 1 && fmt->nChannels != 2) {
- FIXME("Unsupported channels %u for LAW\n", fmt->nChannels);
- return AUDCLNT_E_UNSUPPORTED_FORMAT;
- }
- This->ss.format = fmt->wFormatTag == WAVE_FORMAT_MULAW ? PA_SAMPLE_ULAW : PA_SAMPLE_ALAW;
- pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
- break;
- default:
- WARN("Unhandled tag %x\n", fmt->wFormatTag);
+ case WAVE_FORMAT_ALAW:
+ case WAVE_FORMAT_MULAW:
+ if (fmt->wBitsPerSample != 8) {
+ FIXME("Unsupported bpp %u for LAW\n", fmt->wBitsPerSample);
+ return AUDCLNT_E_UNSUPPORTED_FORMAT;
+ }
+ if (fmt->nChannels != 1 && fmt->nChannels != 2) {
+ FIXME("Unsupported channels %u for LAW\n", fmt->nChannels);
return AUDCLNT_E_UNSUPPORTED_FORMAT;
+ }
+ This->ss.format = fmt->wFormatTag == WAVE_FORMAT_MULAW ? PA_SAMPLE_ULAW : PA_SAMPLE_ALAW;
+ pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
+ break;
+ default:
+ WARN("Unhandled tag %x\n", fmt->wFormatTag);
+ return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
This->ss.channels = This->map.channels;
if (!pa_channel_map_valid(&This->map) || This->ss.format == PA_SAMPLE_INVALID) {
@@ -1287,7 +1278,7 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
else {
ACPacket *cur_packet = (ACPacket*)((char*)This->tmp_buffer + This->bufsize_bytes);
BYTE *data = This->tmp_buffer;
- memset(This->tmp_buffer, This->ss.format == PA_SAMPLE_U8 ? 0x80 : 0, This->bufsize_bytes);
+ silence_buffer(This->ss.format, This->tmp_buffer, This->bufsize_bytes);
list_init(&This->packet_free_head);
list_init(&This->packet_filled_head);
for (i = 0; i < capture_packets; ++i, ++cur_packet) {
@@ -1939,7 +1930,6 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
{
ACImpl *This = impl_from_IAudioRenderClient(iface);
UINT32 written_bytes = written_frames * pa_frame_size(&This->ss);
-// UINT32 period;
TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
@@ -1958,12 +1948,8 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
return AUDCLNT_E_INVALID_SIZE;
}
- if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
- if (This->ss.format == PA_SAMPLE_U8)
- memset(This->tmp_buffer, 128, written_bytes);
- else
- memset(This->tmp_buffer, 0, written_bytes);
- }
+ if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
+ silence_buffer(This->ss.format, This->tmp_buffer, written_bytes);
This->locked = 0;
if (This->locked_ptr)
@@ -1975,10 +1961,6 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss));
assert(This->pad <= This->bufsize_bytes);
-// period = pa_stream_get_buffer_attr(This->stream)->minreq;
- /* Require a minimum of 3 periods filled, if possible */
-// if (This->event && This->pad + period <= This->bufsize_bytes && This->pad < period * 3)
-// SetEvent(This->event);
pthread_mutex_unlock(&pulse_lock);
return S_OK;
}
@@ -2107,7 +2089,6 @@ static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
IAudioCaptureClient *iface, UINT32 *frames)
{
ACImpl *This = impl_from_IAudioCaptureClient(iface);
- ACPacket *p;
TRACE("(%p)->(%p)\n", This, frames);
if (!frames)
@@ -2115,8 +2096,7 @@ static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
pthread_mutex_lock(&pulse_lock);
ACImpl_GetCapturePad(This, NULL);
- p = This->locked_ptr;
- if (p)
+ if (This->locked_ptr)
*frames = This->capture_period / pa_frame_size(&This->ss);
else
*frames = 0;
--
2.3.5
From 48ea4b2cf85ebbd24fe0b60fdbf3908423283b4a Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Mon, 23 Mar 2015 09:14:32 +0100
Subject: winepulse: Sync default channel masks with other drivers
---
dlls/winepulse.drv/mmdevdrv.c | 26 +++++++++-----------------
1 file changed, 9 insertions(+), 17 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 62d040a..1ef2ea2 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -964,33 +964,25 @@ static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
static DWORD get_channel_mask(unsigned int channels)
{
- switch(channels) {
+ switch(channels){
case 0:
return 0;
case 1:
- return SPEAKER_FRONT_CENTER;
+ return KSAUDIO_SPEAKER_MONO;
case 2:
- return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
+ return KSAUDIO_SPEAKER_STEREO;
case 3:
- return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
- SPEAKER_LOW_FREQUENCY;
+ return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
case 4:
- return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT;
+ return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
case 5:
- return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY;
+ return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
case 6:
- return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER;
+ return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
case 7:
- return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER |
- SPEAKER_BACK_CENTER;
+ return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
case 8:
- return SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT |
- SPEAKER_BACK_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_FRONT_CENTER |
- SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
+ return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
}
FIXME("Unknown speaker configuration: %u\n", channels);
return 0;
--
2.3.5
From 7e3da5a0aa0e331df1b8a39b42e86af0872a740c Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Mon, 23 Mar 2015 09:14:32 +0100
Subject: winepulse: In Shared mode, track device position in bytes
---
dlls/winepulse.drv/mmdevdrv.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 1ef2ea2..3463cd8 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -2154,8 +2154,12 @@ static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
pthread_mutex_lock(&pulse_lock);
hr = pulse_stream_valid(This);
- if (SUCCEEDED(hr))
- *freq = This->ss.rate * pa_frame_size(&This->ss);
+ if (SUCCEEDED(hr)) {
+ if (This->share == AUDCLNT_SHAREMODE_SHARED)
+ *freq = This->ss.rate * pa_frame_size(&This->ss);
+ else
+ *freq = This->ss.rate;
+ }
pthread_mutex_unlock(&pulse_lock);
return hr;
}
@@ -2180,6 +2184,9 @@ static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
*pos = This->clock_written;
+ if (This->share == AUDCLNT_SHAREMODE_EXCLUSIVE)
+ *pos /= pa_frame_size(&This->ss);
+
/* Make time never go backwards */
if (*pos < This->clock_lastpos)
*pos = This->clock_lastpos;
@@ -2248,7 +2255,7 @@ static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
{
ACImpl *This = impl_from_IAudioClock2(iface);
HRESULT hr = AudioClock_GetPosition(&This->IAudioClock_iface, pos, qpctime);
- if (SUCCEEDED(hr))
+ if (SUCCEEDED(hr) && This->share == AUDCLNT_SHAREMODE_SHARED)
*pos /= pa_frame_size(&This->ss);
return hr;
}
--
2.3.5
From 3dcfefa8235345cb0224488d6a49066275e3fd70 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Mon, 23 Mar 2015 09:14:32 +0100
Subject: winepulse: Always mute buffer
---
dlls/winepulse.drv/mmdevdrv.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 3463cd8..ba38ab9 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -1940,14 +1940,17 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
return AUDCLNT_E_INVALID_SIZE;
}
- if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
- silence_buffer(This->ss.format, This->tmp_buffer, written_bytes);
-
This->locked = 0;
- if (This->locked_ptr)
+ if (This->locked_ptr) {
+ if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
+ silence_buffer(This->ss.format, This->locked_ptr, written_bytes);
pa_stream_write(This->stream, This->locked_ptr, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
- else
+ } else {
+ if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
+ silence_buffer(This->ss.format, This->tmp_buffer, written_bytes);
pa_stream_write(This->stream, This->tmp_buffer, written_bytes, pulse_free_noop, 0, PA_SEEK_RELATIVE);
+ }
+
This->pad += written_bytes;
This->locked_ptr = NULL;
TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss));
--
2.3.5
From 83295503f2a86768f6e6b48181d2c46f7cf42604 Mon Sep 17 00:00:00 2001
From: Andrew Eikum <aeikum@codeweavers.com>
Date: Mon, 23 Mar 2015 09:14:32 +0100
Subject: winepulse: Remove volume support
This was disabled by default, and a quick Google turned up no references
to anyone being told to set it. If no one's using it, let's just remove
it.
---
dlls/winepulse.drv/mmdevdrv.c | 90 +++----------------------------------------
1 file changed, 6 insertions(+), 84 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index ba38ab9..063b1db 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -84,12 +84,6 @@ static struct list g_sessions = LIST_INIT(g_sessions);
static WAVEFORMATEXTENSIBLE pulse_fmt[2];
static REFERENCE_TIME pulse_min_period[2], pulse_def_period[2];
-static DWORD pulse_stream_volume;
-
-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 GUID pulse_render_guid =
{ 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
static GUID pulse_capture_guid =
@@ -98,15 +92,8 @@ static GUID pulse_capture_guid =
BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
{
if (reason == DLL_PROCESS_ATTACH) {
- HKEY key;
pthread_mutexattr_t attr;
- if (RegOpenKeyW(HKEY_CURRENT_USER, pulse_keyW, &key) == ERROR_SUCCESS) {
- DWORD size = sizeof(pulse_stream_volume);
- RegQueryValueExW(key, pulse_streamW, 0, NULL,
- (BYTE*)&pulse_stream_volume, &size);
- RegCloseKey(key);
- }
DisableThreadLibraryCalls(dll);
pthread_mutexattr_init(&attr);
@@ -527,12 +514,6 @@ static void pulse_op_cb(pa_stream *s, int success, void *user) {
pthread_cond_signal(&pulse_cond);
}
-static void pulse_ctx_op_cb(pa_context *c, int success, void *user) {
- TRACE("Success: %i\n", success);
- *(int*)user = success;
- pthread_cond_signal(&pulse_cond);
-}
-
static void pulse_attr_update(pa_stream *s, void *user) {
const pa_buffer_attr *attr = pa_stream_get_buffer_attr(s);
TRACE("New attributes or device moved:\n");
@@ -2329,36 +2310,12 @@ struct pulse_info_cb_data {
float *levels;
};
-static void pulse_sink_input_info_cb(pa_context *c, const pa_sink_input_info *info, int eol, void *data)
-{
- struct pulse_info_cb_data *d = data;
- int i;
- if (eol)
- return;
- for (i = 0; i < d->n; ++i)
- d->levels[i] = (float)info->volume.values[i] / (float)PA_VOLUME_NORM;
- pthread_cond_signal(&pulse_cond);
-}
-
-static void pulse_source_info_cb(pa_context *c, const pa_source_info *info, int eol, void *data)
-{
- struct pulse_info_cb_data *d = data;
- int i;
- if (eol)
- return;
- for (i = 0; i < d->n; ++i)
- d->levels[i] = (float)info->volume.values[i] / (float)PA_VOLUME_NORM;
- pthread_cond_signal(&pulse_cond);
-}
-
static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
IAudioStreamVolume *iface, UINT32 count, const float *levels)
{
ACImpl *This = impl_from_IAudioStreamVolume(iface);
- pa_operation *o;
HRESULT hr;
- int success = 0, i;
- pa_cvolume cv;
+ int i;
TRACE("(%p)->(%d, %p)\n", This, count, levels);
@@ -2373,26 +2330,8 @@ static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
if (FAILED(hr))
goto out;
- if (pulse_stream_volume) {
- cv.channels = count;
- for (i = 0; i < cv.channels; ++i)
- cv.values[i] = levels[i] * (float)PA_VOLUME_NORM;
- if (This->dataflow == eRender)
- o = pa_context_set_sink_input_volume(pulse_ctx, pa_stream_get_index(This->stream), &cv, pulse_ctx_op_cb, &success);
- else
- o = pa_context_set_source_volume_by_index(pulse_ctx, pa_stream_get_device_index(This->stream), &cv, pulse_ctx_op_cb, &success);
- if (o) {
- while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
- pthread_cond_wait(&pulse_cond, &pulse_lock);
- pa_operation_unref(o);
- }
- if (!success)
- hr = AUDCLNT_E_BUFFER_ERROR;
- } else {
- int i;
- for (i = 0; i < count; ++i)
- This->vol[i] = levels[i];
- }
+ for (i = 0; i < count; ++i)
+ This->vol[i] = levels[i];
out:
pthread_mutex_unlock(&pulse_lock);
@@ -2403,9 +2342,8 @@ static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
IAudioStreamVolume *iface, UINT32 count, float *levels)
{
ACImpl *This = impl_from_IAudioStreamVolume(iface);
- pa_operation *o;
HRESULT hr;
- struct pulse_info_cb_data info;
+ int i;
TRACE("(%p)->(%d, %p)\n", This, count, levels);
@@ -2420,24 +2358,8 @@ static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
if (FAILED(hr))
goto out;
- if (pulse_stream_volume) {
- info.n = count;
- info.levels = levels;
- if (This->dataflow == eRender)
- o = pa_context_get_sink_input_info(pulse_ctx, pa_stream_get_index(This->stream), pulse_sink_input_info_cb, &info);
- else
- o = pa_context_get_source_info_by_index(pulse_ctx, pa_stream_get_device_index(This->stream), pulse_source_info_cb, &info);
- if (o) {
- while(pa_operation_get_state(o) == PA_OPERATION_RUNNING)
- pthread_cond_wait(&pulse_cond, &pulse_lock);
- pa_operation_unref(o);
- } else
- hr = AUDCLNT_E_BUFFER_ERROR;
- } else {
- int i;
- for (i = 0; i < count; ++i)
- levels[i] = This->vol[i];
- }
+ for (i = 0; i < count; ++i)
+ levels[i] = This->vol[i];
out:
pthread_mutex_unlock(&pulse_lock);
--
2.3.5
From 584ff3936434f419493fbcd6e9727d360652a190 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <wine@mblankhorst.nl>
Date: Mon, 23 Mar 2015 09:14:33 +0100
Subject: winepulse: Forward winmm functions to winealsa
---
configure | 2 +-
configure.ac | 2 +-
dlls/winepulse.drv/Makefile.in | 1 +
dlls/winepulse.drv/winepulse.drv.spec | 5 +++++
4 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/configure b/configure
index 37ae481..03d3ddd 100755
--- a/configure
+++ b/configure
@@ -17718,7 +17718,7 @@ wine_fn_config_dll windowscodecs enable_windowscodecs clean,implib
wine_fn_config_test dlls/windowscodecs/tests windowscodecs_test
wine_fn_config_dll windowscodecsext enable_windowscodecsext implib
wine_fn_config_test dlls/windowscodecsext/tests windowscodecsext_test
-wine_fn_config_dll winealsa.drv enable_winealsa_drv
+wine_fn_config_dll winealsa.drv enable_winealsa_drv implib
wine_fn_config_dll winecoreaudio.drv enable_winecoreaudio_drv
wine_fn_config_lib winecrt0
wine_fn_config_dll wined3d enable_wined3d implib
diff --git a/configure.ac b/configure.ac
index 76aed78..0693220 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3353,7 +3353,7 @@ WINE_CONFIG_DLL(windowscodecs,,[clean,implib])
WINE_CONFIG_TEST(dlls/windowscodecs/tests)
WINE_CONFIG_DLL(windowscodecsext,,[implib])
WINE_CONFIG_TEST(dlls/windowscodecsext/tests)
-WINE_CONFIG_DLL(winealsa.drv)
+WINE_CONFIG_DLL(winealsa.drv,,[implib])
WINE_CONFIG_DLL(winecoreaudio.drv)
WINE_CONFIG_LIB(winecrt0)
WINE_CONFIG_DLL(wined3d,,[implib])
diff --git a/dlls/winepulse.drv/Makefile.in b/dlls/winepulse.drv/Makefile.in
index 158bbc0..3428329 100644
--- a/dlls/winepulse.drv/Makefile.in
+++ b/dlls/winepulse.drv/Makefile.in
@@ -1,5 +1,6 @@
MODULE = winepulse.drv
IMPORTS = dxguid uuid winmm user32 advapi32 ole32
+DELAYIMPORTS = winealsa.drv
EXTRALIBS = @PULSELIBS@ $(PTHREAD_LIBS)
EXTRAINCL = @PULSEINCL@
diff --git a/dlls/winepulse.drv/winepulse.drv.spec b/dlls/winepulse.drv/winepulse.drv.spec
index 612bf46..288de87 100644
--- a/dlls/winepulse.drv/winepulse.drv.spec
+++ b/dlls/winepulse.drv/winepulse.drv.spec
@@ -3,3 +3,8 @@
@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs
@ stdcall -private GetAudioEndpoint(ptr ptr ptr) AUDDRV_GetAudioEndpoint
@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager
+
+# WinMM driver functions
+@ stdcall -private DriverProc(long long long long long) winealsa.drv.DriverProc
+@ stdcall -private midMessage(long long long long long) winealsa.drv.midMessage
+@ stdcall -private modMessage(long long long long long) winealsa.drv.modMessage
--
2.3.5
From ecc3a91fdbe5f22e2546ff7db95afc3f484226cc Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <wine@mblankhorst.nl>
Date: Mon, 23 Mar 2015 09:14:33 +0100
Subject: winepulse: dump format in initialize
---
dlls/winepulse.drv/mmdevdrv.c | 7 +++++--
include/config.h.in | 6 ++++++
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 063b1db..63ef9e6 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -945,7 +945,7 @@ static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
static DWORD get_channel_mask(unsigned int channels)
{
- switch(channels){
+ switch(channels) {
case 0:
return 0;
case 1:
@@ -1053,6 +1053,7 @@ static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
pa_channel_map_init(&This->map);
This->ss.rate = fmt->nSamplesPerSec;
This->ss.format = PA_SAMPLE_INVALID;
+
switch(fmt->wFormatTag) {
case WAVE_FORMAT_IEEE_FLOAT:
if (!fmt->nChannels || fmt->nChannels > 2 || fmt->wBitsPerSample != 32)
@@ -1149,7 +1150,6 @@ static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
This->ss.channels = This->map.channels;
if (!pa_channel_map_valid(&This->map) || This->ss.format == PA_SAMPLE_INVALID) {
ERR("Invalid format! Channel spec valid: %i, format: %i\n", pa_channel_map_valid(&This->map), This->ss.format);
- dump_fmt(fmt);
return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
return S_OK;
@@ -1194,6 +1194,9 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
}
hr = pulse_spec_from_waveformat(This, fmt);
+ TRACE("Obtaining format returns %08x\n", hr);
+ dump_fmt(fmt);
+
if (FAILED(hr))
goto exit;
diff --git a/include/config.h.in b/include/config.h.in
index 4d7c7ac..0e44fd7 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -687,6 +687,12 @@
/* Define to 1 if you have the <pthread_np.h> header file. */
#undef HAVE_PTHREAD_NP_H
+/* Define if you have pulseaudio */
+#undef HAVE_PULSEAUDIO
+
+/* Define to 1 if you have the <pulse/pulseaudio.h> header file. */
+#undef HAVE_PULSE_PULSEAUDIO_H
+
/* Define to 1 if you have the <pwd.h> header file. */
#undef HAVE_PWD_H
--
2.3.5
From 42521c0dfd794ee5de1fc14a072235b10ef2eee0 Mon Sep 17 00:00:00 2001
From: Mark Harmstone <mark@harmstone.com>
Date: Mon, 23 Mar 2015 09:14:33 +0100
Subject: winepulse: add stub for GetPropValue
Edited by Maarten Lankhorst: No support for multiple devices in winepulse yet.
---
dlls/winepulse.drv/mmdevdrv.c | 13 +++++++++++++
dlls/winepulse.drv/winepulse.drv.spec | 1 +
2 files changed, 14 insertions(+)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 63ef9e6..5ae76ef 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -50,6 +50,7 @@
#include "initguid.h"
#include "ks.h"
#include "ksmedia.h"
+#include "propkey.h"
#include "mmdeviceapi.h"
#include "audioclient.h"
#include "endpointvolume.h"
@@ -3132,3 +3133,15 @@ HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
*out = &This->IAudioSessionManager2_iface;
return S_OK;
}
+
+HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
+{
+ struct pulse_prop_values_info_cb_data userdata;
+ char name[256];
+ EDataFlow flow;
+ pa_operation *o;
+
+ TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
+
+ return E_NOTIMPL;
+}
diff --git a/dlls/winepulse.drv/winepulse.drv.spec b/dlls/winepulse.drv/winepulse.drv.spec
index 288de87..7aeb175 100644
--- a/dlls/winepulse.drv/winepulse.drv.spec
+++ b/dlls/winepulse.drv/winepulse.drv.spec
@@ -3,6 +3,7 @@
@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs
@ stdcall -private GetAudioEndpoint(ptr ptr ptr) AUDDRV_GetAudioEndpoint
@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager
+@ stdcall -private GetPropValue(ptr ptr ptr) AUDDRV_GetPropValue
# WinMM driver functions
@ stdcall -private DriverProc(long long long long long) winealsa.drv.DriverProc
--
2.3.5
From 84eaebe278499af8b1d8c5779203eb0c5ecb9c75 Mon Sep 17 00:00:00 2001
From: Mark Harmstone <mark@harmstone.com>
Date: Mon, 23 Mar 2015 09:14:33 +0100
Subject: winepulse: return PKEY_AudioEndpoint_PhysicalSpeakers device prop
Edited by Maarten Lankhorst: No support for multiple devices in winepulse yet.
---
dlls/winepulse.drv/mmdevdrv.c | 87 ++++++++++++++++++++++++++++++-------------
1 file changed, 61 insertions(+), 26 deletions(-)
diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index 5ae76ef..ab78d5a 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -303,13 +303,44 @@ static const enum pa_channel_position pulse_pos_from_wfx[] = {
PA_CHANNEL_POSITION_TOP_REAR_RIGHT
};
+static DWORD pulse_channel_map_to_channel_mask(const pa_channel_map *map) {
+ int i;
+ DWORD mask = 0;
+
+ for (i = 0; i < map->channels; ++i)
+ switch (map->map[i]) {
+ default: FIXME("Unhandled channel %s\n", pa_channel_position_to_string(map->map[i])); break;
+ case PA_CHANNEL_POSITION_FRONT_LEFT: mask |= SPEAKER_FRONT_LEFT; break;
+ case PA_CHANNEL_POSITION_MONO:
+ case PA_CHANNEL_POSITION_FRONT_CENTER: mask |= SPEAKER_FRONT_CENTER; break;
+ case PA_CHANNEL_POSITION_FRONT_RIGHT: mask |= SPEAKER_FRONT_RIGHT; break;
+ case PA_CHANNEL_POSITION_REAR_LEFT: mask |= SPEAKER_BACK_LEFT; break;
+ case PA_CHANNEL_POSITION_REAR_CENTER: mask |= SPEAKER_BACK_CENTER; break;
+ case PA_CHANNEL_POSITION_REAR_RIGHT: mask |= SPEAKER_BACK_RIGHT; break;
+ case PA_CHANNEL_POSITION_LFE: mask |= SPEAKER_LOW_FREQUENCY; break;
+ case PA_CHANNEL_POSITION_SIDE_LEFT: mask |= SPEAKER_SIDE_LEFT; break;
+ case PA_CHANNEL_POSITION_SIDE_RIGHT: mask |= SPEAKER_SIDE_RIGHT; break;
+ case PA_CHANNEL_POSITION_TOP_CENTER: mask |= SPEAKER_TOP_CENTER; break;
+ case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: mask |= SPEAKER_TOP_FRONT_LEFT; break;
+ case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: mask |= SPEAKER_TOP_FRONT_CENTER; break;
+ case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: mask |= SPEAKER_TOP_FRONT_RIGHT; break;
+ case PA_CHANNEL_POSITION_TOP_REAR_LEFT: mask |= SPEAKER_TOP_BACK_LEFT; break;
+ case PA_CHANNEL_POSITION_TOP_REAR_CENTER: mask |= SPEAKER_TOP_BACK_CENTER; break;
+ case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: mask |= SPEAKER_TOP_BACK_RIGHT; break;
+ case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: mask |= SPEAKER_FRONT_LEFT_OF_CENTER; break;
+ case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: mask |= SPEAKER_FRONT_RIGHT_OF_CENTER; break;
+ }
+
+ return mask;
+}
+
static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
WAVEFORMATEX *wfx = &fmt->Format;
pa_stream *stream;
pa_channel_map map;
pa_sample_spec ss;
pa_buffer_attr attr;
- int ret, i;
+ int ret;
unsigned int length = 0;
pa_channel_map_init_auto(&map, 2, PA_CHANNEL_MAP_ALSA);
@@ -372,28 +403,7 @@ static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
else
fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- fmt->dwChannelMask = 0;
- for (i = 0; i < map.channels; ++i)
- switch (map.map[i]) {
- default: FIXME("Unhandled channel %s\n", pa_channel_position_to_string(map.map[i])); break;
- case PA_CHANNEL_POSITION_FRONT_LEFT: fmt->dwChannelMask |= SPEAKER_FRONT_LEFT; break;
- case PA_CHANNEL_POSITION_MONO:
- case PA_CHANNEL_POSITION_FRONT_CENTER: fmt->dwChannelMask |= SPEAKER_FRONT_CENTER; break;
- case PA_CHANNEL_POSITION_FRONT_RIGHT: fmt->dwChannelMask |= SPEAKER_FRONT_RIGHT; break;
- case PA_CHANNEL_POSITION_REAR_LEFT: fmt->dwChannelMask |= SPEAKER_BACK_LEFT; break;
- case PA_CHANNEL_POSITION_REAR_CENTER: fmt->dwChannelMask |= SPEAKER_BACK_CENTER; break;
- case PA_CHANNEL_POSITION_REAR_RIGHT: fmt->dwChannelMask |= SPEAKER_BACK_RIGHT; break;
- case PA_CHANNEL_POSITION_LFE: fmt->dwChannelMask |= SPEAKER_LOW_FREQUENCY; break;
- case PA_CHANNEL_POSITION_SIDE_LEFT: fmt->dwChannelMask |= SPEAKER_SIDE_LEFT; break;
- case PA_CHANNEL_POSITION_SIDE_RIGHT: fmt->dwChannelMask |= SPEAKER_SIDE_RIGHT; break;
- case PA_CHANNEL_POSITION_TOP_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_CENTER; break;
- case PA_CHANNEL_POSITION_TOP_FRONT_LEFT: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_LEFT; break;
- case PA_CHANNEL_POSITION_TOP_FRONT_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_CENTER; break;
- case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: fmt->dwChannelMask |= SPEAKER_TOP_FRONT_RIGHT; break;
- case PA_CHANNEL_POSITION_TOP_REAR_LEFT: fmt->dwChannelMask |= SPEAKER_TOP_BACK_LEFT; break;
- case PA_CHANNEL_POSITION_TOP_REAR_CENTER: fmt->dwChannelMask |= SPEAKER_TOP_BACK_CENTER; break;
- case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: fmt->dwChannelMask |= SPEAKER_TOP_BACK_RIGHT; break;
- }
+ fmt->dwChannelMask = pulse_channel_map_to_channel_mask(&map);
}
static HRESULT pulse_connect(void)
@@ -3134,14 +3144,39 @@ HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
return S_OK;
}
+static void pulse_phys_speakers_cb(pa_context *c, const pa_sink_info *i, int eol, void *userdata)
+{
+ PROPVARIANT *pv = userdata;
+
+ if (i)
+ pv->u.ulVal |= pulse_channel_map_to_channel_mask(&i->channel_map);
+
+ pthread_cond_signal(&pulse_cond);
+}
+
HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
{
- struct pulse_prop_values_info_cb_data userdata;
- char name[256];
- EDataFlow flow;
pa_operation *o;
TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
+ if (IsEqualGUID(guid, &pulse_render_guid) && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
+ /* For default Pulseaudio render device, OR together all of the
+ * PKEY_AudioEndpoint_PhysicalSpeakers values of the sinks. */
+
+ out->vt = VT_UI4;
+ out->u.ulVal = 0;
+
+ pthread_mutex_lock(&pulse_lock);
+ o = pa_context_get_sink_info_list(pulse_ctx, &pulse_phys_speakers_cb, out);
+ if (o) {
+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
+ pthread_cond_wait(&pulse_cond, &pulse_lock);
+ pa_operation_unref(o);
+ }
+ pthread_mutex_unlock(&pulse_lock);
+ return out->u.ulVal ? S_OK : E_FAIL;
+ }
+
return E_NOTIMPL;
}
--
2.3.5
From 904ab53814fa9a8ff1a680bfb5ce87f1112d7e8c Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:33 +0100
Subject: dsound: rework ugly mixer logic
---
dlls/dsound/dsound.c | 1 -
dlls/dsound/dsound_main.c | 6 -
dlls/dsound/dsound_private.h | 6 +-
dlls/dsound/mixer.c | 254 +++++++++++--------------------------------
dlls/dsound/primary.c | 107 ++++++++----------
5 files changed, 109 insertions(+), 265 deletions(-)
diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c
index 065c377..b15b366 100644
--- a/dlls/dsound/dsound.c
+++ b/dlls/dsound/dsound.c
@@ -158,7 +158,6 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice)
device->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
device->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
- device->prebuf = ds_snd_queue_max;
device->guid = GUID_NULL;
/* Set default wave format (may need it for waveOutOpen) */
diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c
index cb46301..112ce78 100644
--- a/dlls/dsound/dsound_main.c
+++ b/dlls/dsound/dsound_main.c
@@ -93,7 +93,6 @@ WCHAR wine_vxd_drv[] = { 'w','i','n','e','m','m','.','v','x','d', 0 };
/* All default settings, you most likely don't want to touch these, see wiki on UsefulRegistryKeys */
int ds_hel_buflen = 32768 * 2;
-int ds_snd_queue_max = 10;
static HINSTANCE instance;
/*
@@ -146,15 +145,10 @@ void setup_dsound_options(void)
if (!get_config_key( hkey, appkey, "HelBuflen", buffer, MAX_PATH ))
ds_hel_buflen = atoi(buffer);
- if (!get_config_key( hkey, appkey, "SndQueueMax", buffer, MAX_PATH ))
- ds_snd_queue_max = atoi(buffer);
-
-
if (appkey) RegCloseKey( appkey );
if (hkey) RegCloseKey( hkey );
TRACE("ds_hel_buflen = %d\n", ds_hel_buflen);
- TRACE("ds_snd_queue_max = %d\n", ds_snd_queue_max);
}
static const char * get_device_id(LPCGUID pGuid)
diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h
index 9c001ed..e180f7c 100644
--- a/dlls/dsound/dsound_private.h
+++ b/dlls/dsound/dsound_private.h
@@ -35,7 +35,6 @@
#define DS_MAX_CHANNELS 6
extern int ds_hel_buflen DECLSPEC_HIDDEN;
-extern int ds_snd_queue_max DECLSPEC_HIDDEN;
/*****************************************************************************
* Predeclare the interface implementation structures
@@ -76,10 +75,8 @@ struct DirectSoundDevice
DSCAPS drvcaps;
DWORD priolevel, sleeptime;
PWAVEFORMATEX pwfx, primary_pwfx;
- UINT playing_offs_bytes, in_mmdev_bytes, prebuf;
- DWORD fraglen;
LPBYTE buffer;
- DWORD writelead, buflen, state, playpos, mixpos;
+ DWORD writelead, buflen, aclen, fraglen, state, playpos, pad;
int nrofbuffers;
IDirectSoundBufferImpl** buffers;
RTL_RWLOCK buffer_list_lock;
@@ -213,7 +210,6 @@ HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device) DECLSPEC_HIDDEN;
HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device) DECLSPEC_HIDDEN;
HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device) DECLSPEC_HIDDEN;
HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device) DECLSPEC_HIDDEN;
-HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos) DECLSPEC_HIDDEN;
LPWAVEFORMATEX DSOUND_CopyFormat(LPCWAVEFORMATEX wfex) DECLSPEC_HIDDEN;
HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave) DECLSPEC_HIDDEN;
HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device) DECLSPEC_HIDDEN;
diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c
index 85ab14a..f650089 100644
--- a/dlls/dsound/mixer.c
+++ b/dlls/dsound/mixer.c
@@ -468,7 +468,7 @@ static void DSOUND_MixerVol(const IDirectSoundBufferImpl *dsb, INT frames)
* writepos = position (offset) in device buffer to write at
* fraglen = number of bytes to mix
*/
-static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
+static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, float *mix_buffer, DWORD writepos, DWORD fraglen)
{
INT len = fraglen;
float *ibuf;
@@ -493,7 +493,7 @@ static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWO
/* Apply volume if needed */
DSOUND_MixerVol(dsb, frames);
- mixieee32(ibuf, dsb->device->mix_buffer, frames * dsb->device->pwfx->nChannels);
+ mixieee32(ibuf, mix_buffer, frames * dsb->device->pwfx->nChannels);
/* check for notification positions */
if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY &&
@@ -517,7 +517,7 @@ static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWO
*
* Returns: the number of bytes beyond the writepos that were mixed.
*/
-static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mixlen)
+static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, float *mix_buffer, DWORD writepos, DWORD mixlen)
{
DWORD primary_done = 0;
@@ -544,7 +544,7 @@ static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mi
/* First try to mix to the end of the buffer if possible
* Theoretically it would allow for better optimization
*/
- primary_done += DSOUND_MixInBuffer(dsb, writepos, mixlen);
+ primary_done += DSOUND_MixInBuffer(dsb, mix_buffer, writepos, mixlen);
TRACE("total mixed data=%d\n", primary_done);
@@ -559,14 +559,12 @@ static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mi
* writepos = the current safe-to-write position in the primary buffer
* mixlen = the maximum amount to mix into the primary buffer
* (beyond the current writepos)
- * recover = true if the sound device may have been reset and the write
- * position in the device buffer changed
* all_stopped = reports back if all buffers have stopped
*
* Returns: the length beyond the writepos that was mixed to.
*/
-static void DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos, DWORD mixlen, BOOL recover, BOOL *all_stopped)
+static void DSOUND_MixToPrimary(const DirectSoundDevice *device, float *mix_buffer, DWORD writepos, DWORD mixlen, BOOL *all_stopped)
{
INT i;
IDirectSoundBufferImpl *dsb;
@@ -574,7 +572,7 @@ static void DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos,
/* unless we find a running buffer, all have stopped */
*all_stopped = TRUE;
- TRACE("(%d,%d,%d)\n", writepos, mixlen, recover);
+ TRACE("(%d,%d)\n", writepos, mixlen);
for (i = 0; i < device->nrofbuffers; i++) {
dsb = device->buffers[i];
@@ -594,7 +592,7 @@ static void DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos,
dsb->state = STATE_PLAYING;
/* mix next buffer into the main buffer */
- DSOUND_MixOne(dsb, writepos, mixlen);
+ DSOUND_MixOne(dsb, mix_buffer, writepos, mixlen);
*all_stopped = FALSE;
}
@@ -613,86 +611,27 @@ static void DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos,
* Returns: None
*/
-static void DSOUND_WaveQueue(DirectSoundDevice *device, BOOL force)
+static void DSOUND_WaveQueue(DirectSoundDevice *device, LPBYTE pos, DWORD bytes)
{
- DWORD prebuf_frames, prebuf_bytes, read_offs_bytes;
BYTE *buffer;
HRESULT hr;
TRACE("(%p)\n", device);
- read_offs_bytes = (device->playing_offs_bytes + device->in_mmdev_bytes) % device->buflen;
-
- TRACE("read_offs_bytes = %u, playing_offs_bytes = %u, in_mmdev_bytes: %u, prebuf = %u\n",
- read_offs_bytes, device->playing_offs_bytes, device->in_mmdev_bytes, device->prebuf);
-
- if (!force)
- {
- if(device->mixpos < device->playing_offs_bytes)
- prebuf_bytes = device->mixpos + device->buflen - device->playing_offs_bytes;
- else
- prebuf_bytes = device->mixpos - device->playing_offs_bytes;
- }
- else
- /* buffer the maximum amount of frags */
- prebuf_bytes = device->prebuf * device->fraglen;
-
- /* limit to the queue we have left */
- if(device->in_mmdev_bytes + prebuf_bytes > device->prebuf * device->fraglen)
- prebuf_bytes = device->prebuf * device->fraglen - device->in_mmdev_bytes;
-
- TRACE("prebuf_bytes = %u\n", prebuf_bytes);
-
- if(!prebuf_bytes)
- return;
-
- if(prebuf_bytes + read_offs_bytes > device->buflen){
- DWORD chunk_bytes = device->buflen - read_offs_bytes;
- prebuf_frames = chunk_bytes / device->pwfx->nBlockAlign;
- prebuf_bytes -= chunk_bytes;
- }else{
- prebuf_frames = prebuf_bytes / device->pwfx->nBlockAlign;
- prebuf_bytes = 0;
- }
-
- hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer);
+ hr = IAudioRenderClient_GetBuffer(device->render, bytes / device->pwfx->nBlockAlign, &buffer);
if(FAILED(hr)){
WARN("GetBuffer failed: %08x\n", hr);
- return;
+ goto done;
}
- memcpy(buffer, device->buffer + read_offs_bytes,
- prebuf_frames * device->pwfx->nBlockAlign);
+ memcpy(buffer, pos, bytes);
- hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0);
- if(FAILED(hr)){
+ hr = IAudioRenderClient_ReleaseBuffer(device->render, bytes / device->pwfx->nBlockAlign, 0);
+ if(FAILED(hr))
WARN("ReleaseBuffer failed: %08x\n", hr);
- return;
- }
-
- device->in_mmdev_bytes += prebuf_frames * device->pwfx->nBlockAlign;
-
- /* check if anything wrapped */
- if(prebuf_bytes > 0){
- prebuf_frames = prebuf_bytes / device->pwfx->nBlockAlign;
-
- hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer);
- if(FAILED(hr)){
- WARN("GetBuffer failed: %08x\n", hr);
- return;
- }
-
- memcpy(buffer, device->buffer, prebuf_frames * device->pwfx->nBlockAlign);
- hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0);
- if(FAILED(hr)){
- WARN("ReleaseBuffer failed: %08x\n", hr);
- return;
- }
- device->in_mmdev_bytes += prebuf_frames * device->pwfx->nBlockAlign;
- }
-
- TRACE("in_mmdev_bytes now = %i\n", device->in_mmdev_bytes);
+done:
+ device->pad += bytes;
}
/**
@@ -710,7 +649,8 @@ static void DSOUND_WaveQueue(DirectSoundDevice *device, BOOL force)
*/
static void DSOUND_PerformMix(DirectSoundDevice *device)
{
- UINT32 pad, to_mix_frags, to_mix_bytes;
+ UINT32 pad, maxq, writepos;
+ DWORD block;
HRESULT hr;
TRACE("(%p)\n", device);
@@ -724,147 +664,79 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
LeaveCriticalSection(&device->mixlock);
return;
}
-
- to_mix_frags = device->prebuf - (pad * device->pwfx->nBlockAlign + device->fraglen - 1) / device->fraglen;
-
- to_mix_bytes = to_mix_frags * device->fraglen;
-
- if(device->in_mmdev_bytes > 0){
- DWORD delta_bytes = min(to_mix_bytes, device->in_mmdev_bytes);
- device->in_mmdev_bytes -= delta_bytes;
- device->playing_offs_bytes += delta_bytes;
- device->playing_offs_bytes %= device->buflen;
+ block = device->pwfx->nBlockAlign;
+ pad *= block;
+ device->playpos += device->pad - pad;
+ device->playpos %= device->buflen;
+ device->pad = pad;
+
+ maxq = device->aclen - pad;
+ if(!maxq){
+ /* nothing to do! */
+ LeaveCriticalSection(&device->mixlock);
+ return;
}
+ if (maxq > device->fraglen * 3)
+ maxq = device->fraglen * 3;
+
+ writepos = (device->playpos + pad) % device->buflen;
if (device->priolevel != DSSCL_WRITEPRIMARY) {
- BOOL recover = FALSE, all_stopped = FALSE;
- DWORD playpos, writepos, writelead, maxq, prebuff_max, prebuff_left, size1, size2;
- LPVOID buf1, buf2;
+ BOOL all_stopped = FALSE;
int nfiller;
+ DWORD bpp = device->pwfx->wBitsPerSample>>3;
/* the sound of silence */
nfiller = device->pwfx->wBitsPerSample == 8 ? 128 : 0;
- /* get the position in the primary buffer */
- if (DSOUND_PrimaryGetPosition(device, &playpos, &writepos) != 0){
- LeaveCriticalSection(&(device->mixlock));
- return;
- }
-
- TRACE("primary playpos=%d, writepos=%d, clrpos=%d, mixpos=%d, buflen=%d\n",
- playpos,writepos,device->playpos,device->mixpos,device->buflen);
- assert(device->playpos < device->buflen);
-
- /* calc maximum prebuff */
- prebuff_max = (device->prebuf * device->fraglen);
-
- /* check how close we are to an underrun. It occurs when the writepos overtakes the mixpos */
- prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos);
- writelead = DSOUND_BufPtrDiff(device->buflen, writepos, playpos);
-
/* check for underrun. underrun occurs when the write position passes the mix position
* also wipe out just-played sound data */
- if((prebuff_left > prebuff_max) || (device->state == STATE_STOPPED) || (device->state == STATE_STARTING)){
- if (device->state == STATE_STOPPING || device->state == STATE_PLAYING)
- WARN("Probable buffer underrun\n");
- else TRACE("Buffer starting or buffer underrun\n");
-
- /* recover mixing for all buffers */
- recover = TRUE;
-
- /* reset mix position to write position */
- device->mixpos = writepos;
-
- ZeroMemory(device->buffer, device->buflen);
- } else if (playpos < device->playpos) {
- buf1 = device->buffer + device->playpos;
- buf2 = device->buffer;
- size1 = device->buflen - device->playpos;
- size2 = playpos;
- FillMemory(buf1, size1, nfiller);
- if (playpos && (!buf2 || !size2))
- FIXME("%d: (%d, %d)=>(%d, %d) There should be an additional buffer here!!\n", __LINE__, device->playpos, device->mixpos, playpos, writepos);
- FillMemory(buf2, size2, nfiller);
- } else {
- buf1 = device->buffer + device->playpos;
- buf2 = NULL;
- size1 = playpos - device->playpos;
- size2 = 0;
- FillMemory(buf1, size1, nfiller);
+ if (!pad)
+ WARN("Probable buffer underrun\n");
+ else if (device->state == STATE_STOPPED ||
+ device->state == STATE_STARTING) {
+ TRACE("Buffer restarting\n");
}
- device->playpos = playpos;
-
- /* find the maximum we can prebuffer from current write position */
- maxq = (writelead < prebuff_max) ? (prebuff_max - writelead) : 0;
- TRACE("prebuff_left = %d, prebuff_max = %dx%d=%d, writelead=%d\n",
- prebuff_left, device->prebuf, device->fraglen, prebuff_max, writelead);
-
- ZeroMemory(device->mix_buffer, device->mix_buffer_len);
+ memset(device->mix_buffer, nfiller, maxq);
/* do the mixing */
- DSOUND_MixToPrimary(device, writepos, maxq, recover, &all_stopped);
+ DSOUND_MixToPrimary(device, device->mix_buffer, writepos, maxq, &all_stopped);
- if (maxq + writepos > device->buflen)
- {
+ if (maxq + writepos > device->buflen) {
DWORD todo = device->buflen - writepos;
- DWORD offs_float = (todo / device->pwfx->nBlockAlign) * device->pwfx->nChannels;
- device->normfunction(device->mix_buffer, device->buffer + writepos, todo);
- device->normfunction(device->mix_buffer + offs_float, device->buffer, maxq - todo);
- }
- else
- device->normfunction(device->mix_buffer, device->buffer + writepos, maxq);
- /* update the mix position, taking wrap-around into account */
- device->mixpos = writepos + maxq;
- device->mixpos %= device->buflen;
-
- /* update prebuff left */
- prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos);
-
- /* check if have a whole fragment */
- if (prebuff_left >= device->fraglen){
+ device->normfunction(device->mix_buffer, device->buffer + writepos, todo);
+ DSOUND_WaveQueue(device, device->buffer + writepos, todo);
- /* update the wave queue */
- DSOUND_WaveQueue(device, FALSE);
+ device->normfunction(device->mix_buffer + todo / bpp, device->buffer, (maxq - todo));
+ DSOUND_WaveQueue(device, device->buffer, maxq - todo);
+ } else {
+ device->normfunction(device->mix_buffer, device->buffer + writepos, maxq);
+ DSOUND_WaveQueue(device, device->buffer + writepos, maxq);
+ }
- /* buffers are full. start playing if applicable */
- if(device->state == STATE_STARTING){
- TRACE("started primary buffer\n");
- if(DSOUND_PrimaryPlay(device) != DS_OK){
+ if (maxq) {
+ if (device->state == STATE_STARTING ||
+ device->state == STATE_STOPPED) {
+ if(DSOUND_PrimaryPlay(device) != DS_OK)
WARN("DSOUND_PrimaryPlay failed\n");
- }
- else{
- /* we are playing now */
+ else if (device->state == STATE_STARTING)
device->state = STATE_PLAYING;
- }
- }
-
- /* buffers are full. start stopping if applicable */
- if(device->state == STATE_STOPPED){
- TRACE("restarting primary buffer\n");
- if(DSOUND_PrimaryPlay(device) != DS_OK){
- WARN("DSOUND_PrimaryPlay failed\n");
- }
- else{
- /* start stopping again. as soon as there is no more data, it will stop */
+ else
device->state = STATE_STOPPING;
- }
}
- }
-
- /* if device was stopping, its for sure stopped when all buffers have stopped */
- else if (all_stopped && (device->state == STATE_STOPPING)) {
- TRACE("All buffers have stopped. Stopping primary buffer\n");
+ } else if (!pad && !maxq && (all_stopped == TRUE) &&
+ (device->state == STATE_STOPPING)) {
device->state = STATE_STOPPED;
-
- /* stop the primary buffer now */
DSOUND_PrimaryStop(device);
}
-
} else if (device->state != STATE_STOPPED) {
-
- DSOUND_WaveQueue(device, TRUE);
+ if (writepos + maxq > device->buflen) {
+ DSOUND_WaveQueue(device, device->buffer + writepos, device->buflen - writepos);
+ DSOUND_WaveQueue(device, device->buffer, writepos + maxq - device->buflen);
+ } else
+ DSOUND_WaveQueue(device, device->buffer + writepos, maxq);
/* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
if (device->state == STATE_STARTING) {
diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c
index 3f8a478..19a76b0 100644
--- a/dlls/dsound/primary.c
+++ b/dlls/dsound/primary.c
@@ -40,24 +40,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(dsound);
-static DWORD DSOUND_fraglen(DirectSoundDevice *device)
-{
- REFERENCE_TIME period;
- HRESULT hr;
- DWORD ret;
-
- hr = IAudioClient_GetDevicePeriod(device->client, &period, NULL);
- if(FAILED(hr)){
- /* just guess at 10ms */
- WARN("GetDevicePeriod failed: %08x\n", hr);
- ret = MulDiv(device->pwfx->nBlockAlign, device->pwfx->nSamplesPerSec, 100);
- }else
- ret = MulDiv(device->pwfx->nSamplesPerSec * device->pwfx->nBlockAlign, period, 10000000);
-
- ret -= ret % device->pwfx->nBlockAlign;
- return ret;
-}
-
static DWORD speaker_config_to_channel_mask(DWORD speaker_config)
{
switch (DSSPEAKER_CONFIG(speaker_config)) {
@@ -217,11 +199,10 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client
HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
{
- UINT prebuf_frames;
- REFERENCE_TIME prebuf_rt;
WAVEFORMATEX *wfx = NULL;
HRESULT hres;
- REFERENCE_TIME period;
+ REFERENCE_TIME period, buflen = 800000;
+ UINT32 frames;
DWORD period_ms;
TRACE("(%p, %d)\n", device, forcewave);
@@ -243,6 +224,12 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
device->volume = NULL;
}
+ if (device->pad) {
+ device->playpos += device->pad;
+ device->playpos %= device->buflen;
+ device->pad = 0;
+ }
+
hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient,
CLSCTX_INPROC_SERVER, NULL, (void **)&device->client);
if(FAILED(hres)) {
@@ -263,12 +250,9 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
HeapFree(GetProcessHeap(), 0, device->pwfx);
device->pwfx = wfx;
- prebuf_frames = device->prebuf * DSOUND_fraglen(device) / device->pwfx->nBlockAlign;
- prebuf_rt = (10000000 * (UINT64)prebuf_frames) / device->pwfx->nSamplesPerSec;
-
hres = IAudioClient_Initialize(device->client,
AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST |
- AUDCLNT_STREAMFLAGS_EVENTCALLBACK, prebuf_rt, 0, device->pwfx, NULL);
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buflen, 0, device->pwfx, NULL);
if(FAILED(hres)){
IAudioClient_Release(device->client);
device->client = NULL;
@@ -318,10 +302,19 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
hres = IAudioClient_GetStreamLatency(device->client, &period);
if (FAILED(hres)) {
WARN("GetStreamLatency failed with %08x\n", hres);
- period_ms = 10;
- } else
- period_ms = (period + 9999) / 10000;
- TRACE("period %u ms fraglen %u prebuf %u\n", period_ms, device->fraglen, device->prebuf);
+ period = 100000;
+ }
+ period_ms = (period + 9999) / 10000;
+
+ hres = IAudioClient_GetBufferSize(device->client, &frames);
+ if (FAILED(hres)) {
+ WARN("GetBufferSize failed with %08x\n", hres);
+ frames = (UINT64)device->pwfx->nSamplesPerSec * buflen / 10000000;
+ }
+
+ device->fraglen = MulDiv(device->pwfx->nSamplesPerSec, period, 10000000) * device->pwfx->nBlockAlign;
+ device->aclen = frames * device->pwfx->nBlockAlign;
+ TRACE("period %u ms fraglen %u buflen %u\n", period_ms, device->fraglen, device->aclen);
if (period_ms < 3)
device->sleeptime = 5;
@@ -339,17 +332,11 @@ HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
TRACE("(%p)\n", device);
- device->fraglen = DSOUND_fraglen(device);
-
/* on original windows, the buffer it set to a fixed size, no matter what the settings are.
on windows this size is always fixed (tested on win-xp) */
if (!device->buflen)
device->buflen = ds_hel_buflen;
device->buflen -= device->buflen % device->pwfx->nBlockAlign;
- while(device->buflen < device->fraglen * device->prebuf){
- device->buflen += ds_hel_buflen;
- device->buflen -= device->buflen % device->pwfx->nBlockAlign;
- }
HeapFree(GetProcessHeap(), 0, device->mix_buffer);
device->mix_buffer_len = (device->buflen / (device->pwfx->wBitsPerSample / 8)) * sizeof(float);
@@ -419,9 +406,6 @@ static void DSOUND_PrimaryClose(DirectSoundDevice *device)
if(FAILED(hr))
WARN("Stop failed: %08x\n", hr);
}
-
- /* clear the queue */
- device->in_mmdev_bytes = 0;
}
HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
@@ -496,32 +480,19 @@ HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
return DS_OK;
}
-HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos)
-{
- TRACE("(%p,%p,%p)\n", device, playpos, writepos);
-
- /* check if playpos was requested */
- if (playpos)
- *playpos = device->playing_offs_bytes;
-
- /* check if writepos was requested */
- if (writepos)
- /* the writepos is the first non-queued position */
- *writepos = (device->playing_offs_bytes + device->in_mmdev_bytes) % device->buflen;
-
- TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:-1, writepos?*writepos:-1, device, GetTickCount());
- return DS_OK;
-}
-
WAVEFORMATEX *DSOUND_CopyFormat(const WAVEFORMATEX *wfex)
{
WAVEFORMATEX *pwfx;
if(wfex->wFormatTag == WAVE_FORMAT_PCM){
pwfx = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEX));
+ if (!pwfx)
+ return NULL;
CopyMemory(pwfx, wfex, sizeof(PCMWAVEFORMAT));
pwfx->cbSize = 0;
}else{
pwfx = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEX) + wfex->cbSize);
+ if (!pwfx)
+ return NULL;
CopyMemory(pwfx, wfex, sizeof(WAVEFORMATEX) + wfex->cbSize);
}
@@ -608,8 +579,12 @@ done:
else
HeapFree(GetProcessHeap(), 0, old_fmt);
} else {
- HeapFree(GetProcessHeap(), 0, device->primary_pwfx);
- device->primary_pwfx = DSOUND_CopyFormat(passed_fmt);
+ WAVEFORMATEX *wfx = DSOUND_CopyFormat(passed_fmt);
+ if (wfx) {
+ HeapFree(GetProcessHeap(), 0, device->primary_pwfx);
+ device->primary_pwfx = wfx;
+ } else
+ err = DSERR_OUTOFMEMORY;
}
out:
@@ -840,7 +815,9 @@ static ULONG WINAPI PrimaryBufferImpl_Release(IDirectSoundBuffer *iface)
static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(IDirectSoundBuffer *iface,
DWORD *playpos, DWORD *writepos)
{
- HRESULT hres;
+ HRESULT hres = DS_OK;
+ UINT32 pad = 0;
+ UINT32 mixpos;
IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
DirectSoundDevice *device = This->device;
TRACE("(%p,%p,%p)\n", iface, playpos, writepos);
@@ -848,17 +825,23 @@ static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(IDirectSoundBuffer *i
/* **** */
EnterCriticalSection(&(device->mixlock));
- hres = DSOUND_PrimaryGetPosition(device, playpos, writepos);
+ if (device->client)
+ hres = IAudioClient_GetCurrentPadding(device->client, &pad);
if (hres != DS_OK) {
- WARN("DSOUND_PrimaryGetPosition failed\n");
+ WARN("IAudioClient_GetCurrentPadding failed\n");
LeaveCriticalSection(&(device->mixlock));
return hres;
}
+ mixpos = (device->playpos + pad * device->pwfx->nBlockAlign) % device->buflen;
+ if (playpos)
+ *playpos = mixpos;
if (writepos) {
- if (device->state != STATE_STOPPED)
+ *writepos = mixpos;
+ if (device->state != STATE_STOPPED) {
/* apply the documented 10ms lead to writepos */
*writepos += device->writelead;
- while (*writepos >= device->buflen) *writepos -= device->buflen;
+ *writepos %= device->buflen;
+ }
}
LeaveCriticalSection(&(device->mixlock));
--
2.3.5
From 41b69b7de5b6036fe6a8460a53b9c62a28f69adb Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:33 +0100
Subject: dsound: mix float natively
---
dlls/dsound/mixer.c | 43 ++++++++++++++++++++++++++++++++-----------
1 file changed, 32 insertions(+), 11 deletions(-)
diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c
index f650089..59ee21d 100644
--- a/dlls/dsound/mixer.c
+++ b/dlls/dsound/mixer.c
@@ -684,6 +684,7 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
if (device->priolevel != DSSCL_WRITEPRIMARY) {
BOOL all_stopped = FALSE;
int nfiller;
+ BOOL native = device->normfunction == normfunctions[4];
DWORD bpp = device->pwfx->wBitsPerSample>>3;
/* the sound of silence */
@@ -698,22 +699,42 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
TRACE("Buffer restarting\n");
}
- memset(device->mix_buffer, nfiller, maxq);
+ if (native) {
+ void *buffer = NULL;
- /* do the mixing */
- DSOUND_MixToPrimary(device, device->mix_buffer, writepos, maxq, &all_stopped);
+ hr = IAudioRenderClient_GetBuffer(device->render, maxq / block, (void*)&buffer);
+ if(FAILED(hr)){
+ WARN("GetBuffer failed: %08x\n", hr);
+ LeaveCriticalSection(&device->mixlock);
+ return;
+ }
+ memset(buffer, nfiller, maxq);
- if (maxq + writepos > device->buflen) {
- DWORD todo = device->buflen - writepos;
+ DSOUND_MixToPrimary(device, buffer, writepos, maxq, &all_stopped);
- device->normfunction(device->mix_buffer, device->buffer + writepos, todo);
- DSOUND_WaveQueue(device, device->buffer + writepos, todo);
+ hr = IAudioRenderClient_ReleaseBuffer(device->render, maxq / block, 0);
+ if(FAILED(hr))
+ ERR("ReleaseBuffer failed: %08x\n", hr);
- device->normfunction(device->mix_buffer + todo / bpp, device->buffer, (maxq - todo));
- DSOUND_WaveQueue(device, device->buffer, maxq - todo);
+ device->pad += maxq;
} else {
- device->normfunction(device->mix_buffer, device->buffer + writepos, maxq);
- DSOUND_WaveQueue(device, device->buffer + writepos, maxq);
+ memset(device->mix_buffer, nfiller, maxq);
+
+ /* do the mixing */
+ DSOUND_MixToPrimary(device, device->mix_buffer, writepos, maxq, &all_stopped);
+
+ if (maxq + writepos > device->buflen) {
+ DWORD todo = device->buflen - writepos;
+
+ device->normfunction(device->mix_buffer, device->buffer + writepos, todo);
+ DSOUND_WaveQueue(device, device->buffer + writepos, todo);
+
+ device->normfunction(device->mix_buffer + todo / bpp, device->buffer, (maxq - todo));
+ DSOUND_WaveQueue(device, device->buffer, maxq - todo);
+ } else {
+ device->normfunction(device->mix_buffer, device->buffer + writepos, maxq);
+ DSOUND_WaveQueue(device, device->buffer + writepos, maxq);
+ }
}
if (maxq) {
--
2.3.5
From 0158234aec95c77b0ec0a549cc97a05d5a57b0a5 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:33 +0100
Subject: dsound: fixup DSOUND_WaveQueue checks
If you're going to be silly, you should go all the way. :P
---
dlls/dsound/mixer.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c
index 59ee21d..c763650 100644
--- a/dlls/dsound/mixer.c
+++ b/dlls/dsound/mixer.c
@@ -621,16 +621,18 @@ static void DSOUND_WaveQueue(DirectSoundDevice *device, LPBYTE pos, DWORD bytes)
hr = IAudioRenderClient_GetBuffer(device->render, bytes / device->pwfx->nBlockAlign, &buffer);
if(FAILED(hr)){
WARN("GetBuffer failed: %08x\n", hr);
- goto done;
+ return;
}
memcpy(buffer, pos, bytes);
hr = IAudioRenderClient_ReleaseBuffer(device->render, bytes / device->pwfx->nBlockAlign, 0);
- if(FAILED(hr))
- WARN("ReleaseBuffer failed: %08x\n", hr);
+ if(FAILED(hr)) {
+ ERR("ReleaseBuffer failed: %08x\n", hr);
+ IAudioRenderClient_ReleaseBuffer(device->render, 0, 0);
+ return;
+ }
-done:
device->pad += bytes;
}
--
2.3.5
From 1287957bb675cbc1729bd14bc64af0be2267a8cc Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <wine@mblankhorst.nl>
Date: Mon, 23 Mar 2015 09:14:33 +0100
Subject: dsound: fixup IDirectSoundCaptureBuffer_QueryInterface
Don't expose v8, and actually respond to iunknown.
---
dlls/dsound/capture.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/dlls/dsound/capture.c b/dlls/dsound/capture.c
index 0764261..412b59e 100644
--- a/dlls/dsound/capture.c
+++ b/dlls/dsound/capture.c
@@ -50,7 +50,7 @@ typedef struct IDirectSoundCaptureBufferImpl
IDirectSoundCaptureBuffer8 IDirectSoundCaptureBuffer8_iface;
IDirectSoundNotify IDirectSoundNotify_iface;
LONG numIfaces; /* "in use interfaces" refcount */
- LONG ref, refn;
+ LONG ref, refn, has_dsc8;
/* IDirectSoundCaptureBuffer fields */
DirectSoundCaptureDevice *device;
DSCBUFFERDESC *pdscbd;
@@ -240,8 +240,9 @@ static HRESULT WINAPI IDirectSoundCaptureBufferImpl_QueryInterface(IDirectSoundC
*ppobj = NULL;
- if ( IsEqualGUID( &IID_IDirectSoundCaptureBuffer, riid ) ||
- IsEqualGUID( &IID_IDirectSoundCaptureBuffer8, riid ) ) {
+ if ( IsEqualIID( &IID_IUnknown, riid ) ||
+ IsEqualIID( &IID_IDirectSoundCaptureBuffer, riid ) ||
+ (This->has_dsc8 && IsEqualIID( &IID_IDirectSoundCaptureBuffer8, riid )) ) {
IDirectSoundCaptureBuffer8_AddRef(iface);
*ppobj = iface;
return S_OK;
@@ -1233,6 +1234,8 @@ static HRESULT WINAPI IDirectSoundCaptureImpl_CreateCaptureBuffer(IDirectSoundCa
if (hr != DS_OK)
WARN("IDirectSoundCaptureBufferImpl_Create failed\n");
+ else
+ This->device->capture_buffer->has_dsc8 = This->has_dsc8;
return hr;
}
--
2.3.5
From 018438239d965616c5a41bc682d5c72d1242a78d Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 23 Mar 2015 09:14:34 +0100
Subject: dsound: fix format handling on invalid format to never fail
For the users still on oss4 this is probably useful. This is more of
a theoretical concern than practical, since nobody uses primary mode.
And even if someone did, they would have to find a format that was
unsupported, like IEEE float would probably be the easiest to trigger.
This patch now forces everything to a single call to DSOUND_ReopenDevice,
which will either fail and keep previous state, or succeed.
---
dlls/dsound/dsound.c | 50 +++----
dlls/dsound/dsound_convert.c | 18 ---
dlls/dsound/dsound_main.c | 2 +-
dlls/dsound/dsound_private.h | 5 +-
dlls/dsound/mixer.c | 53 +++----
dlls/dsound/primary.c | 341 +++++++++++++++++++------------------------
6 files changed, 193 insertions(+), 276 deletions(-)
diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c
index b15b366..ed30eae 100644
--- a/dlls/dsound/dsound.c
+++ b/dlls/dsound/dsound.c
@@ -161,24 +161,20 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice)
device->guid = GUID_NULL;
/* Set default wave format (may need it for waveOutOpen) */
- device->pwfx = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEFORMATEXTENSIBLE));
device->primary_pwfx = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEFORMATEXTENSIBLE));
- if (!device->pwfx || !device->primary_pwfx) {
+ if (!device->primary_pwfx) {
WARN("out of memory\n");
- HeapFree(GetProcessHeap(),0,device->primary_pwfx);
- HeapFree(GetProcessHeap(),0,device->pwfx);
HeapFree(GetProcessHeap(),0,device);
return DSERR_OUTOFMEMORY;
}
- device->pwfx->wFormatTag = WAVE_FORMAT_PCM;
- device->pwfx->nSamplesPerSec = 22050;
- device->pwfx->wBitsPerSample = 8;
- device->pwfx->nChannels = 2;
- device->pwfx->nBlockAlign = device->pwfx->wBitsPerSample * device->pwfx->nChannels / 8;
- device->pwfx->nAvgBytesPerSec = device->pwfx->nSamplesPerSec * device->pwfx->nBlockAlign;
- device->pwfx->cbSize = 0;
- memcpy(device->primary_pwfx, device->pwfx, sizeof(*device->pwfx));
+ device->primary_pwfx->wFormatTag = WAVE_FORMAT_PCM;
+ device->primary_pwfx->nSamplesPerSec = 22050;
+ device->primary_pwfx->wBitsPerSample = 8;
+ device->primary_pwfx->nChannels = 2;
+ device->primary_pwfx->nBlockAlign = device->primary_pwfx->wBitsPerSample * device->primary_pwfx->nChannels / 8;
+ device->primary_pwfx->nAvgBytesPerSec = device->primary_pwfx->nSamplesPerSec * device->primary_pwfx->nBlockAlign;
+ device->primary_pwfx->cbSize = 0;
InitializeCriticalSection(&(device->mixlock));
device->mixlock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": DirectSoundDevice.mixlock");
@@ -228,17 +224,16 @@ static ULONG DirectSoundDevice_Release(DirectSoundDevice * device)
if (hr != DS_OK)
WARN("DSOUND_PrimaryDestroy failed\n");
- if(device->client)
+ if(device->client) {
+ IAudioClient_Stop(device->client);
IAudioClient_Release(device->client);
+ }
if(device->render)
IAudioRenderClient_Release(device->render);
- if(device->clock)
- IAudioClock_Release(device->clock);
if(device->volume)
IAudioStreamVolume_Release(device->volume);
HeapFree(GetProcessHeap(), 0, device->tmp_buffer);
- HeapFree(GetProcessHeap(), 0, device->mix_buffer);
HeapFree(GetProcessHeap(), 0, device->buffer);
RtlDeleteResource(&device->buffer_list_lock);
device->mixlock.DebugInfo->Spare[0] = 0;
@@ -324,6 +319,7 @@ static HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGU
device->mmdevice = mmdevice;
device->guid = devGUID;
device->sleepev = CreateEventW(0, 0, 0, 0);
+ device->buflen = ds_hel_buflen;
hr = DSOUND_ReopenDevice(device, FALSE);
if (FAILED(hr))
@@ -382,13 +378,9 @@ static HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGU
ZeroMemory(&device->volpan, sizeof(device->volpan));
- hr = DSOUND_PrimaryCreate(device);
- if (hr == DS_OK) {
- device->thread_finished = CreateEventW(0, 0, 0, 0);
- device->thread = CreateThread(0, 0, DSOUND_mixthread, device, 0, 0);
- SetThreadPriority(device->thread, THREAD_PRIORITY_TIME_CRITICAL);
- } else
- WARN("DSOUND_PrimaryCreate failed: %08x\n", hr);
+ device->thread_finished = CreateEventW(0, 0, 0, 0);
+ device->thread = CreateThread(0, 0, DSOUND_mixthread, device, 0, 0);
+ SetThreadPriority(device->thread, THREAD_PRIORITY_TIME_CRITICAL);
*ppDevice = device;
list_add_tail(&DSOUND_renderers, &device->entry);
@@ -842,7 +834,6 @@ static HRESULT WINAPI IDirectSound8Impl_SetCooperativeLevel(IDirectSound8 *iface
{
IDirectSoundImpl *This = impl_from_IDirectSound8(iface);
DirectSoundDevice *device = This->device;
- DWORD oldlevel;
HRESULT hr = S_OK;
TRACE("(%p,%p,%s)\n", This, hwnd, dumpCooperativeLevel(level));
@@ -859,15 +850,10 @@ static HRESULT WINAPI IDirectSound8Impl_SetCooperativeLevel(IDirectSound8 *iface
RtlAcquireResourceExclusive(&device->buffer_list_lock, TRUE);
EnterCriticalSection(&device->mixlock);
- oldlevel = device->priolevel;
- device->priolevel = level;
- if ((level == DSSCL_WRITEPRIMARY) != (oldlevel == DSSCL_WRITEPRIMARY)) {
+ if ((level == DSSCL_WRITEPRIMARY) != (device->priolevel == DSSCL_WRITEPRIMARY))
hr = DSOUND_ReopenDevice(device, level == DSSCL_WRITEPRIMARY);
- if (FAILED(hr))
- device->priolevel = oldlevel;
- else
- DSOUND_PrimaryOpen(device);
- }
+ if (SUCCEEDED(hr))
+ device->priolevel = level;
LeaveCriticalSection(&device->mixlock);
RtlReleaseResource(&device->buffer_list_lock);
return hr;
diff --git a/dlls/dsound/dsound_convert.c b/dlls/dsound/dsound_convert.c
index 5accba6..af81c91 100644
--- a/dlls/dsound/dsound_convert.c
+++ b/dlls/dsound/dsound_convert.c
@@ -263,27 +263,9 @@ static void norm32(float *src, INT *dst, unsigned len)
}
}
-static void normieee32(float *src, float *dst, unsigned len)
-{
- TRACE("%p - %p %d\n", src, dst, len);
- len /= 4;
- while (len--)
- {
- if(*src > 1)
- *dst = 1;
- else if(*src < -1)
- *dst = -1;
- else
- *dst = *src;
- ++dst;
- ++src;
- }
-}
-
const normfunc normfunctions[5] = {
(normfunc)norm8,
(normfunc)norm16,
(normfunc)norm24,
(normfunc)norm32,
- (normfunc)normieee32
};
diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c
index 112ce78..e89d857 100644
--- a/dlls/dsound/dsound_main.c
+++ b/dlls/dsound/dsound_main.c
@@ -92,7 +92,7 @@ GUID DSOUND_capture_guids[MAXWAVEDRIVERS];
WCHAR wine_vxd_drv[] = { 'w','i','n','e','m','m','.','v','x','d', 0 };
/* All default settings, you most likely don't want to touch these, see wiki on UsefulRegistryKeys */
-int ds_hel_buflen = 32768 * 2;
+int ds_hel_buflen = 32768;
static HINSTANCE instance;
/*
diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h
index e180f7c..f142b7c 100644
--- a/dlls/dsound/dsound_private.h
+++ b/dlls/dsound/dsound_private.h
@@ -87,7 +87,7 @@ struct DirectSoundDevice
int speaker_num[DS_MAX_CHANNELS];
int num_speakers;
int lfe_channel;
- float *mix_buffer, *tmp_buffer;
+ float *tmp_buffer;
DWORD tmp_buffer_len, mix_buffer_len;
DSVOLUMEPAN volpan;
@@ -100,7 +100,6 @@ struct DirectSoundDevice
IMMDevice *mmdevice;
IAudioClient *client;
- IAudioClock *clock;
IAudioStreamVolume *volume;
IAudioRenderClient *render;
@@ -206,13 +205,11 @@ void DSOUND_ParseSpeakerConfig(DirectSoundDevice *device) DECLSPEC_HIDDEN;
/* primary.c */
-HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device) DECLSPEC_HIDDEN;
HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device) DECLSPEC_HIDDEN;
HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device) DECLSPEC_HIDDEN;
HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device) DECLSPEC_HIDDEN;
LPWAVEFORMATEX DSOUND_CopyFormat(LPCWAVEFORMATEX wfex) DECLSPEC_HIDDEN;
HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave) DECLSPEC_HIDDEN;
-HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device) DECLSPEC_HIDDEN;
HRESULT primarybuffer_create(DirectSoundDevice *device, IDirectSoundBufferImpl **ppdsb,
const DSBUFFERDESC *dsbd) DECLSPEC_HIDDEN;
void primarybuffer_destroy(IDirectSoundBufferImpl *This) DECLSPEC_HIDDEN;
diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c
index c763650..155e378 100644
--- a/dlls/dsound/mixer.c
+++ b/dlls/dsound/mixer.c
@@ -646,8 +646,7 @@ static void DSOUND_WaveQueue(DirectSoundDevice *device, LPBYTE pos, DWORD bytes)
* secondary->buffer (secondary format)
* =[Resample]=> device->tmp_buffer (float format)
* =[Volume]=> device->tmp_buffer (float format)
- * =[Mix]=> device->mix_buffer (float format)
- * =[Reformat]=> device->buffer (device format)
+ * =[Reformat]=> device->buffer (device format, skipped on float)
*/
static void DSOUND_PerformMix(DirectSoundDevice *device)
{
@@ -686,8 +685,7 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
if (device->priolevel != DSSCL_WRITEPRIMARY) {
BOOL all_stopped = FALSE;
int nfiller;
- BOOL native = device->normfunction == normfunctions[4];
- DWORD bpp = device->pwfx->wBitsPerSample>>3;
+ void *buffer = NULL;
/* the sound of silence */
nfiller = device->pwfx->wBitsPerSample == 8 ? 128 : 0;
@@ -701,43 +699,30 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
TRACE("Buffer restarting\n");
}
- if (native) {
- void *buffer = NULL;
+ hr = IAudioRenderClient_GetBuffer(device->render, maxq / block, (void*)&buffer);
+ if(FAILED(hr)){
+ WARN("GetBuffer failed: %08x\n", hr);
+ LeaveCriticalSection(&device->mixlock);
+ return;
+ }
- hr = IAudioRenderClient_GetBuffer(device->render, maxq / block, (void*)&buffer);
- if(FAILED(hr)){
- WARN("GetBuffer failed: %08x\n", hr);
- LeaveCriticalSection(&device->mixlock);
- return;
- }
- memset(buffer, nfiller, maxq);
+ memset(buffer, nfiller, maxq);
+ if (!device->normfunction)
DSOUND_MixToPrimary(device, buffer, writepos, maxq, &all_stopped);
-
- hr = IAudioRenderClient_ReleaseBuffer(device->render, maxq / block, 0);
- if(FAILED(hr))
- ERR("ReleaseBuffer failed: %08x\n", hr);
-
- device->pad += maxq;
- } else {
- memset(device->mix_buffer, nfiller, maxq);
+ else {
/* do the mixing */
- DSOUND_MixToPrimary(device, device->mix_buffer, writepos, maxq, &all_stopped);
+ DSOUND_MixToPrimary(device, (float*)device->buffer, writepos, maxq, &all_stopped);
- if (maxq + writepos > device->buflen) {
- DWORD todo = device->buflen - writepos;
+ device->normfunction(device->buffer, buffer, maxq);
+ }
- device->normfunction(device->mix_buffer, device->buffer + writepos, todo);
- DSOUND_WaveQueue(device, device->buffer + writepos, todo);
+ hr = IAudioRenderClient_ReleaseBuffer(device->render, maxq / block, 0);
+ if(FAILED(hr))
+ ERR("ReleaseBuffer failed: %08x\n", hr);
- device->normfunction(device->mix_buffer + todo / bpp, device->buffer, (maxq - todo));
- DSOUND_WaveQueue(device, device->buffer, maxq - todo);
- } else {
- device->normfunction(device->mix_buffer, device->buffer + writepos, maxq);
- DSOUND_WaveQueue(device, device->buffer + writepos, maxq);
- }
- }
+ device->pad += maxq;
if (maxq) {
if (device->state == STATE_STARTING ||
@@ -755,6 +740,8 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
DSOUND_PrimaryStop(device);
}
} else if (device->state != STATE_STOPPED) {
+ if (maxq > device->buflen)
+ maxq = device->buflen;
if (writepos + maxq > device->buflen) {
DSOUND_WaveQueue(device, device->buffer + writepos, device->buflen - writepos);
DSOUND_WaveQueue(device, device->buffer, writepos + maxq - device->buflen);
diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c
index 19a76b0..4bfae7e 100644
--- a/dlls/dsound/primary.c
+++ b/dlls/dsound/primary.c
@@ -197,16 +197,8 @@ static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client
return S_OK;
}
-HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
+static void DSOUND_ReleaseDevice(DirectSoundDevice *device)
{
- WAVEFORMATEX *wfx = NULL;
- HRESULT hres;
- REFERENCE_TIME period, buflen = 800000;
- UINT32 frames;
- DWORD period_ms;
-
- TRACE("(%p, %d)\n", device, forcewave);
-
if(device->client){
IAudioClient_Release(device->client);
device->client = NULL;
@@ -215,10 +207,6 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
IAudioRenderClient_Release(device->render);
device->render = NULL;
}
- if(device->clock){
- IAudioClock_Release(device->clock);
- device->clock = NULL;
- }
if(device->volume){
IAudioStreamVolume_Release(device->volume);
device->volume = NULL;
@@ -229,92 +217,171 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
device->playpos %= device->buflen;
device->pad = 0;
}
+}
- hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient,
- CLSCTX_INPROC_SERVER, NULL, (void **)&device->client);
- if(FAILED(hres)) {
- WARN("Activate failed: %08x\n", hres);
- return hres;
- }
+static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device, WAVEFORMATEX *wfx, DWORD aclen, BOOL forcewave)
+{
+ IDirectSoundBufferImpl** dsb = device->buffers;
+ LPBYTE newbuf;
+ DWORD new_buflen;
+ BOOL mixfloat = FALSE;
+ int i;
- device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, 0);
+ TRACE("(%p)\n", device);
- DSOUND_ParseSpeakerConfig(device);
+ new_buflen = device->buflen;
+ new_buflen -= new_buflen % wfx->nBlockAlign;
- hres = DSOUND_WaveFormat(device, device->client, forcewave, &wfx);
- if (FAILED(hres)) {
- IAudioClient_Release(device->client);
- device->client = NULL;
- return hres;
+ if (wfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
+ (wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
+ IsEqualGUID(&((WAVEFORMATEXTENSIBLE*)wfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
+ mixfloat = TRUE;
+
+ /* reallocate emulated primary buffer */
+ if (forcewave) {
+ if (device->buffer)
+ newbuf = HeapReAlloc(GetProcessHeap(), 0, device->buffer, new_buflen);
+ else
+ newbuf = HeapAlloc(GetProcessHeap(), 0, new_buflen);
+
+ if (!newbuf) {
+ ERR("failed to allocate primary buffer\n");
+ return DSERR_OUTOFMEMORY;
+ }
+ device->mix_buffer_len = 0;
+ } else if (!mixfloat) {
+ DWORD alloc_len = aclen / (wfx->nBlockAlign / 8) * sizeof(float);
+
+ if (device->buffer)
+ newbuf = HeapReAlloc(GetProcessHeap(), 0, device->buffer, alloc_len);
+ else
+ newbuf = HeapAlloc(GetProcessHeap(), 0, alloc_len);
+
+ if (!newbuf) {
+ ERR("failed to allocate primary buffer\n");
+ return DSERR_OUTOFMEMORY;
+ }
+ device->mix_buffer_len = alloc_len;
+ } else {
+ HeapFree(GetProcessHeap(), 0, device->buffer);
+ newbuf = NULL;
+ device->mix_buffer_len = 0;
}
+
+ device->buffer = newbuf;
+ device->buflen = new_buflen;
HeapFree(GetProcessHeap(), 0, device->pwfx);
device->pwfx = wfx;
- hres = IAudioClient_Initialize(device->client,
- AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST |
- AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buflen, 0, device->pwfx, NULL);
- if(FAILED(hres)){
- IAudioClient_Release(device->client);
- device->client = NULL;
- WARN("Initialize failed: %08x\n", hres);
- return hres;
+ if (device->state == STATE_PLAYING)
+ device->state = STATE_STARTING;
+ else if (device->state == STATE_STOPPING)
+ device->state = STATE_STOPPED;
+
+ device->writelead = (wfx->nSamplesPerSec / 100) * wfx->nBlockAlign;
+
+ TRACE("buflen: %u, fraglen: %u, mix_buffer_len: %u\n",
+ device->buflen, device->fraglen, device->mix_buffer_len);
+
+ if (!forcewave && !mixfloat)
+ device->normfunction = normfunctions[wfx->nBlockAlign/8 - 1];
+ else
+ device->normfunction = NULL;
+
+ if (device->mix_buffer_len)
+ FillMemory(device->buffer, device->mix_buffer_len, 0);
+ else if (device->buffer)
+ FillMemory(device->buffer, device->buflen, (wfx->wBitsPerSample == 8) ? 128 : 0);
+ device->playpos = 0;
+
+ for (i = 0; i < device->nrofbuffers; i++) {
+ RtlAcquireResourceExclusive(&dsb[i]->lock, TRUE);
+ DSOUND_RecalcFormat(dsb[i]);
+ RtlReleaseResource(&dsb[i]->lock);
}
- IAudioClient_SetEventHandle(device->client, device->sleepev);
- hres = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
- (void**)&device->render);
+ return DS_OK;
+}
+
+HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
+{
+ HRESULT hres;
+ REFERENCE_TIME period;
+ UINT32 frames;
+ DWORD period_ms;
+ IAudioClient *client = NULL;
+ IAudioRenderClient *render = NULL;
+ IAudioStreamVolume *volume = NULL;
+ DWORD fraglen, aclen;
+ WAVEFORMATEX *wfx = NULL;
+ DWORD oldspeakerconfig = device->speaker_config;
+
+ TRACE("(%p, %d)\n", device, forcewave);
+
+ hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient,
+ CLSCTX_INPROC_SERVER, NULL, (void **)&client);
if(FAILED(hres)){
- IAudioClient_Release(device->client);
- device->client = NULL;
- WARN("GetService failed: %08x\n", hres);
+ WARN("Activate failed: %08x\n", hres);
return hres;
}
- hres = IAudioClient_GetService(device->client, &IID_IAudioClock,
- (void**)&device->clock);
- if(FAILED(hres)){
- IAudioClient_Release(device->client);
- IAudioRenderClient_Release(device->render);
- device->client = NULL;
- device->render = NULL;
- WARN("GetService failed: %08x\n", hres);
+ hres = DSOUND_WaveFormat(device, client, forcewave, &wfx);
+ if (FAILED(hres)) {
+ IAudioClient_Release(client);
return hres;
}
- hres = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
- (void**)&device->volume);
+ hres = IAudioClient_Initialize(client,
+ AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST |
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 800000, 0, wfx, NULL);
if(FAILED(hres)){
- IAudioClient_Release(device->client);
- IAudioRenderClient_Release(device->render);
- IAudioClock_Release(device->clock);
- device->client = NULL;
- device->render = NULL;
- device->clock = NULL;
- WARN("GetService failed: %08x\n", hres);
+ IAudioClient_Release(client);
+ ERR("Initialize failed: %08x\n", hres);
return hres;
}
- /* Now kick off the timer so the event fires periodically */
- hres = IAudioClient_Start(device->client);
- if (FAILED(hres))
- WARN("starting failed with %08x\n", hres);
+ IAudioClient_SetEventHandle(client, device->sleepev);
+
+ hres = IAudioClient_GetService(client, &IID_IAudioRenderClient, (void**)&render);
+ if(FAILED(hres))
+ goto err_service;
+
+ hres = IAudioClient_GetService(client, &IID_IAudioStreamVolume, (void**)&volume);
+ if(FAILED(hres))
+ goto err_service;
- hres = IAudioClient_GetStreamLatency(device->client, &period);
+ /* Now kick off the timer so the event fires periodically */
+ hres = IAudioClient_Start(client);
+ if (FAILED(hres)) {
+ WARN("Start failed with %08x\n", hres);
+ goto err;
+ }
+ hres = IAudioClient_GetStreamLatency(client, &period);
if (FAILED(hres)) {
WARN("GetStreamLatency failed with %08x\n", hres);
- period = 100000;
+ goto err;
}
- period_ms = (period + 9999) / 10000;
-
- hres = IAudioClient_GetBufferSize(device->client, &frames);
+ hres = IAudioClient_GetBufferSize(client, &frames);
if (FAILED(hres)) {
WARN("GetBufferSize failed with %08x\n", hres);
- frames = (UINT64)device->pwfx->nSamplesPerSec * buflen / 10000000;
+ goto err;
}
- device->fraglen = MulDiv(device->pwfx->nSamplesPerSec, period, 10000000) * device->pwfx->nBlockAlign;
- device->aclen = frames * device->pwfx->nBlockAlign;
- TRACE("period %u ms fraglen %u buflen %u\n", period_ms, device->fraglen, device->aclen);
+ period_ms = (period + 9999) / 10000;
+ fraglen = MulDiv(wfx->nSamplesPerSec, period, 10000000) * wfx->nBlockAlign;
+ aclen = frames * wfx->nBlockAlign;
+ TRACE("period %u ms fraglen %u buflen %u\n", period_ms, fraglen, aclen);
+
+ hres = DSOUND_PrimaryOpen(device, wfx, aclen, forcewave);
+ if(FAILED(hres))
+ goto err;
+
+ DSOUND_ReleaseDevice(device);
+ device->client = client;
+ device->render = render;
+ device->volume = volume;
+ device->fraglen = fraglen;
+ device->aclen = aclen;
if (period_ms < 3)
device->sleeptime = 5;
@@ -322,107 +389,20 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
device->sleeptime = period_ms * 5 / 2;
return S_OK;
-}
-
-HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
-{
- IDirectSoundBufferImpl** dsb = device->buffers;
- LPBYTE newbuf;
- int i;
-
- TRACE("(%p)\n", device);
-
- /* on original windows, the buffer it set to a fixed size, no matter what the settings are.
- on windows this size is always fixed (tested on win-xp) */
- if (!device->buflen)
- device->buflen = ds_hel_buflen;
- device->buflen -= device->buflen % device->pwfx->nBlockAlign;
-
- HeapFree(GetProcessHeap(), 0, device->mix_buffer);
- device->mix_buffer_len = (device->buflen / (device->pwfx->wBitsPerSample / 8)) * sizeof(float);
- device->mix_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, device->mix_buffer_len);
- if (!device->mix_buffer)
- return DSERR_OUTOFMEMORY;
-
- if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
- else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
-
- /* reallocate emulated primary buffer */
- if (device->buffer)
- newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer, device->buflen);
- else
- newbuf = HeapAlloc(GetProcessHeap(),0, device->buflen);
-
- if (!newbuf) {
- ERR("failed to allocate primary buffer\n");
- return DSERR_OUTOFMEMORY;
- /* but the old buffer might still exist and must be re-prepared */
- }
-
- device->writelead = (device->pwfx->nSamplesPerSec / 100) * device->pwfx->nBlockAlign;
-
- device->buffer = newbuf;
-
- TRACE("buflen: %u, fraglen: %u, mix_buffer_len: %u\n",
- device->buflen, device->fraglen, device->mix_buffer_len);
-
- if(device->pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
- (device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
- IsEqualGUID(&((WAVEFORMATEXTENSIBLE*)device->pwfx)->SubFormat,
- &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
- device->normfunction = normfunctions[4];
- else
- device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
-
- FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
- FillMemory(device->mix_buffer, device->mix_buffer_len, 0);
- device->playpos = 0;
- if (device->pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
- (device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
- IsEqualGUID(&((WAVEFORMATEXTENSIBLE*)device->pwfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
- device->normfunction = normfunctions[4];
- else
- device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
-
- for (i = 0; i < device->nrofbuffers; i++) {
- RtlAcquireResourceExclusive(&dsb[i]->lock, TRUE);
- DSOUND_RecalcFormat(dsb[i]);
- RtlReleaseResource(&dsb[i]->lock);
- }
-
- return DS_OK;
-}
-
-
-static void DSOUND_PrimaryClose(DirectSoundDevice *device)
-{
- HRESULT hr;
-
- TRACE("(%p)\n", device);
-
- if(device->client){
- hr = IAudioClient_Stop(device->client);
- if(FAILED(hr))
- WARN("Stop failed: %08x\n", hr);
- }
-}
-
-HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
-{
- HRESULT err = DS_OK;
- TRACE("(%p)\n", device);
-
- device->buflen = ds_hel_buflen;
- err = DSOUND_PrimaryOpen(device);
-
- if (err != DS_OK) {
- WARN("DSOUND_PrimaryOpen failed\n");
- return err;
- }
-
- device->state = STATE_STOPPED;
- return DS_OK;
+err_service:
+ ERR("GetService failed: %08x\n", hres);
+err:
+ device->speaker_config = oldspeakerconfig;
+ DSOUND_ParseSpeakerConfig(device);
+ if (volume)
+ IAudioStreamVolume_Release(volume);
+ if (render)
+ IAudioRenderClient_Release(render);
+ if (client)
+ IAudioClient_Release(client);
+ HeapFree(GetProcessHeap(), 0, wfx);
+ return hres;
}
HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
@@ -432,8 +412,6 @@ HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
/* **** */
EnterCriticalSection(&(device->mixlock));
- DSOUND_PrimaryClose(device);
-
if(device->primary && (device->primary->ref || device->primary->numIfaces))
WARN("Destroying primary buffer while references held (%u %u)\n", device->primary->ref, device->primary->numIfaces);
@@ -509,7 +487,6 @@ HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX passe
HRESULT err = S_OK;
WAVEFORMATEX *old_fmt;
WAVEFORMATEXTENSIBLE *fmtex, *passed_fmtex = (WAVEFORMATEXTENSIBLE*)passed_fmt;
- BOOL forced = (device->priolevel == DSSCL_WRITEPRIMARY);
TRACE("(%p,%p)\n", device, passed_fmt);
@@ -559,24 +536,12 @@ HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX passe
fmtex->Samples.wValidBitsPerSample = fmtex->Format.wBitsPerSample;
}
- DSOUND_PrimaryClose(device);
-
- err = DSOUND_ReopenDevice(device, forced);
+ err = DSOUND_ReopenDevice(device, TRUE);
if (FAILED(err)) {
ERR("No formats could be opened\n");
- goto done;
- }
-
- err = DSOUND_PrimaryOpen(device);
- if (err != DS_OK) {
- ERR("DSOUND_PrimaryOpen failed\n");
- goto done;
- }
-
-done:
- if (err != DS_OK)
+ HeapFree(GetProcessHeap(), 0, device->primary_pwfx);
device->primary_pwfx = old_fmt;
- else
+ } else
HeapFree(GetProcessHeap(), 0, old_fmt);
} else {
WAVEFORMATEX *wfx = DSOUND_CopyFormat(passed_fmt);
--
2.3.5
From c40cbcde120258f81b54e515644c3fa20bc1a032 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 16 Mar 2015 14:33:41 +0100
Subject: dsound: remove state machine from render buffer
.. wat
---
dlls/dsound/dsound.c | 2 +-
dlls/dsound/dsound_private.h | 2 +-
dlls/dsound/mixer.c | 36 +------------------------
dlls/dsound/primary.c | 64 ++++----------------------------------------
4 files changed, 8 insertions(+), 96 deletions(-)
diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c
index ed30eae..603670f 100644
--- a/dlls/dsound/dsound.c
+++ b/dlls/dsound/dsound.c
@@ -135,7 +135,7 @@ static HRESULT DirectSoundDevice_Create(DirectSoundDevice ** ppDevice)
device->ref = 1;
device->priolevel = DSSCL_NORMAL;
- device->state = STATE_STOPPED;
+ device->stopped = 1;
device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE);
DSOUND_ParseSpeakerConfig(device);
diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h
index f142b7c..44bd527 100644
--- a/dlls/dsound/dsound_private.h
+++ b/dlls/dsound/dsound_private.h
@@ -76,7 +76,7 @@ struct DirectSoundDevice
DWORD priolevel, sleeptime;
PWAVEFORMATEX pwfx, primary_pwfx;
LPBYTE buffer;
- DWORD writelead, buflen, aclen, fraglen, state, playpos, pad;
+ DWORD writelead, buflen, aclen, fraglen, playpos, pad, stopped;
int nrofbuffers;
IDirectSoundBufferImpl** buffers;
RTL_RWLOCK buffer_list_lock;
diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c
index 155e378..794d9c4 100644
--- a/dlls/dsound/mixer.c
+++ b/dlls/dsound/mixer.c
@@ -694,10 +694,6 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
* also wipe out just-played sound data */
if (!pad)
WARN("Probable buffer underrun\n");
- else if (device->state == STATE_STOPPED ||
- device->state == STATE_STARTING) {
- TRACE("Buffer restarting\n");
- }
hr = IAudioRenderClient_GetBuffer(device->render, maxq / block, (void*)&buffer);
if(FAILED(hr)){
@@ -723,23 +719,7 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
ERR("ReleaseBuffer failed: %08x\n", hr);
device->pad += maxq;
-
- if (maxq) {
- if (device->state == STATE_STARTING ||
- device->state == STATE_STOPPED) {
- if(DSOUND_PrimaryPlay(device) != DS_OK)
- WARN("DSOUND_PrimaryPlay failed\n");
- else if (device->state == STATE_STARTING)
- device->state = STATE_PLAYING;
- else
- device->state = STATE_STOPPING;
- }
- } else if (!pad && !maxq && (all_stopped == TRUE) &&
- (device->state == STATE_STOPPING)) {
- device->state = STATE_STOPPED;
- DSOUND_PrimaryStop(device);
- }
- } else if (device->state != STATE_STOPPED) {
+ } else if (!device->stopped) {
if (maxq > device->buflen)
maxq = device->buflen;
if (writepos + maxq > device->buflen) {
@@ -747,20 +727,6 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
DSOUND_WaveQueue(device, device->buffer, writepos + maxq - device->buflen);
} else
DSOUND_WaveQueue(device, device->buffer + writepos, maxq);
-
- /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
- if (device->state == STATE_STARTING) {
- if (DSOUND_PrimaryPlay(device) != DS_OK)
- WARN("DSOUND_PrimaryPlay failed\n");
- else
- device->state = STATE_PLAYING;
- }
- else if (device->state == STATE_STOPPING) {
- if (DSOUND_PrimaryStop(device) != DS_OK)
- WARN("DSOUND_PrimaryStop failed\n");
- else
- device->state = STATE_STOPPED;
- }
}
LeaveCriticalSection(&(device->mixlock));
diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c
index 4bfae7e..0f83eb9 100644
--- a/dlls/dsound/primary.c
+++ b/dlls/dsound/primary.c
@@ -273,11 +273,6 @@ static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device, WAVEFORMATEX *wfx,
HeapFree(GetProcessHeap(), 0, device->pwfx);
device->pwfx = wfx;
- if (device->state == STATE_PLAYING)
- device->state = STATE_STARTING;
- else if (device->state == STATE_STOPPING)
- device->state = STATE_STOPPED;
-
device->writelead = (wfx->nSamplesPerSec / 100) * wfx->nBlockAlign;
TRACE("buflen: %u, fraglen: %u, mix_buffer_len: %u\n",
@@ -391,7 +386,7 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
return S_OK;
err_service:
- ERR("GetService failed: %08x\n", hres);
+ WARN("GetService failed: %08x\n", hres);
err:
device->speaker_config = oldspeakerconfig;
DSOUND_ParseSpeakerConfig(device);
@@ -428,36 +423,6 @@ HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
return DS_OK;
}
-HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device)
-{
- HRESULT hr;
-
- TRACE("(%p)\n", device);
-
- hr = IAudioClient_Start(device->client);
- if(FAILED(hr) && hr != AUDCLNT_E_NOT_STOPPED){
- WARN("Start failed: %08x\n", hr);
- return hr;
- }
-
- return DS_OK;
-}
-
-HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
-{
- HRESULT hr;
-
- TRACE("(%p)\n", device);
-
- hr = IAudioClient_Stop(device->client);
- if(FAILED(hr)){
- WARN("Stop failed: %08x\n", hr);
- return hr;
- }
-
- return DS_OK;
-}
-
WAVEFORMATEX *DSOUND_CopyFormat(const WAVEFORMATEX *wfex)
{
WAVEFORMATEX *pwfx;
@@ -705,16 +670,7 @@ static HRESULT WINAPI PrimaryBufferImpl_Play(IDirectSoundBuffer *iface, DWORD re
return DSERR_INVALIDPARAM;
}
- /* **** */
- EnterCriticalSection(&(device->mixlock));
-
- if (device->state == STATE_STOPPED)
- device->state = STATE_STARTING;
- else if (device->state == STATE_STOPPING)
- device->state = STATE_PLAYING;
-
- LeaveCriticalSection(&(device->mixlock));
- /* **** */
+ device->stopped = 0;
return DS_OK;
}
@@ -725,16 +681,7 @@ static HRESULT WINAPI PrimaryBufferImpl_Stop(IDirectSoundBuffer *iface)
DirectSoundDevice *device = This->device;
TRACE("(%p)\n", iface);
- /* **** */
- EnterCriticalSection(&(device->mixlock));
-
- if (device->state == STATE_PLAYING)
- device->state = STATE_STOPPING;
- else if (device->state == STATE_STARTING)
- device->state = STATE_STOPPED;
-
- LeaveCriticalSection(&(device->mixlock));
- /* **** */
+ device->stopped = 1;
return DS_OK;
}
@@ -802,7 +749,7 @@ static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(IDirectSoundBuffer *i
*playpos = mixpos;
if (writepos) {
*writepos = mixpos;
- if (device->state != STATE_STOPPED) {
+ if (!device->stopped) {
/* apply the documented 10ms lead to writepos */
*writepos += device->writelead;
*writepos %= device->buflen;
@@ -828,8 +775,7 @@ static HRESULT WINAPI PrimaryBufferImpl_GetStatus(IDirectSoundBuffer *iface, DWO
}
*status = 0;
- if ((device->state == STATE_STARTING) ||
- (device->state == STATE_PLAYING))
+ if (!device->stopped)
*status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
TRACE("status=%x\n", *status);
--
2.3.5
From 403c30cf2e1ca2e8e840c0f21991d921cb4aa585 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 16 Mar 2015 14:33:41 +0100
Subject: dsound: kill unconditional memory allocation in mixing thread
Unfortunately this doesn't get rid of the memory allocation entirely,
but it will decrease the chance of underruns due to locking immensely.
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@canonical.com>
---
dlls/dsound/dsound.c | 1 +
dlls/dsound/dsound_private.h | 4 ++--
dlls/dsound/mixer.c | 27 ++++++++++++++++++---------
3 files changed, 21 insertions(+), 11 deletions(-)
diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c
index 603670f..1007785 100644
--- a/dlls/dsound/dsound.c
+++ b/dlls/dsound/dsound.c
@@ -234,6 +234,7 @@ static ULONG DirectSoundDevice_Release(DirectSoundDevice * device)
IAudioStreamVolume_Release(device->volume);
HeapFree(GetProcessHeap(), 0, device->tmp_buffer);
+ HeapFree(GetProcessHeap(), 0, device->cp_buffer);
HeapFree(GetProcessHeap(), 0, device->buffer);
RtlDeleteResource(&device->buffer_list_lock);
device->mixlock.DebugInfo->Spare[0] = 0;
diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h
index 44bd527..04067e1 100644
--- a/dlls/dsound/dsound_private.h
+++ b/dlls/dsound/dsound_private.h
@@ -87,8 +87,8 @@ struct DirectSoundDevice
int speaker_num[DS_MAX_CHANNELS];
int num_speakers;
int lfe_channel;
- float *tmp_buffer;
- DWORD tmp_buffer_len, mix_buffer_len;
+ float *tmp_buffer, *cp_buffer;
+ DWORD tmp_buffer_len, mix_buffer_len, cp_buffer_len;
DSVOLUMEPAN volpan;
diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c
index 794d9c4..39dc4a4 100644
--- a/dlls/dsound/mixer.c
+++ b/dlls/dsound/mixer.c
@@ -282,7 +282,8 @@ static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, LONG64 *
{
UINT i, channel;
UINT istride = dsb->pwfx->nBlockAlign;
- UINT ostride = dsb->device->pwfx->nChannels * sizeof(float);
+ DirectSoundDevice *dev = dsb->device;
+ UINT ostride = dev->pwfx->nChannels * sizeof(float);
LONG64 freqAcc_start = *freqAccNum;
LONG64 freqAcc_end = freqAcc_start + count * dsb->freqAdjustNum;
@@ -292,18 +293,29 @@ static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, LONG64 *
UINT fir_cachesize = (fir_len + dsbfirstep - 2) / dsbfirstep;
UINT required_input = max_ipos + fir_cachesize;
+ float *intermediate, *fir_copy, *itmp;
+
+ DWORD len = required_input * channels;
+ len += fir_cachesize;
+ len *= sizeof(float);
+
+ if (!dev->cp_buffer) {
+ dev->cp_buffer = HeapAlloc(GetProcessHeap(), 0, len);
+ dev->cp_buffer_len = len;
+ } else if (len > dev->cp_buffer_len) {
+ dev->cp_buffer = HeapReAlloc(GetProcessHeap(), 0, dev->cp_buffer, len);
+ dev->cp_buffer_len = len;
+ }
- float* intermediate = HeapAlloc(GetProcessHeap(), 0,
- sizeof(float) * required_input * channels);
+ fir_copy = dev->cp_buffer;
+ intermediate = fir_copy + fir_cachesize;
- float* fir_copy = HeapAlloc(GetProcessHeap(), 0,
- sizeof(float) * fir_cachesize);
/* Important: this buffer MUST be non-interleaved
* if you want -msse3 to have any effect.
* This is good for CPU cache effects, too.
*/
- float* itmp = intermediate;
+ itmp = intermediate;
for (channel = 0; channel < channels; channel++)
for (i = 0; i < required_input; i++)
*(itmp++) = get_current_sample(dsb,
@@ -338,9 +350,6 @@ static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, LONG64 *
*freqAccNum = freqAcc_end % dsb->freqAdjustDen;
- HeapFree(GetProcessHeap(), 0, fir_copy);
- HeapFree(GetProcessHeap(), 0, intermediate);
-
return max_ipos;
}
--
2.3.5
From 2bdf6d7332491ce7444c37df8e762c13adac9ca5 Mon Sep 17 00:00:00 2001
From: Maarten Lankhorst <maarten.lankhorst@canonical.com>
Date: Mon, 16 Mar 2015 14:33:41 +0100
Subject: TESTING -- override pthreads to fix gstreamer v5
I believe the code is ready and will work properly now in all cases.
but please test before cherry picking this patch, and report
success or failure to me please.
Changes since v1:
- Call pthread_yield to make sure that we link against libpthread.
This fixes the build on saucy.
Changes since v2:
- Set thread_data->detached before creating the thread to prevent
a race condition.
Changes since v3:
- Set thread_data->detached CORRECTLY. Fix a small race between
thread creation and pthread_detach.
Changes since v4:
- Set native thread stack defaults in a similar way to eglibc,
and respect the defaults for win32 again. This fixes the regression
where you can only create 194 threads in java.
---
dlls/ntdll/ntdll_misc.h | 3 +
dlls/ntdll/thread.c | 331 ++++++++++++++++++++++++++++++++++++++--
dlls/winegstreamer/glibthread.c | 13 ++
libs/wine/loader.c | 7 +
libs/wine/wine.map | 6 +
loader/Makefile.in | 2 +-
loader/main.c | 41 +++++
7 files changed, 386 insertions(+), 17 deletions(-)
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h
index 674fcbc..7f93bdf 100644
--- a/dlls/ntdll/ntdll_misc.h
+++ b/dlls/ntdll/ntdll_misc.h
@@ -28,6 +28,7 @@
#include "winnt.h"
#include "winternl.h"
#include "wine/server.h"
+#include "wine/list.h"
#define MAX_NT_PATH_LENGTH 277
@@ -240,6 +241,8 @@ struct ntdll_thread_data
WINE_VM86_TEB_INFO vm86; /* 1fc vm86 private data */
void *exit_frame; /* 204 exit frame pointer */
#endif
+ struct list entry;
+ BOOL detached;
};
static inline struct ntdll_thread_data *ntdll_get_thread_data(void)
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index 3696c8e..a33bf47 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -33,6 +33,7 @@
#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
#endif
+#include <errno.h>
#define NONAMELESSUNION
#include "ntstatus.h"
@@ -58,6 +59,7 @@ struct startup_info
TEB *teb;
PRTL_THREAD_START_ROUTINE entry_point;
void *entry_arg;
+ BOOL native_thread;
};
static PEB *peb;
@@ -202,6 +204,80 @@ static ULONG64 get_dyld_image_info_addr(void)
}
#endif /* __APPLE__ */
+#ifdef __linux__
+#include <sys/resource.h>
+
+extern typeof(pthread_create) *__glob_pthread_create, *call_pthread_create;
+extern typeof(pthread_join) *__glob_pthread_join, *call_pthread_join;
+extern typeof(pthread_detach) *__glob_pthread_detach, *call_pthread_detach;
+
+static typeof(pthread_create) __hook_pthread_create;
+static typeof(pthread_join) __hook_pthread_join;
+static typeof(pthread_detach) __hook_pthread_detach;
+
+static pthread_mutex_t thread_lock;
+
+static void thread_wrap_init(void)
+{
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
+ pthread_mutex_init(&thread_lock, &attr);
+ pthread_mutexattr_destroy(&attr);
+
+ call_pthread_create = __hook_pthread_create;
+ call_pthread_join = __hook_pthread_join;
+ call_pthread_detach = __hook_pthread_detach;
+}
+
+static TEB *dead_teb;
+static struct list active_list = LIST_INIT(active_list);
+
+static void take_thread_lock(void)
+{
+ int ret = pthread_mutex_lock(&thread_lock);
+ if (ret == EOWNERDEAD)
+ pthread_mutex_consistent(&thread_lock);
+}
+
+static void detach_thread_unlock(TEB *own_teb)
+{
+ struct ntdll_thread_data *thread_data;
+ TEB *teb = dead_teb;
+
+ dead_teb = own_teb;
+
+ pthread_mutex_unlock(&thread_lock);
+ if (!teb)
+ return;
+
+ thread_data = (struct ntdll_thread_data *)teb->SpareBytes1;
+ __glob_pthread_join(thread_data->pthread_id, NULL);
+ signal_free_thread(teb);
+}
+
+static void reap_thread(TEB *teb)
+{
+ struct ntdll_thread_data *thread_data = (struct ntdll_thread_data *)teb->SpareBytes1;
+ take_thread_lock();
+ if (thread_data->detached)
+ detach_thread_unlock(teb);
+ else {
+ /*
+ * Do not unlock, wait until the thread is thoroughly dead.
+ * This prevents a race condition where detach is called
+ * after the thread has not finished dying yet.
+ */
+ }
+}
+
+#else
+#define __glob_pthread_create pthread_create
+#define __glob_pthread_join pthread_join
+#define __glob_pthread_detach pthread_detach
+#define thread_wrap_init()
+#endif
+
/***********************************************************************
* thread_init
*
@@ -223,6 +299,7 @@ HANDLE thread_init(void)
ULONG64 dyld_image_info;
#endif
+ thread_wrap_init();
virtual_init();
/* reserve space for shared user data */
@@ -363,14 +440,12 @@ void terminate_thread( int status )
pthread_exit( UIntToPtr(status) );
}
-
-/***********************************************************************
- * exit_thread
- */
-void exit_thread( int status )
+static void exit_thread_common( int status )
{
+#ifndef __linux__
static void *prev_teb;
TEB *teb;
+#endif
if (status) /* send the exit code to the server (0 is already the default) */
{
@@ -394,24 +469,199 @@ void exit_thread( int status )
pthread_sigmask( SIG_BLOCK, &server_block_set, NULL );
+#ifndef __linux__
if ((teb = interlocked_xchg_ptr( &prev_teb, NtCurrentTeb() )))
{
struct ntdll_thread_data *thread_data = (struct ntdll_thread_data *)teb->SpareBytes1;
if (thread_data->pthread_id)
{
- pthread_join( thread_data->pthread_id, NULL );
+ __glob_pthread_join( thread_data->pthread_id, NULL );
signal_free_thread( teb );
}
}
+#else
+ reap_thread(NtCurrentTeb());
+#endif
close( ntdll_get_thread_data()->wait_fd[0] );
close( ntdll_get_thread_data()->wait_fd[1] );
close( ntdll_get_thread_data()->reply_fd );
close( ntdll_get_thread_data()->request_fd );
+}
+
+void exit_thread( int status )
+{
+ exit_thread_common(status);
pthread_exit( UIntToPtr(status) );
}
+#ifdef __linux__
+
+struct unix_arg {
+ void *(*start)(void *);
+ void *arg;
+};
+
+/* dummy used for comparison */
+static DWORD native_unix_start;
+
+static void call_native_cleanup(void *arg)
+{
+ exit_thread_common(0);
+}
+
+static int
+__hook_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine) (void *), void *parm)
+{
+ NTSTATUS ret;
+ pthread_t tid;
+ size_t stack = 0, stack_commit;
+ static size_t default_stack;
+ struct unix_arg arg;
+ IMAGE_NT_HEADERS *nt;
+
+ arg.start = start_routine;
+ arg.arg = parm;
+
+ if (!default_stack) {
+ struct rlimit limit;
+
+ if (getrlimit(RLIMIT_STACK, &limit) == 0 &&
+ limit.rlim_cur != RLIM_INFINITY)
+ default_stack = limit.rlim_cur;
+ else
+ default_stack = 2 * 1024 * 1024;
+ }
+
+ if (!thread)
+ thread = &tid;
+
+ TRACE("Overriding thread creation!\n");
+ if (attr) {
+ static int once;
+ if (!once++)
+ FIXME("most thread attributes ignored!\n");
+ else
+ WARN("most thread attributes ignored!\n");
+
+ pthread_attr_getstacksize(attr, &stack);
+ }
+
+ if (!stack)
+ stack = default_stack;
+
+ nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress );
+ stack_commit = nt->OptionalHeader.SizeOfStackCommit;
+
+ if (!stack_commit || stack_commit > stack)
+ stack_commit = stack;
+
+ ret = RtlCreateUserThread( NtCurrentProcess(), NULL, FALSE, NULL, stack, stack_commit, (void*)&native_unix_start, &arg, NULL, (void*)thread );
+ if (ret != STATUS_SUCCESS)
+ FIXME("ret: %08x\n", ret);
+ switch (ret) {
+ case STATUS_SUCCESS:
+ TRACE("created thread %lx for %p/%p\n", *thread, start_routine, parm);
+ return 0;
+ case STATUS_NO_MEMORY:
+ return ENOMEM;
+ case STATUS_TOO_MANY_OPENED_FILES:
+ return EMFILE;
+ default:
+ ERR("Unhandled ntstatus %08x\n", ret);
+ return ENOMEM;
+ }
+}
+
+static int __hook_pthread_detach(pthread_t thread)
+{
+ struct ntdll_thread_data *thread_data;
+ TEB *teb = NULL;
+
+ if (pthread_equal(thread, pthread_self())) {
+ TRACE("Detached self: %lx\n", pthread_self());
+ ntdll_get_thread_data()->detached = 1;
+ return 0;
+ }
+
+ take_thread_lock();
+ LIST_FOR_EACH_ENTRY(thread_data, &active_list, typeof(*thread_data), entry) {
+ if (pthread_equal(thread_data->pthread_id, thread)) {
+ teb = CONTAINING_RECORD(thread_data, typeof(*teb), SpareBytes1);
+
+ list_remove(&thread_data->entry);
+ if (!pthread_tryjoin_np(thread, NULL)) {
+ detach_thread_unlock(NULL);
+ TRACE("Thread %lx was dead, cleaning up\n", thread);
+ signal_free_thread(teb);
+ return 0;
+ }
+ thread_data->detached = 1;
+ break;
+ }
+ }
+ detach_thread_unlock(NULL);
+ if (!teb)
+ TRACE("Could not find thread %lx to detach\n", thread);
+ else
+ TRACE("Changed thread %lx to detached\n", thread);
+ return teb ? 0 : ESRCH;
+}
+
+static int __hook_pthread_join(pthread_t thread, void **retval)
+{
+ struct ntdll_thread_data *thread_data, *t2;
+ int ret = ESRCH;
+
+ if (pthread_equal(thread, pthread_self()))
+ return EDEADLK;
+
+ take_thread_lock();
+ LIST_FOR_EACH_ENTRY(thread_data, &active_list, typeof(*thread_data), entry) {
+ TEB *teb = CONTAINING_RECORD(thread_data, typeof(*teb), SpareBytes1);
+
+ if (pthread_equal(thread, thread_data->pthread_id)) {
+
+ ret = pthread_tryjoin_np(thread, retval);
+ if (!ret) {
+ TRACE("Thread %lx was dead fastpath, cleaning up\n", thread);
+ goto free;
+ }
+ detach_thread_unlock(NULL);
+
+ ret = __glob_pthread_join(thread, retval);
+ if (ret) {
+ TRACE("Thread %lx join failed with %i, ignoring\n", thread, ret);
+ return ret;
+ }
+
+ take_thread_lock();
+ /* Check if someone else freed the thread yet */
+ LIST_FOR_EACH_ENTRY(t2, &active_list, typeof(*thread_data), entry)
+ if (t2 == thread_data) {
+ TRACE("Cleaning up after successful join\n");
+ goto free;
+ }
+ TRACE("No clean up after successful join, multiple pthread_join's?\n");
+ break;
+
+free:
+ list_remove(&thread_data->entry);
+ detach_thread_unlock(NULL);
+ signal_free_thread(teb);
+ return 0;
+ }
+ }
+
+ detach_thread_unlock(NULL);
+ if (ret)
+ TRACE("failed with %i\n", ret);
+ return ret;
+}
+
+#endif
/***********************************************************************
* start_thread
@@ -440,9 +690,19 @@ static void start_thread( struct startup_info *info )
if (TRACE_ON(relay))
DPRINTF( "%04x:Starting thread proc %p (arg=%p)\n", GetCurrentThreadId(), func, arg );
- call_thread_entry_point( (LPTHREAD_START_ROUTINE)func, arg );
-}
+#ifdef __linux__
+ if (info->native_thread) {
+ void *(*start)(void*) = (void*)func;
+ FIXME("Started native thread %08x\n", GetCurrentThreadId());
+ pthread_cleanup_push(call_native_cleanup, NULL);
+ pthread_exit(start(arg));
+ pthread_cleanup_pop(1);
+ return;
+ }
+#endif
+ call_thread_entry_point( (LPTHREAD_START_ROUTINE)func, arg );
+}
/***********************************************************************
* RtlCreateUserThread (NTDLL.@)
@@ -454,14 +714,13 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
HANDLE *handle_ptr, CLIENT_ID *id )
{
sigset_t sigset;
- pthread_t pthread_id;
pthread_attr_t attr;
struct ntdll_thread_data *thread_data;
struct startup_info *info = NULL;
HANDLE handle = 0, actctx = 0;
TEB *teb = NULL;
DWORD tid = 0;
- int request_pipe[2];
+ int request_pipe[2], ret;
NTSTATUS status;
if (process != NtCurrentProcess())
@@ -486,10 +745,14 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
if (handle_ptr) *handle_ptr = wine_server_ptr_handle( result.create_thread.handle );
else NtClose( wine_server_ptr_handle( result.create_thread.handle ));
}
+ TRACE("CreateThread for other process returns %08x\n", result.create_thread.status);
return result.create_thread.status;
}
- if (server_pipe( request_pipe ) == -1) return STATUS_TOO_MANY_OPENED_FILES;
+ if (server_pipe( request_pipe ) == -1) {
+ TRACE("CreateThread cannot create request pipe: %m\n");
+ return STATUS_TOO_MANY_OPENED_FILES;
+ }
wine_server_send_fd( request_pipe[0] );
SERVER_START_REQ( new_thread )
@@ -510,12 +773,16 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
if (status)
{
close( request_pipe[1] );
+ TRACE("CreateThread server request failed with %08x\n", status);
return status;
}
pthread_sigmask( SIG_BLOCK, &server_block_set, &sigset );
- if ((status = signal_alloc_thread( &teb ))) goto error;
+ if ((status = signal_alloc_thread( &teb ))) {
+ TRACE("CreateThread signal thread allocation failed with %08x\n", status);
+ goto error;
+ }
teb->Peb = NtCurrentTeb()->Peb;
teb->ClientId.UniqueProcess = ULongToHandle(GetCurrentProcessId());
@@ -538,32 +805,64 @@ NTSTATUS WINAPI RtlCreateUserThread( HANDLE process, const SECURITY_DESCRIPTOR *
info = (struct startup_info *)(teb + 1);
info->teb = teb;
- info->entry_point = start;
- info->entry_arg = param;
+#ifdef __linux__
+ info->native_thread = (void*)start == (void*)&native_unix_start;
+ if (info->native_thread) {
+ struct unix_arg *arg = param;
+ info->entry_point = (void*)arg->start;
+ info->entry_arg = arg->arg;
+ } else
+#endif
+ {
+ info->entry_point = start;
+ info->entry_arg = param;
+ }
thread_data = (struct ntdll_thread_data *)teb->SpareBytes1;
+#ifdef __linux__
+ thread_data->detached = !info->native_thread;
+#endif
thread_data->request_fd = request_pipe[1];
thread_data->reply_fd = -1;
thread_data->wait_fd[0] = -1;
thread_data->wait_fd[1] = -1;
+ thread_data->entry.next = NULL;
- if ((status = virtual_alloc_thread_stack( teb, stack_reserve, stack_commit ))) goto error;
+ if ((status = virtual_alloc_thread_stack( teb, stack_reserve, stack_commit ))) {
+ TRACE("Allocating virtual stack for %p (%li/%li) failed with %08x\n", start, stack_reserve, stack_commit, status);
+ goto error;
+ }
pthread_attr_init( &attr );
pthread_attr_setstack( &attr, teb->DeallocationStack,
(char *)teb->Tib.StackBase - (char *)teb->DeallocationStack );
pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ); /* force creating a kernel thread */
interlocked_xchg_add( &nb_threads, 1 );
- if (pthread_create( &pthread_id, &attr, (void * (*)(void *))start_thread, info ))
+
+ take_thread_lock();
+ ret = __glob_pthread_create( &thread_data->pthread_id, &attr, (void * (*)(void *))start_thread, info );
+ if (ret)
{
+ TRACE("pthread create failed with %i/%m\n", ret);
interlocked_xchg_add( &nb_threads, -1 );
pthread_attr_destroy( &attr );
status = STATUS_NO_MEMORY;
goto error;
}
+ if (!thread_data->detached)
+ list_add_tail(&active_list, &thread_data->entry);
+ detach_thread_unlock(NULL);
+
pthread_attr_destroy( &attr );
pthread_sigmask( SIG_SETMASK, &sigset, NULL );
+ TRACE("Created thread succesfully, win handle: %04x, pthread: %lx\n", tid, thread_data->pthread_id);
+
+#ifdef __linux__
+ if ((void*)start == (void*)&native_unix_start && id)
+ *(pthread_t*)id = thread_data->pthread_id;
+ else
+#endif
if (id) id->UniqueThread = ULongToHandle(tid);
if (handle_ptr) *handle_ptr = handle;
else NtClose( handle );
diff --git a/dlls/winegstreamer/glibthread.c b/dlls/winegstreamer/glibthread.c
index 0d829a0..46e22f4 100644
--- a/dlls/winegstreamer/glibthread.c
+++ b/dlls/winegstreamer/glibthread.c
@@ -43,6 +43,7 @@
#include <stdlib.h>
#include <stdio.h>
+#if 0
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
@@ -388,3 +389,15 @@ void g_thread_impl_init (void)
g_thread_self_tls = TlsAlloc ();
g_thread_init(&g_thread_functions_for_glib_use_default);
}
+
+#else
+
+void g_thread_impl_init (void)
+{
+ static gboolean beenhere = FALSE;
+
+ if (!beenhere++)
+ g_thread_init(NULL);
+}
+
+#endif
diff --git a/libs/wine/loader.c b/libs/wine/loader.c
index 7261522..a8c31b9 100644
--- a/libs/wine/loader.c
+++ b/libs/wine/loader.c
@@ -73,6 +73,13 @@ char **__wine_main_argv = NULL;
WCHAR **__wine_main_wargv = NULL;
char **__wine_main_environ = NULL;
+#ifdef __linux__
+#include <pthread.h>
+typeof(pthread_create) *call_pthread_create, *__glob_pthread_create;
+typeof(pthread_join) *call_pthread_join, *__glob_pthread_join;
+typeof(pthread_detach) *call_pthread_detach, *__glob_pthread_detach;
+#endif
+
struct dll_path_context
{
unsigned int index; /* current index in the dll path list */
diff --git a/libs/wine/wine.map b/libs/wine/wine.map
index 2159fac..fb3b951 100644
--- a/libs/wine/wine.map
+++ b/libs/wine/wine.map
@@ -117,6 +117,12 @@ WINE_1.0
wine_utf8_mbstowcs;
wine_utf8_wcstombs;
wine_wctype_table;
+ __glob_pthread_create;
+ call_pthread_create;
+ __glob_pthread_join;
+ call_pthread_join;
+ __glob_pthread_detach;
+ call_pthread_detach;
local: *;
};
diff --git a/loader/Makefile.in b/loader/Makefile.in
index 95e4798..a18dd02 100644
--- a/loader/Makefile.in
+++ b/loader/Makefile.in
@@ -1,4 +1,4 @@
-EXTRALIBS = $(PTHREAD_LIBS)
+EXTRALIBS = $(PTHREAD_LIBS) $(DL_LIBS)
C_SRCS = \
main.c \
diff --git a/loader/main.c b/loader/main.c
index ce21173..0e13862 100644
--- a/loader/main.c
+++ b/loader/main.c
@@ -202,6 +202,45 @@ static int pre_exec(void)
#endif
+#ifdef __linux__
+
+extern typeof(pthread_create) *call_pthread_create, *__glob_pthread_create;
+extern typeof(pthread_detach) *call_pthread_detach, *__glob_pthread_detach;
+extern typeof(pthread_join) *call_pthread_join, *__glob_pthread_join;
+
+int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+ void *(*start_routine) (void *), void *arg)
+{
+ return call_pthread_create(thread, attr, start_routine, arg);
+}
+
+int pthread_detach(pthread_t thread)
+{
+ return call_pthread_detach(thread);
+}
+
+int pthread_join(pthread_t thread, void **retval)
+{
+ return call_pthread_join(thread, retval);
+}
+
+static void init_thread_hook(void) {
+ call_pthread_create = __glob_pthread_create = dlvsym(RTLD_NEXT, "pthread_create", "GLIBC_2.2.5");
+ if (!__glob_pthread_create)
+ call_pthread_create = __glob_pthread_create = dlvsym(RTLD_NEXT, "pthread_create", "GLIBC_2.1");
+
+ call_pthread_detach = __glob_pthread_detach = dlsym(RTLD_NEXT, "pthread_detach");
+ call_pthread_join = __glob_pthread_join = dlsym(RTLD_NEXT, "pthread_join");
+
+ /* Call a function from libpthread to ensure being linked against it */
+ pthread_yield();
+}
+
+#else
+
+#define init_thread_hook()
+
+#endif
/**********************************************************************
* main
@@ -211,6 +250,8 @@ int main( int argc, char *argv[] )
char error[1024];
int i;
+ init_thread_hook();
+
if (!getenv( "WINELOADERNOEXEC" )) /* first time around */
{
static char noexec[] = "WINELOADERNOEXEC=1";
--
2.3.5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment