From bb0fcf08d2f37f191c99f9048cb0babd18689801 Mon Sep 17 00:00:00 2001 From: Timo Wischer Date: Tue, 7 May 2019 17:10:02 +0200 Subject: [PATCH 01/42] posix: fifo: use right prototype Change-Id: Ia6aee61aa7059a70d2014f7de66a41cbaca8c7ed Signed-off-by: Timo Wischer --- posix/JackFifo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posix/JackFifo.h b/posix/JackFifo.h index 8ac72591d..8b0a8d786 100644 --- a/posix/JackFifo.h +++ b/posix/JackFifo.h @@ -44,7 +44,7 @@ class SERVER_EXPORT JackFifo : public detail::JackSynchro protected: - void BuildName(const char* name, const char* server_name, char* res); + void BuildName(const char* name, const char* server_name, char* res, int size); public: From c8111682a42afe224ff27e1f25f05b6d42af91e5 Mon Sep 17 00:00:00 2001 From: Timo Wischer Date: Tue, 7 May 2019 17:21:29 +0200 Subject: [PATCH 02/42] posix: socket: Add missing time header include Change-Id: I1cab18816980f704b6ae73a37960c1421e46ed9b Signed-off-by: Timo Wischer --- posix/JackNetUnixSocket.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/posix/JackNetUnixSocket.cpp b/posix/JackNetUnixSocket.cpp index 20724089a..528c95880 100644 --- a/posix/JackNetUnixSocket.cpp +++ b/posix/JackNetUnixSocket.cpp @@ -22,6 +22,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include #include +#include using namespace std; From e06493203cd31d5c30d44c8b324da87f1b786d99 Mon Sep 17 00:00:00 2001 From: Timo Wischer Date: Tue, 7 May 2019 16:59:08 +0200 Subject: [PATCH 03/42] Add QNX platform specific implementations Change-Id: Ic0b9c377e111cb59ed4859819b810842347e8525 Signed-off-by: Timo Wischer --- common/jack/systemdeps.h | 2 +- qnx/JackAtomic_os.h | 86 +++++++++++ qnx/JackLinuxTime.c | 215 +++++++++++++++++++++++++++ qnx/JackPlatformPlug_os.h | 85 +++++++++++ qnx/driver.h | 300 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 687 insertions(+), 1 deletion(-) mode change 100644 => 100755 common/jack/systemdeps.h create mode 100755 qnx/JackAtomic_os.h create mode 100755 qnx/JackLinuxTime.c create mode 100755 qnx/JackPlatformPlug_os.h create mode 100755 qnx/driver.h diff --git a/common/jack/systemdeps.h b/common/jack/systemdeps.h old mode 100644 new mode 100755 index 1a1119272..2a2ffbeb7 --- a/common/jack/systemdeps.h +++ b/common/jack/systemdeps.h @@ -107,7 +107,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #endif /* _WIN32 && !__CYGWIN__ && !GNU_WIN32 */ -#if defined(__APPLE__) || defined(__linux__) || defined(__sun__) || defined(sun) || defined(__unix__) || defined(__CYGWIN__) || defined(GNU_WIN32) +#if defined(__APPLE__) || defined(__linux__) || defined(__sun__) || defined(sun) || defined(__unix__) || defined(__CYGWIN__) || defined(GNU_WIN32) || defined(__QNXNTO__) #if defined(__CYGWIN__) || defined(GNU_WIN32) #include diff --git a/qnx/JackAtomic_os.h b/qnx/JackAtomic_os.h new file mode 100755 index 000000000..e201d0d6a --- /dev/null +++ b/qnx/JackAtomic_os.h @@ -0,0 +1,86 @@ +/* +Copyright (C) 2004-2008 Grame + +This program 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 program 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 program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackAtomic_linux__ +#define __JackAtomic_linux__ + +#include "JackTypes.h" + +#ifdef __PPC__ + +static inline int CAS(register UInt32 value, register UInt32 newvalue, register volatile void* addr) +{ + register int result; + register UInt32 tmp; + asm volatile ( + "# CAS \n" + " lwarx %4, 0, %1 \n" // creates a reservation on addr + " cmpw %4, %2 \n" // test value at addr + " bne- 1f \n" + " sync \n" // synchronize instructions + " stwcx. %3, 0, %1 \n" // if the reservation is not altered + // stores the new value at addr + " bne- 1f \n" + " li %0, 1 \n" + " b 2f \n" + "1: \n" + " li %0, 0 \n" + "2: \n" + : "=r" (result) + : "r" (addr), "r" (value), "r" (newvalue), "r" (tmp) + ); + return result; +} + +#endif + +#if defined(__i386__) || defined(__x86_64__) + +#define LOCK "lock ; " + +static inline char CAS(volatile UInt32 value, UInt32 newvalue, volatile void* addr) +{ + register char ret; + __asm__ __volatile__ ( + "# CAS \n\t" + LOCK "cmpxchg %2, (%1) \n\t" + "sete %0 \n\t" + : "=a" (ret) + : "c" (addr), "d" (newvalue), "a" (value) + ); + return ret; +} + +#endif + + + + +#if !defined(__i386__) && !defined(__x86_64__) && !defined(__PPC__) + + +static inline char CAS(volatile UInt32 value, UInt32 newvalue, volatile void* addr) +{ + return __sync_bool_compare_and_swap ((UInt32*)addr, value, newvalue); +} +#endif + + +#endif + diff --git a/qnx/JackLinuxTime.c b/qnx/JackLinuxTime.c new file mode 100755 index 000000000..a5ece7650 --- /dev/null +++ b/qnx/JackLinuxTime.c @@ -0,0 +1,215 @@ +/* +Copyright (C) 2001-2003 Paul Davis +Copyright (C) 2005 Jussi Laako +Copyright (C) 2004-2008 Grame + +This program 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 program 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 program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "JackConstants.h" +#include "JackTime.h" +#include "JackTypes.h" +#include "JackError.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +jack_time_t (*_jack_get_microseconds)(void) = 0; + +#if defined(__gnu_linux__) && (defined(__i386__) || defined(__x86_64__)) +#define HPET_SUPPORT +#define HPET_MMAP_SIZE 1024 +#define HPET_CAPS 0x000 +#define HPET_PERIOD 0x004 +#define HPET_COUNTER 0x0f0 +#define HPET_CAPS_COUNTER_64BIT (1 << 13) +#if defined(__x86_64__) +typedef uint64_t hpet_counter_t; +#else +typedef uint32_t hpet_counter_t; +#endif +static int hpet_fd; +static unsigned char *hpet_ptr; +static uint32_t hpet_period; /* period length in femto secs */ +static uint64_t hpet_offset = 0; +static uint64_t hpet_wrap; +static hpet_counter_t hpet_previous = 0; +#endif /* defined(__gnu_linux__) && (__i386__ || __x86_64__) */ + +#ifdef HPET_SUPPORT + +static int jack_hpet_init () +{ + uint32_t hpet_caps; + + hpet_fd = open("/dev/hpet", O_RDONLY); + if (hpet_fd < 0) { + jack_error ("This system has no accessible HPET device (%s)", strerror (errno)); + return -1; + } + + hpet_ptr = (unsigned char *) mmap(NULL, HPET_MMAP_SIZE, + PROT_READ, MAP_SHARED, hpet_fd, 0); + if (hpet_ptr == MAP_FAILED) { + jack_error ("This system has no mappable HPET device (%s)", strerror (errno)); + close (hpet_fd); + return -1; + } + + /* this assumes period to be constant. if needed, + it can be moved to the clock access function + */ + hpet_period = *((uint32_t *) (hpet_ptr + HPET_PERIOD)); + hpet_caps = *((uint32_t *) (hpet_ptr + HPET_CAPS)); + hpet_wrap = ((hpet_caps & HPET_CAPS_COUNTER_64BIT) && + (sizeof(hpet_counter_t) == sizeof(uint64_t))) ? + 0 : ((uint64_t) 1 << 32); + + return 0; +} + +static jack_time_t jack_get_microseconds_from_hpet (void) +{ + hpet_counter_t hpet_counter; + long double hpet_time; + + hpet_counter = *((hpet_counter_t *) (hpet_ptr + HPET_COUNTER)); + if (hpet_counter < hpet_previous) + hpet_offset += hpet_wrap; + hpet_previous = hpet_counter; + hpet_time = (long double) (hpet_offset + hpet_counter) * + (long double) hpet_period * (long double) 1e-9; + return ((jack_time_t) (hpet_time + 0.5)); +} + +#else + +static int jack_hpet_init () +{ + jack_error ("This version of JACK or this computer does not have HPET support.\n" + "Please choose a different clock source."); + return -1; +} + +static jack_time_t jack_get_microseconds_from_hpet (void) +{ + /* never called */ + return 0; +} + +#endif /* HPET_SUPPORT */ + +#define HAVE_CLOCK_GETTIME 1 + +#ifndef HAVE_CLOCK_GETTIME + +static jack_time_t jack_get_microseconds_from_system (void) +{ + jack_time_t jackTime; + struct timeval tv; + + gettimeofday (&tv, NULL); + jackTime = (jack_time_t) tv.tv_sec * 1000000 + (jack_time_t) tv.tv_usec; + return jackTime; +} + +#else + +static jack_time_t jack_get_microseconds_from_system (void) +{ + jack_time_t jackTime; + struct timespec time; + + clock_gettime(CLOCK_MONOTONIC, &time); + jackTime = (jack_time_t) time.tv_sec * 1e6 + + (jack_time_t) time.tv_nsec / 1e3; + return jackTime; +} + +#endif /* HAVE_CLOCK_GETTIME */ + + +SERVER_EXPORT void JackSleep(long usec) +{ + usleep(usec); +} + +SERVER_EXPORT void InitTime() +{ + /* nothing to do on a generic system - we use the system clock */ +} + +SERVER_EXPORT void EndTime() +{} + +void SetClockSource(jack_timer_type_t source) +{ + jack_log("Clock source : %s", ClockSourceName(source)); + + switch (source) + { + case JACK_TIMER_HPET: + if (jack_hpet_init () == 0) { + _jack_get_microseconds = jack_get_microseconds_from_hpet; + } else { + _jack_get_microseconds = jack_get_microseconds_from_system; + } + break; + + case JACK_TIMER_SYSTEM_CLOCK: + default: + _jack_get_microseconds = jack_get_microseconds_from_system; + break; + } +} + +const char* ClockSourceName(jack_timer_type_t source) +{ + switch (source) { + case JACK_TIMER_HPET: + return "hpet"; + case JACK_TIMER_SYSTEM_CLOCK: + #ifdef HAVE_CLOCK_GETTIME + return "system clock via clock_gettime"; + #else + return "system clock via gettimeofday"; + #endif + } + + /* what is wrong with gcc ? */ + return "unknown"; +} + +SERVER_EXPORT jack_time_t GetMicroSeconds() +{ + return _jack_get_microseconds(); +} + +SERVER_EXPORT jack_time_t jack_get_microseconds() +{ + return _jack_get_microseconds(); +} + diff --git a/qnx/JackPlatformPlug_os.h b/qnx/JackPlatformPlug_os.h new file mode 100755 index 000000000..c845dca8f --- /dev/null +++ b/qnx/JackPlatformPlug_os.h @@ -0,0 +1,85 @@ +/* +Copyright (C) 2004-2008 Grame + +This program 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 program 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 program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackPlatformPlug_linux__ +#define __JackPlatformPlug_linux__ + +#define jack_server_dir "/dev/shm" +#define jack_client_dir "/dev/shm" +#define JACK_DEFAULT_DRIVER "alsa" + +namespace Jack +{ + struct JackRequest; + struct JackResult; + + class JackPosixMutex; + class JackPosixThread; + class JackFifo; + class JackSocketServerChannel; + class JackSocketClientChannel; + class JackSocketServerNotifyChannel; + class JackSocketNotifyChannel; + class JackClientSocket; + class JackNetUnixSocket; +} + +/* __JackPlatformMutex__ */ +#include "JackPosixMutex.h" +namespace Jack {typedef JackPosixMutex JackMutex; } + +/* __JackPlatformThread__ */ +#include "JackPosixThread.h" +namespace Jack { typedef JackPosixThread JackThread; } + +#include "JackFifo.h" +namespace Jack { typedef JackFifo JackSynchro; } + + +/* __JackPlatformChannelTransaction__ */ +/* +#include "JackSocket.h" +namespace Jack { typedef JackClientSocket JackChannelTransaction; } +*/ + +/* __JackPlatformProcessSync__ */ +#include "JackPosixProcessSync.h" +namespace Jack { typedef JackPosixProcessSync JackProcessSync; } + +/* __JackPlatformServerChannel__ */ +#include "JackSocketServerChannel.h" +namespace Jack { typedef JackSocketServerChannel JackServerChannel; } + +/* __JackPlatformClientChannel__ */ +#include "JackSocketClientChannel.h" +namespace Jack { typedef JackSocketClientChannel JackClientChannel; } + +/* __JackPlatformServerNotifyChannel__ */ +#include "JackSocketServerNotifyChannel.h" +namespace Jack { typedef JackSocketServerNotifyChannel JackServerNotifyChannel; } + +/* __JackPlatformNotifyChannel__ */ +#include "JackSocketNotifyChannel.h" +namespace Jack { typedef JackSocketNotifyChannel JackNotifyChannel; } + +/* __JackPlatformNetSocket__ */ +#include "JackNetUnixSocket.h" +namespace Jack { typedef JackNetUnixSocket JackNetSocket; } + +#endif diff --git a/qnx/driver.h b/qnx/driver.h new file mode 100755 index 000000000..6e1466f4a --- /dev/null +++ b/qnx/driver.h @@ -0,0 +1,300 @@ +/* + Copyright (C) 2001 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: driver.h,v 1.2 2005/11/23 11:24:29 letz Exp $ +*/ + +#ifndef __jack_driver_h__ +#define __jack_driver_h__ + +#include +#include "types.h" +#include "jslist.h" +#include "driver_interface.h" + +typedef float gain_t; +typedef long channel_t; + +typedef enum { + Lock = 0x1, + NoLock = 0x2, + Sync = 0x4, + NoSync = 0x8 +} ClockSyncStatus; + +typedef void (*ClockSyncListenerFunction)(channel_t, ClockSyncStatus, void*); + +typedef struct +{ + unsigned long id; + ClockSyncListenerFunction function; + void *arg; +} +ClockSyncListener; + +struct _jack_engine; +struct _jack_driver; + +typedef int (*JackDriverAttachFunction)(struct _jack_driver *, + struct _jack_engine *); +typedef int (*JackDriverDetachFunction)(struct _jack_driver *, + struct _jack_engine *); +typedef int (*JackDriverReadFunction)(struct _jack_driver *, + jack_nframes_t nframes); +typedef int (*JackDriverWriteFunction)(struct _jack_driver *, + jack_nframes_t nframes); +typedef int (*JackDriverNullCycleFunction)(struct _jack_driver *, + jack_nframes_t nframes); +typedef int (*JackDriverStopFunction)(struct _jack_driver *); +typedef int (*JackDriverStartFunction)(struct _jack_driver *); +typedef int (*JackDriverBufSizeFunction)(struct _jack_driver *, + jack_nframes_t nframes); +/* + Call sequence summary: + + 1) engine loads driver via runtime dynamic linking + - calls jack_driver_load + - we call dlsym for "driver_initialize" and execute it + 2) engine attaches to driver + 3) engine starts driver + 4) driver runs its own thread, calling + while () { + driver->wait (); + driver->engine->run_cycle () + } + 5) engine stops driver + 6) engine detaches from driver + 7) engine calls driver `finish' routine + + Note that stop/start may be called multiple times in the event of an + error return from the `wait' function. +*/ + +typedef struct _jack_driver +{ + + /* The _jack_driver structure fields are included at the beginning of + each driver-specific structure using the JACK_DRIVER_DECL macro, + which is defined below. The comments that follow describe each + common field. + + The driver should set this to be the interval it expects to elapse + between returning from the `wait' function. if set to zero, it + implies that the driver does not expect regular periodic wakeups. + + jack_time_t period_usecs; + + + The driver should set this within its "wait" function to indicate + the UST of the most recent determination that the engine cycle + should run. it should not be set if the "extra_fd" argument of + the wait function is set to a non-zero value. + + jack_time_t last_wait_ust; + + + These are not used by the driver. They should not be written to or + modified in any way + + void *handle; + struct _jack_internal_client *internal_client; + + This should perform any cleanup associated with the driver. it will + be called when jack server process decides to get rid of the + driver. in some systems, it may not be called at all, so the driver + should never rely on a call to this. it can set it to NULL if + it has nothing do do. + + void (*finish)(struct _jack_driver *); + + + The JACK engine will call this when it wishes to attach itself to + the driver. the engine will pass a pointer to itself, which the driver + may use in anyway it wishes to. the driver may assume that this + is the same engine object that will make `wait' calls until a + `detach' call is made. + + JackDriverAttachFunction attach; + + + The JACK engine will call this when it is finished using a driver. + + JackDriverDetachFunction detach; + + + The JACK engine will call this when it wants to wait until the + driver decides that its time to process some data. the driver returns + a count of the number of audioframes that can be processed. + + it should set the variable pointed to by `status' as follows: + + zero: the wait completed normally, processing may begin + negative: the wait failed, and recovery is not possible + positive: the wait failed, and the driver stopped itself. + a call to `start' will return the driver to + a correct and known state. + + the driver should also fill out the `delayed_usecs' variable to + indicate any delay in its expected periodic execution. for example, + if it discovers that its return from poll(2) is later than it + expects it to be, it would place an estimate of the delay + in this variable. the engine will use this to decide if it + plans to continue execution. + + JackDriverWaitFunction wait; + + + The JACK engine will call this to ask the driver to move + data from its inputs to its output port buffers. it should + return 0 to indicate successful completion, negative otherwise. + + This function will always be called after the wait function (above). + + JackDriverReadFunction read; + + + The JACK engine will call this to ask the driver to move + data from its input port buffers to its outputs. it should + return 0 to indicate successful completion, negative otherwise. + + this function will always be called after the read function (above). + + JackDriverWriteFunction write; + + + The JACK engine will call this after the wait function (above) has + been called, but for some reason the engine is unable to execute + a full "cycle". the driver should do whatever is necessary to + keep itself running correctly, but cannot reference ports + or other JACK data structures in any way. + + JackDriverNullCycleFunction null_cycle; + + + The engine will call this when it plans to stop calling the `wait' + function for some period of time. the driver should take + appropriate steps to handle this (possibly no steps at all). + NOTE: the driver must silence its capture buffers (if any) + from within this function or the function that actually + implements the change in state. + + JackDriverStopFunction stop; + + + The engine will call this to let the driver know that it plans + to start calling the `wait' function on a regular basis. the driver + should take any appropriate steps to handle this (possibly no steps + at all). NOTE: The driver may wish to silence its playback buffers + (if any) from within this function or the function that actually + implements the change in state. + + JackDriverStartFunction start; + + The engine will call this to let the driver know that some client + has requested a new buffer size. The stop function will be called + prior to this, and the start function after this one has returned. + + JackDriverBufSizeFunction bufsize; + */ + + /* define the fields here... */ +#define JACK_DRIVER_DECL \ + jack_time_t period_usecs; \ + jack_time_t last_wait_ust; \ + void *handle; \ + struct _jack_client_internal * internal_client; \ + void (*finish)(struct _jack_driver *);\ + JackDriverAttachFunction attach; \ + JackDriverDetachFunction detach; \ + JackDriverReadFunction read; \ + JackDriverWriteFunction write; \ + JackDriverNullCycleFunction null_cycle; \ + JackDriverStopFunction stop; \ + JackDriverStartFunction start; \ + JackDriverBufSizeFunction bufsize; + + JACK_DRIVER_DECL /* expand the macro */ +} +jack_driver_t; + +void jack_driver_init (jack_driver_t *); +void jack_driver_release (jack_driver_t *); + +jack_driver_t *jack_driver_load (int argc, char **argv); +void jack_driver_unload (jack_driver_t *); + +/**************************** + *** Non-Threaded Drivers *** + ****************************/ + +/* + Call sequence summary: + + 1) engine loads driver via runtime dynamic linking + - calls jack_driver_load + - we call dlsym for "driver_initialize" and execute it + - driver_initialize calls jack_driver_nt_init + 2) nt layer attaches to driver + 3) nt layer starts driver + 4) nt layer runs a thread, calling + while () { + driver->nt_run_ctcle(); + } + 5) nt layer stops driver + 6) nt layer detaches driver + 7) engine calls driver `finish' routine which calls jack_driver_nt_finish + + Note that stop/start may be called multiple times in the event of an + error return from the `wait' function. +*/ + +struct _jack_driver_nt; + +typedef int (*JackDriverNTAttachFunction)(struct _jack_driver_nt *); +typedef int (*JackDriverNTDetachFunction)(struct _jack_driver_nt *); +typedef int (*JackDriverNTStopFunction)(struct _jack_driver_nt *); +typedef int (*JackDriverNTStartFunction)(struct _jack_driver_nt *); +typedef int (*JackDriverNTBufSizeFunction)(struct _jack_driver_nt *, + jack_nframes_t nframes); +typedef int (*JackDriverNTRunCycleFunction)(struct _jack_driver_nt *); + +typedef struct _jack_driver_nt +{ +#define JACK_DRIVER_NT_DECL \ + JACK_DRIVER_DECL \ + struct _jack_engine * engine; \ + volatile int nt_run; \ + pthread_t nt_thread; \ + pthread_mutex_t nt_run_lock; \ + JackDriverNTAttachFunction nt_attach; \ + JackDriverNTDetachFunction nt_detach; \ + JackDriverNTStopFunction nt_stop; \ + JackDriverNTStartFunction nt_start; \ + JackDriverNTBufSizeFunction nt_bufsize; \ + JackDriverNTRunCycleFunction nt_run_cycle; +#define nt_read read +#define nt_write write +#define nt_null_cycle null_cycle + + JACK_DRIVER_NT_DECL +} +jack_driver_nt_t; + +void jack_driver_nt_init (jack_driver_nt_t * driver); +void jack_driver_nt_finish (jack_driver_nt_t * driver); + +#endif /* __jack_driver_h__ */ From d86d2bedad3c0f9b029df2f18d22bf127ba98862 Mon Sep 17 00:00:00 2001 From: Timo Wischer Date: Tue, 7 May 2019 16:59:16 +0200 Subject: [PATCH 04/42] AudioAdapter: Use empty Adapter also for QNX Change-Id: I69bc2a73849431925c14d865bc970e2cbfa2d843 Signed-off-by: Timo Wischer --- common/JackAudioAdapterInterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/JackAudioAdapterInterface.cpp b/common/JackAudioAdapterInterface.cpp index ee90edd4a..0aeffa05c 100644 --- a/common/JackAudioAdapterInterface.cpp +++ b/common/JackAudioAdapterInterface.cpp @@ -22,7 +22,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #endif #include "JackAudioAdapter.h" -#ifndef MY_TARGET_OS_IPHONE +#if !defined(MY_TARGET_OS_IPHONE) && !defined(__QNXNTO__) #include "JackLibSampleRateResampler.h" #endif #include "JackTime.h" @@ -185,7 +185,7 @@ namespace Jack fRunning = false; } -#ifdef MY_TARGET_OS_IPHONE +#if defined(MY_TARGET_OS_IPHONE) || defined(__QNXNTO__) void JackAudioAdapterInterface::Create() {} #else From 8521b9855a2dc6fcf029faaefcadebd6474a59d0 Mon Sep 17 00:00:00 2001 From: Timo Wischer Date: Tue, 7 May 2019 17:31:33 +0200 Subject: [PATCH 05/42] qnx: sigaction: do not set SA_RESTART flag because it is not available in QNX Change-Id: I94a7586c148aa4f9b6cd6fef98a8637e7fea717c Signed-off-by: Timo Wischer --- common/JackControlAPI.cpp | 5 ++++- common/netjack_packet.c | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/common/JackControlAPI.cpp b/common/JackControlAPI.cpp index bbabbac1d..7e0289481 100644 --- a/common/JackControlAPI.cpp +++ b/common/JackControlAPI.cpp @@ -663,7 +663,10 @@ jackctl_setup_signals( sigfillset(&allsignals); action.sa_handler = signal_handler; action.sa_mask = allsignals; - action.sa_flags = SA_RESTART|SA_RESETHAND; + action.sa_flags = SA_RESETHAND; +#ifndef __QNXNTO__ + action.sa_flags |= SA_RESTART; +#endif for (i = 1; i < NSIG; i++) { diff --git a/common/netjack_packet.c b/common/netjack_packet.c index 4671368f3..e59806c17 100644 --- a/common/netjack_packet.c +++ b/common/netjack_packet.c @@ -428,7 +428,9 @@ netjack_poll (int sockfd, int timeout) action.sa_handler = SIG_DFL; action.sa_mask = sigmask; +#ifndef __QNXNTO__ action.sa_flags = SA_RESTART; +#endif for (i = 1; i < NSIG; i++) if (sigismember (&sigmask, i)) From 5dfc216068a965c88291f07c98c660a21925e350 Mon Sep 17 00:00:00 2001 From: Timo Wischer Date: Tue, 7 May 2019 17:39:41 +0200 Subject: [PATCH 06/42] qnx: shm: Include right header Change-Id: I40287828155d7b7530d8760724b3fb3b06ae15b7 Signed-off-by: Timo Wischer --- common/shm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common/shm.c b/common/shm.c index b1e7fc71a..6f03933fd 100644 --- a/common/shm.c +++ b/common/shm.c @@ -49,11 +49,16 @@ #include #include #include -#include #include #include #include "promiscuous.h" +#ifdef __QNXNTO__ +#include +#else +#include +#endif + #endif #include "shm.h" From 012fe027db811c5c7ba34f987a699e5091c94fae Mon Sep 17 00:00:00 2001 From: Timo Wischer Date: Wed, 22 May 2019 10:45:07 +0200 Subject: [PATCH 07/42] shm: Add semget() and semop() replacement for QNX Change-Id: I7ea831b96cb774fc69b7934c65797a51e9b9ec6a Signed-off-by: Timo Wischer --- common/shm.c | 62 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/common/shm.c b/common/shm.c index 6f03933fd..690de7991 100644 --- a/common/shm.c +++ b/common/shm.c @@ -153,8 +153,6 @@ static jack_shm_registry_t *jack_shm_registry = NULL; #define JACK_SHM_REGISTRY_KEY JACK_SEMAPHORE_KEY #endif -static int semid = -1; - #ifdef WIN32 #include @@ -183,12 +181,61 @@ static BOOL check_process_running(DWORD process_id) } static int -semaphore_init () {return 0;} +jack_shm_lock_registry () {return 0;} + +static void +jack_shm_unlock_registry () { } + +#elif __QNXNTO__ +#include + +static sem_t* semid = SEM_FAILED; + +static int +semaphore_init () +{ + const char name[] = "/jack-shm-registry-lock"; + const int oflag = O_CREAT | O_RDWR; + const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + if ((semid = sem_open(name, oflag, mode, 1)) == SEM_FAILED) { + jack_error("Creating semaphore %s failed", name); + return -1; + } + + return 0; +} + +static int +jack_shm_lock_registry (void) +{ + if (semid == SEM_FAILED) { + if (semaphore_init () < 0) + return -1; + } + + // TODO automatically unblock in case the process terminates + const int ret = sem_wait(semid); + if (ret < 0) { + jack_error("sem_wait() failed with %s", strerror(ret)); + return -1; + } + + return 0; +} + +static void +jack_shm_unlock_registry (void) +{ + const int ret = sem_post(semid); -static int -semaphore_add (int value) {return 0;} + if (ret < 0) { + jack_error("sem_post() failed with %s", strerror(ret)); + } +} #else +static int semid = -1; + /* all semaphore errors are fatal -- issue message, but do not return */ static void semaphore_error (char *msg) @@ -252,8 +299,6 @@ semaphore_add (int value) return 0; } -#endif - static int jack_shm_lock_registry (void) { @@ -271,6 +316,8 @@ jack_shm_unlock_registry (void) semaphore_add (1); } +#endif + static void jack_shm_init_registry () { @@ -1313,4 +1360,3 @@ jack_attach_shm_read (jack_shm_info_t* si) } #endif /* !USE_POSIX_SHM */ - From 7bed71c0b97734c18fd541531cb80df6d11b5b10 Mon Sep 17 00:00:00 2001 From: Timo Wischer Date: Fri, 6 Sep 2019 10:10:26 +0200 Subject: [PATCH 08/42] alsa: Extract snd_pcm_start/poll_descriptors/avail_update Change-Id: I92760ed539b475e210bc877b335afdbb4a982a04 Signed-off-by: Timo Wischer --- linux/alsa/alsa_driver.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index be9c44932..0172a8829 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -983,6 +983,12 @@ alsa_driver_get_channel_addresses (alsa_driver_t *driver, return 0; } +static int +alsa_driver_stream_start(snd_pcm_t *pcm, bool is_capture) +{ + return snd_pcm_start(pcm); +} + int alsa_driver_start (alsa_driver_t *driver) { @@ -1089,7 +1095,7 @@ alsa_driver_start (alsa_driver_t *driver) driver->user_nperiods * driver->frames_per_cycle); - if ((err = snd_pcm_start (driver->playback_handle)) < 0) { + if ((err = alsa_driver_stream_start (driver->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { jack_error ("ALSA: could not start playback (%s)", snd_strerror (err)); return -1; @@ -1098,7 +1104,7 @@ alsa_driver_start (alsa_driver_t *driver) if ((driver->capture_handle && driver->capture_and_playback_not_synced) || !driver->playback_handle) { - if ((err = snd_pcm_start (driver->capture_handle)) < 0) { + if ((err = alsa_driver_stream_start (driver->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { jack_error ("ALSA: could not start capture (%s)", snd_strerror (err)); return -1; @@ -1280,6 +1286,18 @@ alsa_driver_set_clock_sync_status (alsa_driver_t *driver, channel_t chn, alsa_driver_clock_sync_notify (driver, chn, status); } +static int +alsa_driver_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space, bool is_capture) +{ + return snd_pcm_poll_descriptors(pcm, pfds, space); +} + +static snd_pcm_sframes_t +alsa_driver_avail(alsa_driver_t *driver, snd_pcm_t *pcm, bool is_capture) +{ + return snd_pcm_avail_update(pcm); +} + static int under_gdb = FALSE; jack_nframes_t @@ -1320,16 +1338,16 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float nfds = 0; if (need_playback) { - snd_pcm_poll_descriptors (driver->playback_handle, + alsa_driver_poll_descriptors (driver->playback_handle, &driver->pfd[0], - driver->playback_nfds); + driver->playback_nfds, SND_PCM_STREAM_PLAYBACK); nfds += driver->playback_nfds; } if (need_capture) { - snd_pcm_poll_descriptors (driver->capture_handle, + alsa_driver_poll_descriptors (driver->capture_handle, &driver->pfd[nfds], - driver->capture_nfds); + driver->capture_nfds, SND_PCM_STREAM_CAPTURE); ci = nfds; nfds += driver->capture_nfds; } @@ -1506,8 +1524,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float } if (driver->capture_handle) { - if ((capture_avail = snd_pcm_avail_update ( - driver->capture_handle)) < 0) { + if ((capture_avail = alsa_driver_avail (driver, + driver->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { if (capture_avail == -EPIPE) { xrun_detected = TRUE; } else { @@ -1521,8 +1539,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float } if (driver->playback_handle) { - if ((playback_avail = snd_pcm_avail_update ( - driver->playback_handle)) < 0) { + if ((playback_avail = alsa_driver_avail (driver, + driver->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { if (playback_avail == -EPIPE) { xrun_detected = TRUE; } else { From 71ecea6fdb851759750eb71f165d6782a27ed5e4 Mon Sep 17 00:00:00 2001 From: Timo Wischer Date: Tue, 4 Feb 2020 15:08:39 +0100 Subject: [PATCH 09/42] alsa backend for qnx Change-Id: Id1dda94d4621a8e77c76c2cd15f08e26a31b87e5 Signed-off-by: Laxmi Devi Signed-off-by: Timo Wischer --- linux/alsa/JackAlsaDriver.cpp | 25 +- linux/alsa/alsa_driver.c | 876 +++++++++++++++++++++++++--------- linux/alsa/alsa_driver.h | 62 ++- linux/alsa/usx2y.h | 4 + 4 files changed, 713 insertions(+), 254 deletions(-) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index 0bd904d5c..6fcdf4889 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -204,6 +204,7 @@ int JackAlsaDriver::Detach() return JackAudioDriver::Detach(); } +#ifndef __QNXNTO__ extern "C" char* get_control_device_name(const char * device_name) { char * ctl_name; @@ -233,7 +234,9 @@ extern "C" char* get_control_device_name(const char * device_name) return ctl_name; } +#endif +#ifndef __QNXNTO__ static int card_to_num(const char* device) { int err; @@ -273,6 +276,7 @@ static int card_to_num(const char* device) fail: return i; } +#endif int JackAlsaDriver::Open(jack_nframes_t nframes, jack_nframes_t user_nperiods, @@ -301,6 +305,8 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, } alsa_midi_t *midi = 0; + +#ifndef __QNXNTO__ #ifndef __ANDROID__ if (strcmp(midi_driver_name, "seq") == 0) midi = alsa_seqmidi_new((jack_client_t*)this, 0); @@ -333,6 +339,7 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, } } } +#endif fDriver = alsa_driver_new ((char*)"alsa_pcm", (char*)playback_driver_name, (char*)capture_driver_name, NULL, @@ -356,12 +363,14 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, // ALSA driver may have changed the in/out values fCaptureChannels = ((alsa_driver_t *)fDriver)->capture_nchannels; fPlaybackChannels = ((alsa_driver_t *)fDriver)->playback_nchannels; +#ifndef __QNXNTO__ if (JackServerGlobals::on_device_reservation_loop != NULL) { device_reservation_loop_running = true; if (JackPosixThread::StartImp(&fReservationLoopThread, 0, 0, on_device_reservation_loop, NULL) != 0) { device_reservation_loop_running = false; } } +#endif return 0; } else { Close(); @@ -378,6 +387,7 @@ int JackAlsaDriver::Close() alsa_driver_delete((alsa_driver_t*)fDriver); } +#ifndef __QNXNTO__ if (device_reservation_loop_running) { device_reservation_loop_running = false; JackPosixThread::StopImp(fReservationLoopThread); @@ -398,6 +408,7 @@ int JackAlsaDriver::Close() JackServerGlobals::on_device_release(audio_name); } } +#endif return res; } @@ -578,6 +589,7 @@ extern "C" { #endif +#ifndef __QNXNTO__ static jack_driver_param_constraint_desc_t * enum_alsa_devices() @@ -667,6 +679,7 @@ enum_alsa_devices() jack_constraint_free(constraint_ptr); return NULL; } +#endif static int dither_opt (char c, DitherAlgorithm* dither) @@ -705,11 +718,14 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () desc = jack_driver_descriptor_construct("alsa", JackDriverMaster, "Linux ALSA API based audio backend", &filler); strcpy(value.str, "hw:0"); +#ifndef __QNXNTO__ #ifdef __ANDROID__ jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, NULL, "ALSA device name", NULL); #else jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, enum_alsa_devices(), "ALSA device name", NULL); #endif +#endif + strcpy(value.str, "none"); jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamString, &value, NULL, "Provide capture ports. Optionally set device", NULL); @@ -951,8 +967,13 @@ void SetTime(jack_time_t time) int Restart() { int res; - if ((res = g_alsa_driver->Stop()) == 0) { - res = g_alsa_driver->Start(); + if ((res = g_alsa_driver->Stop()) != 0) { + jack_error("restart: stop driver failed"); + return res; + } + if ((res = g_alsa_driver->Start()) != 0) { + jack_error("restart: start driver failed"); + return res; } return res; } diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 0172a8829..5f4f9d9e2 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -95,6 +95,23 @@ jack_driver_nt_init (jack_driver_nt_t * driver) driver->nt_run_cycle = 0; } +static int +alsa_driver_prepare (snd_pcm_t *handle, int is_capture) +{ + int res = 0; + +#ifndef __QNXNTO__ + res = snd_pcm_prepare (handle); +#else + res = snd_pcm_plugin_prepare(handle, is_capture); +#endif + if (res < 0) { + jack_error("error preparing: %s", snd_strerror(res)); + } + + return res; +} + static void alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) { @@ -111,6 +128,20 @@ alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) driver->capture_addr = 0; } +#ifdef __QNXNTO__ + if (driver->playback_areas_ptr) { + free(driver->playback_areas_ptr); + driver->playback_areas = NULL; + driver->playback_areas_ptr = NULL; + } + + if (driver->capture_areas_ptr) { + free(driver->capture_areas_ptr); + driver->capture_areas = NULL; + driver->capture_areas_ptr = NULL; + } +#endif + if (driver->playback_interleave_skip) { free (driver->playback_interleave_skip); driver->playback_interleave_skip = NULL; @@ -132,6 +163,8 @@ alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) } } +#ifndef __QNXNTO__ + static int alsa_driver_check_capabilities (alsa_driver_t *driver) { @@ -266,6 +299,7 @@ alsa_driver_hw_specific (alsa_driver_t *driver, int hw_monitoring, return 0; } +#endif static void alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) @@ -352,6 +386,119 @@ alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) } } +#ifdef __QNXNTO__ + +static int +alsa_driver_allocate_buffer(alsa_driver_t *driver, int frames, int channels, bool is_capture) +{ + const long ALIGNMENT = 32; + + // TODO driver->playback_sample_bytes + char* const fBuffer = malloc(channels * ((sizeof(alsa_driver_default_format_t)) * frames) + ALIGNMENT); + if(fBuffer) { + /* Provide an 32 byte aligned buffer */ + char* const aligned_buffer = (char*)((uintptr_t)fBuffer & ~(ALIGNMENT-1)) + ALIGNMENT; + + if(is_capture) { + driver->capture_areas_ptr = fBuffer; + driver->capture_areas = aligned_buffer; + } else { + driver->playback_areas_ptr = fBuffer; + driver->playback_areas = aligned_buffer; + } + + return 0; + } + + jack_error ("ALSA: could not allocate audio buffer"); + return -1; +} + +static int +alsa_driver_get_setup (alsa_driver_t *driver, snd_pcm_channel_setup_t *setup, bool is_capture) +{ + int err = 0; + + memset(setup, 0, sizeof(*setup)); + setup->channel = is_capture; + + if(is_capture) { + err = snd_pcm_plugin_setup(driver->capture_handle, setup); + } else { + err = snd_pcm_plugin_setup(driver->playback_handle, setup); + } + if (err < 0) { + jack_error("couldn't get channel setup for %s, err = %s ", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + strerror(err)); + return -1; + } + + return 0; +} + +static int +alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, + const char *stream_name, + snd_pcm_t *handle, + unsigned int *nperiodsp, + channel_t *nchns, + unsigned long sample_width, + bool is_capture) +{ + int err = 0; + snd_pcm_channel_info_t ch_info; + snd_pcm_channel_params_t ch_params; + const unsigned long sample_size = is_capture ? driver->capture_sample_bytes + : driver->playback_sample_bytes; + + memset(&ch_info, 0, sizeof(ch_info)); + /*A pointer to a snd_pcm_channel_info_t structure that snd_pcm_plugin_info() fills in with information about the PCM channel. + * Before calling snd_pcm_plugin_info(), set the info structure's channel member to specify the direction. + * This function sets all the other members.*/ + ch_info.channel = is_capture; + if ((err = snd_pcm_plugin_info(handle, &ch_info)) < 0) { + jack_error("couldn't get channel info for %s, %s, err = (%s)", stream_name, device_name, snd_strerror(err)); + alsa_driver_delete(driver); + return -1; + } + + if (!*nchns) { + *nchns = ch_info.max_voices; + } + + ch_params.mode = SND_PCM_MODE_BLOCK; + ch_params.start_mode = SND_PCM_START_GO; + ch_params.stop_mode = SND_PCM_STOP_STOP; + ch_params.buf.block.frag_size = driver->frames_per_cycle * *nchns * sample_size; + + *nperiodsp = driver->user_nperiods; + ch_params.buf.block.frags_min = 1; + /* the maximal available periods (-1 due to one period is always processed + * by DMA and therefore not free) + */ + ch_params.buf.block.frags_max = *nperiodsp - 1; + ch_params.format.interleave = 1; + ch_params.format.rate = driver->frame_rate; + ch_params.format.voices = *nchns; + ch_params.channel = is_capture; + + ch_params.format.format = (sample_width == 4) ? SND_PCM_SFMT_S32_LE : SND_PCM_SFMT_S16_LE; + /*Set the configurable parameters for a PCM channel*/ + if ((err = snd_pcm_plugin_params(handle, &ch_params)) < 0) { + jack_error("snd_pcm_plugin_params failed for %s %s with err = (%s)", snd_strerror(err), stream_name, device_name); + alsa_driver_delete(driver); + return -1; + } + + /* + * The buffer has to be able to hold a full HW audio buffer + * (periods * period_size) because the silence prefill will fill the + * complete buffer + */ + return alsa_driver_allocate_buffer(driver, driver->frames_per_cycle * *nperiodsp, *nchns, is_capture); +} +#else static int alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, const char *stream_name, @@ -613,6 +760,58 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, return 0; } +#endif + +#ifdef __QNXNTO__ +static int +alsa_driver_check_format (unsigned int format) +{ +#else +static int +alsa_driver_check_format (snd_pcm_format_t format) +{ +#endif + + switch (format) { +#ifndef __QNXNTO__ + case SND_PCM_FORMAT_FLOAT_LE: + case SND_PCM_FORMAT_S24_3LE: + case SND_PCM_FORMAT_S24_3BE: + case SND_PCM_FORMAT_S24_LE: + case SND_PCM_FORMAT_S24_BE: + case SND_PCM_FORMAT_S32_BE: + case SND_PCM_FORMAT_S16_BE: +#endif + case SND_PCM_FORMAT_S16_LE: + case SND_PCM_FORMAT_S32_LE: + break; + default: + jack_error ("format not supported %d", format); + return -1; + } + + return 0; +} + +static void +alsa_driver_set_sample_bytes (alsa_driver_t *driver) +{ +#ifdef __QNXNTO__ + driver->playback_sample_bytes = + snd_pcm_format_width (driver->playback_sample_format) + / 8; + driver->capture_sample_bytes = + snd_pcm_format_width (driver->capture_sample_format) + / 8; +#else + driver->playback_sample_bytes = + snd_pcm_format_physical_width (driver->playback_sample_format) + / 8; + driver->capture_sample_bytes = + snd_pcm_format_physical_width (driver->capture_sample_format) + / 8; +#endif +} static int alsa_driver_set_parameters (alsa_driver_t *driver, @@ -620,7 +819,14 @@ alsa_driver_set_parameters (alsa_driver_t *driver, jack_nframes_t user_nperiods, jack_nframes_t rate) { +#ifdef __QNXNTO__ + snd_pcm_channel_setup_t c_setup; + snd_pcm_channel_setup_t p_setup; + jack_nframes_t p_periods = 0; + jack_nframes_t c_periods = 0; +#else int dir; +#endif snd_pcm_uframes_t p_period_size = 0; snd_pcm_uframes_t c_period_size = 0; channel_t chn; @@ -637,37 +843,90 @@ alsa_driver_set_parameters (alsa_driver_t *driver, rate, frames_per_cycle, (((float)frames_per_cycle / (float) rate) * 1000.0f), user_nperiods); if (driver->capture_handle) { - if (alsa_driver_configure_stream ( - driver, - driver->alsa_name_capture, - "capture", - driver->capture_handle, - driver->capture_hw_params, - driver->capture_sw_params, - &driver->capture_nperiods, - &driver->capture_nchannels, - driver->capture_sample_bytes)) { +#ifdef __QNXNTO__ + err = alsa_driver_configure_stream ( + driver, + driver->alsa_name_capture, + "capture", + driver->capture_handle, + &driver->capture_nperiods, + &driver->capture_nchannels, + driver->capture_sample_bytes, + SND_PCM_CHANNEL_CAPTURE); +#else + err = alsa_driver_configure_stream ( + driver, + driver->alsa_name_capture, + "capture", + driver->capture_handle, + driver->capture_hw_params, + driver->capture_sw_params, + &driver->capture_nperiods, + &driver->capture_nchannels, + driver->capture_sample_bytes); +#endif + if (err) { jack_error ("ALSA: cannot configure capture channel"); return -1; } } if (driver->playback_handle) { - if (alsa_driver_configure_stream ( - driver, - driver->alsa_name_playback, - "playback", - driver->playback_handle, - driver->playback_hw_params, - driver->playback_sw_params, - &driver->playback_nperiods, - &driver->playback_nchannels, - driver->playback_sample_bytes)) { +#ifdef __QNXNTO__ + err = alsa_driver_configure_stream ( + driver, + driver->alsa_name_playback, + "playback", + driver->playback_handle, + &driver->playback_nperiods, + &driver->playback_nchannels, + driver->playback_sample_bytes, + SND_PCM_CHANNEL_PLAYBACK); +#else + err = alsa_driver_configure_stream ( + driver, + driver->alsa_name_playback, + "playback", + driver->playback_handle, + driver->playback_hw_params, + driver->playback_sw_params, + &driver->playback_nperiods, + &driver->playback_nchannels, + driver->playback_sample_bytes); +#endif + if (err) { jack_error ("ALSA: cannot configure playback channel"); return -1; } } +#ifdef __QNXNTO__ + if (driver->capture_handle) { + err = alsa_driver_get_setup(driver, &c_setup, SND_PCM_CHANNEL_CAPTURE); + if(err < 0) { + return -1; + } + cr = c_setup.format.rate; + c_period_size = c_setup.buf.block.frag_size / driver->capture_nchannels + / driver->capture_sample_bytes; + c_periods = c_setup.buf.block.frags; + driver->capture_sample_format = c_setup.format.format; + driver->capture_interleaved = c_setup.format.interleave; + } + if (driver->playback_handle) { + err = alsa_driver_get_setup(driver, &p_setup, SND_PCM_CHANNEL_PLAYBACK); + if(err < 0) { + return -1; + } + pr = p_setup.format.rate; + p_period_size = p_setup.buf.block.frag_size / driver->playback_nchannels + / driver->playback_sample_bytes; + p_periods = p_setup.buf.block.frags; + driver->playback_sample_format = p_setup.format.format; + driver->playback_interleaved = p_setup.format.interleave; + } +#else + /* check the rate, since that's rather important */ if (driver->playback_handle) { @@ -679,6 +938,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, snd_pcm_hw_params_get_rate (driver->capture_hw_params, &cr, &dir); } +#endif if (driver->capture_handle && driver->playback_handle) { if (cr != pr) { @@ -716,6 +976,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, /* check the fragment size, since that's non-negotiable */ if (driver->playback_handle) { +#ifndef __QNXNTO__ snd_pcm_access_t access; err = snd_pcm_hw_params_get_period_size ( @@ -728,7 +989,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->playback_interleaved = (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) || (access == SND_PCM_ACCESS_MMAP_COMPLEX); - +#endif if (p_period_size != driver->frames_per_cycle) { jack_error ("alsa_pcm: requested an interrupt every %" PRIu32 @@ -736,9 +997,21 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->frames_per_cycle, p_period_size); return -1; } +#ifdef __QNXNTO__ + if (p_periods != driver->user_nperiods) { + jack_error ("alsa_pcm: requested %" + PRIu32 + " periods but got %" + PRIu32 + " periods for playback", + driver->user_nperiods, p_periods); + return -1; + } +#endif } if (driver->capture_handle) { +#ifndef __QNXNTO__ snd_pcm_access_t access; err = snd_pcm_hw_params_get_period_size ( @@ -751,7 +1024,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->capture_interleaved = (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) || (access == SND_PCM_ACCESS_MMAP_COMPLEX); - +#endif if (c_period_size != driver->frames_per_cycle) { jack_error ("alsa_pcm: requested an interrupt every %" PRIu32 @@ -759,56 +1032,44 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->frames_per_cycle, c_period_size); return -1; } +#ifdef __QNXNTO__ + /* capture buffers can be configured bigger but it should fail + * if they are smaller as expected + */ + if (c_periods < driver->user_nperiods) { + jack_error ("alsa_pcm: requested %" + PRIu32 + " periods but got %" + PRIu32 + " periods for capture", + driver->user_nperiods, c_periods); + return -1; + } +#endif } - driver->playback_sample_bytes = - snd_pcm_format_physical_width (driver->playback_sample_format) - / 8; - driver->capture_sample_bytes = - snd_pcm_format_physical_width (driver->capture_sample_format) - / 8; + alsa_driver_set_sample_bytes(driver); if (driver->playback_handle) { - switch (driver->playback_sample_format) { - case SND_PCM_FORMAT_FLOAT_LE: - case SND_PCM_FORMAT_S32_LE: - case SND_PCM_FORMAT_S24_3LE: - case SND_PCM_FORMAT_S24_3BE: - case SND_PCM_FORMAT_S24_LE: - case SND_PCM_FORMAT_S24_BE: - case SND_PCM_FORMAT_S16_LE: - case SND_PCM_FORMAT_S32_BE: - case SND_PCM_FORMAT_S16_BE: - break; - - default: + err = alsa_driver_check_format(driver->playback_sample_format); + if(err < 0) { jack_error ("programming error: unhandled format " "type for playback"); - exit (1); + return -1; } } if (driver->capture_handle) { - switch (driver->capture_sample_format) { - case SND_PCM_FORMAT_FLOAT_LE: - case SND_PCM_FORMAT_S32_LE: - case SND_PCM_FORMAT_S24_3LE: - case SND_PCM_FORMAT_S24_3BE: - case SND_PCM_FORMAT_S24_LE: - case SND_PCM_FORMAT_S24_BE: - case SND_PCM_FORMAT_S16_LE: - case SND_PCM_FORMAT_S32_BE: - case SND_PCM_FORMAT_S16_BE: - break; - - default: + err = alsa_driver_check_format(driver->capture_sample_format); + if(err < 0) { jack_error ("programming error: unhandled format " "type for capture"); - exit (1); + return -1; } } if (driver->playback_interleaved) { +#ifndef __QNXNTO__ const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames; if (snd_pcm_mmap_begin(driver->playback_handle, @@ -817,13 +1078,21 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->alsa_name_playback); return -1; } + + // TODO does not work for capture only driver->interleave_unit = snd_pcm_format_physical_width ( driver->playback_sample_format) / 8; +#else + driver->interleave_unit = snd_pcm_format_width( + driver->playback_sample_format) / 8; +#endif } else { + driver->interleave_unit = 0; /* NOT USED */ } +#ifndef __QNXNTO__ if (driver->capture_interleaved) { const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames; @@ -834,7 +1103,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, return -1; } } - +#endif if (driver->playback_nchannels > driver->capture_nchannels) { driver->max_nchannels = driver->playback_nchannels; driver->user_nchannels = driver->capture_nchannels; @@ -932,6 +1201,23 @@ alsa_driver_reset_parameters (alsa_driver_t *driver, user_nperiods, rate); } +#ifdef __QNXNTO__ +static int +snd_pcm_poll_descriptors_count(snd_pcm_t *pcm) +{ + return 1; +} + +static int +snd_pcm_poll_descriptors_revents(snd_pcm_t *pcm, struct pollfd *pfds, + unsigned int nfds, unsigned short *revents) +{ + *revents = pfds->revents; + + return 0; +} +#endif + static int alsa_driver_get_channel_addresses (alsa_driver_t *driver, snd_pcm_uframes_t *capture_avail, @@ -939,10 +1225,11 @@ alsa_driver_get_channel_addresses (alsa_driver_t *driver, snd_pcm_uframes_t *capture_offset, snd_pcm_uframes_t *playback_offset) { - int err; channel_t chn; if (capture_avail) { +#ifndef __QNXNTO__ + int err; if ((err = snd_pcm_mmap_begin ( driver->capture_handle, &driver->capture_areas, (snd_pcm_uframes_t *) capture_offset, @@ -959,9 +1246,25 @@ alsa_driver_get_channel_addresses (alsa_driver_t *driver, + ((a->first + a->step * *capture_offset) / 8); driver->capture_interleave_skip[chn] = (unsigned long ) (a->step / 8); } +#else + for (chn = 0; chn < driver->capture_nchannels; chn++) { + char* const a = driver->capture_areas; + if (driver->capture_interleaved) { + driver->capture_addr[chn] = &a[chn * driver->capture_sample_bytes]; + driver->capture_interleave_skip[chn] = driver->capture_nchannels * + driver->capture_sample_bytes; + } else { + driver->capture_addr[chn] = &a[chn * + driver->capture_sample_bytes * driver->frames_per_cycle]; + driver->capture_interleave_skip[chn] = driver->capture_sample_bytes; + } + } +#endif } if (playback_avail) { +#ifndef __QNXNTO__ + int err; if ((err = snd_pcm_mmap_begin ( driver->playback_handle, &driver->playback_areas, (snd_pcm_uframes_t *) playback_offset, @@ -978,16 +1281,38 @@ alsa_driver_get_channel_addresses (alsa_driver_t *driver, + ((a->first + a->step * *playback_offset) / 8); driver->playback_interleave_skip[chn] = (unsigned long ) (a->step / 8); } +#else + for (chn = 0; chn < driver->playback_nchannels; chn++) { + char* const a = driver->playback_areas; + if (driver->playback_interleaved) { + driver->playback_addr[chn] = &a[chn * driver->playback_sample_bytes]; + driver->playback_interleave_skip[chn] = driver->playback_nchannels * + driver->playback_sample_bytes; + } else { + driver->playback_addr[chn] = &a[chn * + driver->playback_sample_bytes * driver->frames_per_cycle]; + driver->playback_interleave_skip[chn] = driver->playback_sample_bytes; + } + } +#endif } return 0; } +#ifdef __QNXNTO__ +static int +alsa_driver_stream_start(snd_pcm_t *pcm, bool is_capture) +{ + return snd_pcm_channel_go(pcm, is_capture); +} +#else static int alsa_driver_stream_start(snd_pcm_t *pcm, bool is_capture) { return snd_pcm_start(pcm); } +#endif int alsa_driver_start (alsa_driver_t *driver) @@ -1000,7 +1325,7 @@ alsa_driver_start (alsa_driver_t *driver) driver->poll_next = 0; if (driver->playback_handle) { - if ((err = snd_pcm_prepare (driver->playback_handle)) < 0) { + if ((err = alsa_driver_prepare (driver->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { jack_error ("ALSA: prepare error for playback on " "\"%s\" (%s)", driver->alsa_name_playback, snd_strerror(err)); @@ -1010,7 +1335,7 @@ alsa_driver_start (alsa_driver_t *driver) if ((driver->capture_handle && driver->capture_and_playback_not_synced) || !driver->playback_handle) { - if ((err = snd_pcm_prepare (driver->capture_handle)) < 0) { + if ((err = alsa_driver_prepare (driver->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { jack_error ("ALSA: prepare error for capture on \"%s\"" " (%s)", driver->alsa_name_capture, snd_strerror(err)); @@ -1058,20 +1383,24 @@ alsa_driver_start (alsa_driver_t *driver) (driver->midi->start)(driver->midi); if (driver->playback_handle) { + const jack_nframes_t silence_frames = driver->frames_per_cycle * + driver->playback_nperiods; /* fill playback buffer with zeroes, and mark all fragments as having data. */ +#ifndef __QNXNTO__ pavail = snd_pcm_avail_update (driver->playback_handle); - if (pavail != - driver->frames_per_cycle * driver->playback_nperiods) { + if (pavail != silence_frames) { jack_error ("ALSA: full buffer not available at start"); return -1; } +#endif if (alsa_driver_get_channel_addresses (driver, 0, &pavail, 0, &poffset)) { + jack_error("silence failed, get channel addresses"); return -1; } @@ -1086,14 +1415,21 @@ alsa_driver_start (alsa_driver_t *driver) for (chn = 0; chn < driver->playback_nchannels; chn++) { alsa_driver_silence_on_channel ( - driver, chn, - driver->user_nperiods - * driver->frames_per_cycle); + driver, chn, silence_frames); } - snd_pcm_mmap_commit (driver->playback_handle, poffset, - driver->user_nperiods - * driver->frames_per_cycle); +#ifdef __QNXNTO__ + const size_t bytes = silence_frames * driver->playback_nchannels * + driver->playback_sample_bytes; + if ((err = snd_pcm_plugin_write(driver->playback_handle, + driver->playback_areas, bytes)) < bytes) { + jack_error ("ALSA: could not complete write of %" + PRIu32 " frames: error = %d", silence_frames, err); + return -1; + } +#else + snd_pcm_mmap_commit (driver->playback_handle, poffset, silence_frames); +#endif if ((err = alsa_driver_stream_start (driver->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { jack_error ("ALSA: could not start playback (%s)", @@ -1144,7 +1480,13 @@ alsa_driver_stop (alsa_driver_t *driver) ClearOutput(); if (driver->playback_handle) { - if ((err = snd_pcm_drop (driver->playback_handle)) < 0) { +#ifdef __QNXNTO__ + /* In case of playback: Drain discards the frames */ + err = snd_pcm_plugin_playback_drain(driver->playback_handle); +#else + err = snd_pcm_drop (driver->playback_handle); +#endif + if (err < 0) { jack_error ("ALSA: channel flush for playback " "failed (%s)", snd_strerror (err)); return -1; @@ -1154,7 +1496,13 @@ alsa_driver_stop (alsa_driver_t *driver) if (!driver->playback_handle || driver->capture_and_playback_not_synced) { if (driver->capture_handle) { - if ((err = snd_pcm_drop (driver->capture_handle)) < 0) { +#ifdef __QNXNTO__ + /* In case of capture: Flush discards the frames */ + err = snd_pcm_plugin_flush(driver->capture_handle, SND_PCM_CHANNEL_CAPTURE); +#else + err = snd_pcm_drop (driver->capture_handle); +#endif + if (err < 0) { jack_error ("ALSA: channel flush for " "capture failed (%s)", snd_strerror (err)); @@ -1194,72 +1542,102 @@ alsa_driver_restart (alsa_driver_t *driver) } static int -alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs) +alsa_driver_get_status (alsa_driver_t *driver) { - snd_pcm_status_t *status; int res; + snd_pcm_t *pcm_handle; +#ifdef __QNXNTO__ + snd_pcm_channel_status_t status; +#else + snd_pcm_status_t *status; snd_pcm_status_alloca(&status); - +#endif if (driver->capture_handle) { - if ((res = snd_pcm_status(driver->capture_handle, status)) - < 0) { - jack_error("status error: %s", snd_strerror(res)); - } + pcm_handle = driver->capture_handle; } else { - if ((res = snd_pcm_status(driver->playback_handle, status)) - < 0) { - jack_error("status error: %s", snd_strerror(res)); - } + pcm_handle = driver->playback_handle; } +#ifdef __QNXNTO__ + memset (&status, 0, sizeof (status)); + status.channel = driver->capture_handle ? SND_PCM_CHANNEL_CAPTURE : + SND_PCM_CHANNEL_PLAYBACK; + res = snd_pcm_plugin_status(pcm_handle, &status); +#else + res = snd_pcm_status(pcm_handle, status); +#endif + if (res < 0) { + jack_error("status error: %s", snd_strerror(res)); + return -1; + } +#ifdef __QNXNTO__ + return status.status; +#else + return snd_pcm_status_get_state(status); +#endif +} - if (snd_pcm_status_get_state(status) == SND_PCM_STATE_SUSPENDED) - { - jack_log("**** alsa_pcm: pcm in suspended state, resuming it" ); +static int +alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs) +{ + int status; + int res; + + status = alsa_driver_get_status(driver); + if (status == SND_PCM_STATE_SUSPENDED) { if (driver->capture_handle) { - if ((res = snd_pcm_prepare(driver->capture_handle)) + if ((res = alsa_driver_prepare(driver->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { jack_error("error preparing after suspend: %s", snd_strerror(res)); } } if (driver->playback_handle) { - if ((res = snd_pcm_prepare(driver->playback_handle)) + if ((res = alsa_driver_prepare(driver->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { jack_error("error preparing after suspend: %s", snd_strerror(res)); } } } - if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN + // TODO overrun + if (status == SND_PCM_STATE_XRUN && driver->process_count > XRUN_REPORT_DELAY) { - struct timeval now, diff, tstamp; driver->xrun_count++; +#ifdef __QNXNTO__ + /* Timestamp api's are not available as per QNX Documentation */ + *delayed_usecs = 0; +#else + struct timeval now, diff, tstamp; snd_pcm_status_get_tstamp(status,&now); snd_pcm_status_get_trigger_tstamp(status, &tstamp); timersub(&now, &tstamp, &diff); *delayed_usecs = diff.tv_sec * 1000000.0 + diff.tv_usec; +#endif jack_log("**** alsa_pcm: xrun of at least %.3f msecs",*delayed_usecs / 1000.0); if (driver->capture_handle) { jack_log("Repreparing capture"); - if ((res = snd_pcm_prepare(driver->capture_handle)) < 0) { + if ((res = alsa_driver_prepare(driver->capture_handle, + SND_PCM_STREAM_CAPTURE)) < 0) { jack_error("error preparing after xrun: %s", snd_strerror(res)); } } if (driver->playback_handle) { jack_log("Repreparing playback"); - if ((res = snd_pcm_prepare(driver->playback_handle)) < 0) { + if ((res = alsa_driver_prepare(driver->playback_handle, + SND_PCM_STREAM_PLAYBACK)) < 0) { jack_error("error preparing after xrun: %s", snd_strerror(res)); } } } if (alsa_driver_restart (driver)) { + jack_error("xrun recovery failed to restart driver"); return -1; } return 0; } -void +static void alsa_driver_silence_untouched_channels (alsa_driver_t *driver, jack_nframes_t nframes) { @@ -1278,14 +1656,24 @@ alsa_driver_silence_untouched_channels (alsa_driver_t *driver, } } -void -alsa_driver_set_clock_sync_status (alsa_driver_t *driver, channel_t chn, - ClockSyncStatus status) +#ifdef __QNXNTO__ +static int +alsa_driver_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space, bool is_capture) { - driver->clock_sync_data[chn] = status; - alsa_driver_clock_sync_notify (driver, chn, status); + pfds->fd = snd_pcm_file_descriptor (pcm, is_capture); + pfds->events = POLLHUP|POLLNVAL; + pfds->events |= (is_capture == SND_PCM_STREAM_PLAYBACK) ? POLLOUT : POLLIN; + + return 0; } +static snd_pcm_sframes_t +alsa_driver_avail(alsa_driver_t *driver, snd_pcm_t *pcm, bool is_capture) +{ + /* QNX guarantees that after poll() event at least one perido is available */ + return driver->frames_per_cycle; +} +#else static int alsa_driver_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space, bool is_capture) { @@ -1297,6 +1685,7 @@ alsa_driver_avail(alsa_driver_t *driver, snd_pcm_t *pcm, bool is_capture) { return snd_pcm_avail_update(pcm); } +#endif static int under_gdb = FALSE; @@ -1626,6 +2015,17 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) &offset, 0) < 0) { return -1; } + +#ifdef __QNXNTO__ + const size_t bytes = contiguous * driver->capture_nchannels * driver->capture_sample_bytes; + if ((err = snd_pcm_plugin_read(driver->capture_handle, + driver->capture_areas, bytes)) < bytes) { + jack_error ("ALSA: could not complete read of %" + PRIu32 " frames: error = %d", contiguous, err); + return -1; + } +#endif + // JACK2 /* for (chn = 0, node = driver->capture_ports; node; @@ -1644,12 +2044,14 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) */ ReadInput(orig_nframes, contiguous, nread); +#ifndef __QNXNTO__ if ((err = snd_pcm_mmap_commit (driver->capture_handle, offset, contiguous)) < 0) { jack_error ("ALSA: could not complete read of %" PRIu32 " frames: error = %d", contiguous, err); return -1; } +#endif nframes -= contiguous; nread += contiguous; @@ -1767,6 +2169,15 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) contiguous); } +#ifdef __QNXNTO__ + const size_t bytes = contiguous * driver->playback_nchannels * driver->playback_sample_bytes; + if ((err = snd_pcm_plugin_write(driver->playback_handle, + driver->playback_areas, bytes)) < bytes) { + jack_error ("ALSA: could not complete write of %" + PRIu32 " frames: error = %d", contiguous, err); + return -1; + } +#else if ((err = snd_pcm_mmap_commit (driver->playback_handle, offset, contiguous)) < 0) { jack_error ("ALSA: could not complete playback of %" @@ -1774,6 +2185,7 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) if (err != -EPIPE && err != -ESTRPIPE) return -1; } +#endif nframes -= contiguous; nwritten += contiguous; @@ -1859,6 +2271,7 @@ alsa_driver_delete (alsa_driver_t *driver) driver->capture_handle = 0; } +#ifndef __QNXNTO__ if (driver->capture_hw_params) { snd_pcm_hw_params_free (driver->capture_hw_params); driver->capture_hw_params = 0; @@ -1878,6 +2291,7 @@ alsa_driver_delete (alsa_driver_t *driver) snd_pcm_sw_params_free (driver->playback_sw_params); driver->playback_sw_params = 0; } +#endif if (driver->pfd) { free (driver->pfd); @@ -1998,6 +2412,121 @@ discover_alsa_using_apps () } } +static int +alsa_driver_open (alsa_driver_t *driver, bool is_capture) +{ + int err = 0; + char* current_apps; + + if(is_capture) { +#ifdef __QNXNTO__ + err = snd_pcm_open_name (&driver->capture_handle, + driver->alsa_name_capture, + SND_PCM_OPEN_CAPTURE | SND_PCM_OPEN_NONBLOCK); +#else + err = snd_pcm_open (&driver->capture_handle, + driver->alsa_name_capture, + SND_PCM_STREAM_CAPTURE, + SND_PCM_NONBLOCK); +#endif + } else { +#ifdef __QNXNTO__ + err = snd_pcm_open_name (&driver->playback_handle, + driver->alsa_name_playback, + SND_PCM_OPEN_PLAYBACK | SND_PCM_OPEN_NONBLOCK); +#else + err = snd_pcm_open (&driver->playback_handle, + driver->alsa_name_playback, + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK); +#endif + } + if (err < 0) { + switch (errno) { + case EBUSY: +#ifdef __ANDROID__ + jack_error ("\n\nATTENTION: The device \"%s\" is " + "already in use. Please stop the" + " application using it and " + "run JACK again", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback); +#else + current_apps = discover_alsa_using_apps (); + if (current_apps) { + jack_error ("\n\nATTENTION: The device \"%s\" is " + "already in use. The following applications " + " are using your soundcard(s) so you should " + " check them and stop them as necessary before " + " trying to start JACK again:\n\n%s", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + current_apps); + free (current_apps); + } else { + jack_error ("\n\nATTENTION: The device \"%s\" is " + "already in use. Please stop the" + " application using it and " + "run JACK again", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback); + } +#endif + break; + + case EPERM: + jack_error ("you do not have permission to open " + "the audio device \"%s\" for playback", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback); + break; + + case EINVAL: + jack_error ("the state of handle or the mode is invalid " + "or invalid state change occured \"%s\" for %s", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + is_capture ? "capture" : "playback"); + break; + + case ENOENT: + jack_error ("device \"%s\" does not exist for %s", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + is_capture ? "capture" : "playback"); + break; + + case ENOMEM: + jack_error ("Not enough memory available for allocation for \"%s\" for %s", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + is_capture ? "capture" : "playback"); + break; + + case SND_ERROR_INCOMPATIBLE_VERSION: + jack_error ("Version mismatch \"%s\" for %s", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + is_capture ? "capture" : "playback"); + break; + } + alsa_driver_delete (driver); + if(is_capture) { + driver->capture_handle = NULL; + } else { + driver->playback_handle = NULL; + } + } + + if (is_capture && driver->capture_handle) { +#ifdef __QNXNTO__ + snd_pcm_nonblock_mode (driver->capture_handle, 0); +#else + snd_pcm_nonblock (driver->capture_handle, 0); +#endif + } else if(!is_capture && driver->playback_handle) { +#ifdef __QNXNTO__ + snd_pcm_nonblock_mode (driver->playback_handle, 0); +#else + snd_pcm_nonblock (driver->playback_handle, 0); +#endif + } + + return err; +} + jack_driver_t * alsa_driver_new (char *name, char *playback_alsa_device, char *capture_alsa_device, @@ -2021,7 +2550,6 @@ alsa_driver_new (char *name, char *playback_alsa_device, ) { int err; - char* current_apps; alsa_driver_t *driver; jack_info ("creating alsa driver ... %s|%s|%" PRIu32 "|%" PRIu32 @@ -2070,6 +2598,12 @@ alsa_driver_new (char *name, char *playback_alsa_device, driver->capture_addr = 0; driver->playback_interleave_skip = NULL; driver->capture_interleave_skip = NULL; +#ifdef __QNXNTO__ + driver->playback_areas = NULL; + driver->playback_areas_ptr = NULL; + driver->capture_areas = NULL; + driver->capture_areas_ptr = NULL; +#endif driver->silent = 0; @@ -2105,113 +2639,29 @@ alsa_driver_new (char *name, char *playback_alsa_device, driver->midi = midi_driver; driver->xrun_recovery = 0; +#ifndef __QNXNTO__ if (alsa_driver_check_card_type (driver)) { alsa_driver_delete (driver); return NULL; } alsa_driver_hw_specific (driver, hw_monitoring, hw_metering); - - if (playing) { - if (snd_pcm_open (&driver->playback_handle, - playback_alsa_device, - SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK) < 0) { - switch (errno) { - case EBUSY: -#ifdef __ANDROID__ - jack_error ("\n\nATTENTION: The playback device \"%s\" is " - "already in use. Please stop the" - " application using it and " - "run JACK again", - playback_alsa_device); -#else - current_apps = discover_alsa_using_apps (); - if (current_apps) { - jack_error ("\n\nATTENTION: The playback device \"%s\" is " - "already in use. The following applications " - " are using your soundcard(s) so you should " - " check them and stop them as necessary before " - " trying to start JACK again:\n\n%s", - playback_alsa_device, - current_apps); - free (current_apps); - } else { - jack_error ("\n\nATTENTION: The playback device \"%s\" is " - "already in use. Please stop the" - " application using it and " - "run JACK again", - playback_alsa_device); - } #endif - alsa_driver_delete (driver); - return NULL; - case EPERM: - jack_error ("you do not have permission to open " - "the audio device \"%s\" for playback", - playback_alsa_device); - alsa_driver_delete (driver); - return NULL; - break; - } - - driver->playback_handle = NULL; - } - - if (driver->playback_handle) { - snd_pcm_nonblock (driver->playback_handle, 0); + if (playing) { + err = alsa_driver_open(driver, SND_PCM_STREAM_PLAYBACK); + if(err < 0) { + jack_error ("\n\nATTENTION: Opening of the playback device \"%s\" failed.", + playback_alsa_device); + return NULL; } } - - if (capturing) { - if (snd_pcm_open (&driver->capture_handle, - capture_alsa_device, - SND_PCM_STREAM_CAPTURE, - SND_PCM_NONBLOCK) < 0) { - switch (errno) { - case EBUSY: -#ifdef __ANDROID__ - jack_error ("\n\nATTENTION: The capture (recording) device \"%s\" is " - "already in use", - capture_alsa_device); -#else - current_apps = discover_alsa_using_apps (); - if (current_apps) { - jack_error ("\n\nATTENTION: The capture device \"%s\" is " - "already in use. The following applications " - " are using your soundcard(s) so you should " - " check them and stop them as necessary before " - " trying to start JACK again:\n\n%s", - capture_alsa_device, - current_apps); - free (current_apps); - } else { - jack_error ("\n\nATTENTION: The capture (recording) device \"%s\" is " - "already in use. Please stop the" - " application using it and " - "run JACK again", - capture_alsa_device); - } - alsa_driver_delete (driver); - return NULL; -#endif - break; - - case EPERM: - jack_error ("you do not have permission to open " - "the audio device \"%s\" for capture", - capture_alsa_device); - alsa_driver_delete (driver); - return NULL; - break; - } - - driver->capture_handle = NULL; - } - - if (driver->capture_handle) { - snd_pcm_nonblock (driver->capture_handle, 0); + if(capturing) { + err = alsa_driver_open(driver, SND_PCM_STREAM_CAPTURE); + if(err < 0) { + jack_error ("\n\nATTENTION: Opening of the capture device \"%s\" failed.", + capture_alsa_device); + return NULL; } } @@ -2253,6 +2703,7 @@ alsa_driver_new (char *name, char *playback_alsa_device, } } +#ifndef __QNXNTO__ driver->playback_hw_params = 0; driver->capture_hw_params = 0; driver->playback_sw_params = 0; @@ -2293,7 +2744,7 @@ alsa_driver_new (char *name, char *playback_alsa_device, return NULL; } } - +#endif if (alsa_driver_set_parameters (driver, frames_per_cycle, user_nperiods, rate)) { alsa_driver_delete (driver); @@ -2314,25 +2765,6 @@ alsa_driver_new (char *name, char *playback_alsa_device, return (jack_driver_t *) driver; } -int -alsa_driver_listen_for_clock_sync_status (alsa_driver_t *driver, - ClockSyncListenerFunction func, - void *arg) -{ - ClockSyncListener *csl; - - csl = (ClockSyncListener *) malloc (sizeof (ClockSyncListener)); - csl->function = func; - csl->arg = arg; - csl->id = driver->next_clock_sync_listener_id++; - - pthread_mutex_lock (&driver->clock_sync_lock); - driver->clock_sync_listeners = - jack_slist_prepend (driver->clock_sync_listeners, csl); - pthread_mutex_unlock (&driver->clock_sync_lock); - return csl->id; -} - int alsa_driver_stop_listening_to_clock_sync_status (alsa_driver_t *driver, unsigned int which) @@ -2357,22 +2789,6 @@ alsa_driver_stop_listening_to_clock_sync_status (alsa_driver_t *driver, return ret; } -void -alsa_driver_clock_sync_notify (alsa_driver_t *driver, channel_t chn, - ClockSyncStatus status) -{ - JSList *node; - - pthread_mutex_lock (&driver->clock_sync_lock); - for (node = driver->clock_sync_listeners; node; - node = jack_slist_next (node)) { - ClockSyncListener *csl = (ClockSyncListener *) node->data; - csl->function (chn, status, csl->arg); - } - pthread_mutex_unlock (&driver->clock_sync_lock); - -} - /* DRIVER "PLUGIN" INTERFACE */ const char driver_client_name[] = "alsa_pcm"; diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h index f01c08eaf..2acc07dc4 100644 --- a/linux/alsa/alsa_driver.h +++ b/linux/alsa/alsa_driver.h @@ -21,7 +21,12 @@ #ifndef __jack_alsa_driver_h__ #define __jack_alsa_driver_h__ +#ifdef __QNXNTO__ +#include +#else #include +#endif + #include "bitset.h" #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -41,6 +46,24 @@ #include "memops.h" #include "alsa_midi.h" +#ifdef __QNXNTO__ +#define SND_PCM_FORMAT_S16_LE SND_PCM_SFMT_S16_LE +#define SND_PCM_FORMAT_S16_BE SND_PCM_SFMT_S16_BE +#define SND_PCM_FORMAT_S24_LE SND_PCM_SFMT_S24_LE +#define SND_PCM_FORMAT_S24_BE SND_PCM_SFMT_S24_BE +#define SND_PCM_FORMAT_S32_LE SND_PCM_SFMT_S32_LE +#define SND_PCM_FORMAT_S32_BE SND_PCM_SFMT_S32_BE +#define SND_PCM_FORMAT_FLOAT_LE SND_PCM_SFMT_FLOAT_LE +#define SND_PCM_STATE_SUSPENDED SND_PCM_STATUS_SUSPENDED +#define SND_PCM_STATE_XRUN SND_PCM_STATUS_UNDERRUN +#define SND_PCM_STREAM_PLAYBACK SND_PCM_CHANNEL_PLAYBACK +#define SND_PCM_STREAM_CAPTURE SND_PCM_CHANNEL_CAPTURE + +typedef unsigned long snd_pcm_uframes_t; +typedef signed long snd_pcm_sframes_t; +typedef int32_t alsa_driver_default_format_t; +#endif + #ifdef __cplusplus extern "C" { @@ -58,13 +81,28 @@ typedef struct _alsa_driver { JACK_DRIVER_NT_DECL +#ifdef __QNXNTO__ + unsigned int playback_sample_format; + unsigned int capture_sample_format; + void *capture_areas; + void *playback_areas; + void *capture_areas_ptr; + void *playback_areas_ptr; +#else + snd_pcm_format_t playback_sample_format; + snd_pcm_format_t capture_sample_format; + const snd_pcm_channel_area_t *capture_areas; + const snd_pcm_channel_area_t *playback_areas; + snd_pcm_hw_params_t *playback_hw_params; + snd_pcm_sw_params_t *playback_sw_params; + snd_pcm_hw_params_t *capture_hw_params; + snd_pcm_sw_params_t *capture_sw_params; +#endif int poll_timeout_ms; jack_time_t poll_last; jack_time_t poll_next; char **playback_addr; char **capture_addr; - const snd_pcm_channel_area_t *capture_areas; - const snd_pcm_channel_area_t *playback_areas; struct pollfd *pfd; unsigned int playback_nfds; unsigned int capture_nfds; @@ -89,8 +127,6 @@ typedef struct _alsa_driver { char *alsa_driver; bitset_t channels_not_done; bitset_t channels_done; - snd_pcm_format_t playback_sample_format; - snd_pcm_format_t capture_sample_format; float max_sample_val; unsigned long user_nperiods; unsigned int playback_nperiods; @@ -99,10 +135,6 @@ typedef struct _alsa_driver { snd_ctl_t *ctl_handle; snd_pcm_t *playback_handle; snd_pcm_t *capture_handle; - snd_pcm_hw_params_t *playback_hw_params; - snd_pcm_sw_params_t *playback_sw_params; - snd_pcm_hw_params_t *capture_hw_params; - snd_pcm_sw_params_t *capture_sw_params; jack_hardware_t *hw; ClockSyncStatus *clock_sync_data; jack_client_t *client; @@ -211,18 +243,6 @@ alsa_driver_write_to_channel (alsa_driver_t *driver, alsa_driver_mark_channel_done (driver, channel); } -void alsa_driver_silence_untouched_channels (alsa_driver_t *driver, - jack_nframes_t nframes); -void alsa_driver_set_clock_sync_status (alsa_driver_t *driver, channel_t chn, - ClockSyncStatus status); -int alsa_driver_listen_for_clock_sync_status (alsa_driver_t *, - ClockSyncListenerFunction, - void *arg); -int alsa_driver_stop_listen_for_clock_sync_status (alsa_driver_t *, - unsigned int); -void alsa_driver_clock_sync_notify (alsa_driver_t *, channel_t chn, - ClockSyncStatus); - int alsa_driver_reset_parameters (alsa_driver_t *driver, jack_nframes_t frames_per_cycle, @@ -269,8 +289,6 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes); int alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes); -jack_time_t jack_get_microseconds(void); - // Code implemented in JackAlsaDriver.cpp void ReadInput(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread); diff --git a/linux/alsa/usx2y.h b/linux/alsa/usx2y.h index 522d19a86..51a82a0f9 100644 --- a/linux/alsa/usx2y.h +++ b/linux/alsa/usx2y.h @@ -22,6 +22,8 @@ #ifndef __jack_usx2y_h__ #define __jack_usx2y_h__ + #include + #define USX2Y_MAXPACK 50 #define USX2Y_MAXBUFFERMS 100 #define USX2Y_MAXSTRIDE 3 @@ -51,7 +53,9 @@ typedef struct snd_usX2Y_hwdep_pcm_shm snd_usX2Y_hwdep_pcm_shm_t; typedef struct { alsa_driver_t *driver; +#ifndef __QNXNTO__ snd_hwdep_t *hwdep_handle; +#endif struct pollfd pfds; struct snd_usX2Y_hwdep_pcm_shm *hwdep_pcm_shm; int playback_iso_start; From 8e8ae8eefa892f98ef50bf8b860e403f3fde53fd Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Tue, 15 Oct 2019 14:18:37 +0200 Subject: [PATCH 10/42] alsa: Simplify get state on linux shave off a few usec from processing time Change-Id: I052d168b11caa54d881d651c32822d6db33383b8 Signed-off-by: Adam Miartus (cherry picked from commit 138925dfd5c45c99d7978ba39f99d9d0fc349727) Signed-off-by: Timo Wischer --- linux/alsa/alsa_driver.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 5f4f9d9e2..a8f5916dd 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -1544,36 +1544,31 @@ alsa_driver_restart (alsa_driver_t *driver) static int alsa_driver_get_status (alsa_driver_t *driver) { - int res; snd_pcm_t *pcm_handle; #ifdef __QNXNTO__ + int res; snd_pcm_channel_status_t status; -#else - snd_pcm_status_t *status; - snd_pcm_status_alloca(&status); #endif + if (driver->capture_handle) { pcm_handle = driver->capture_handle; } else { pcm_handle = driver->playback_handle; } + #ifdef __QNXNTO__ memset (&status, 0, sizeof (status)); status.channel = driver->capture_handle ? SND_PCM_CHANNEL_CAPTURE : SND_PCM_CHANNEL_PLAYBACK; res = snd_pcm_plugin_status(pcm_handle, &status); -#else - res = snd_pcm_status(pcm_handle, status); -#endif if (res < 0) { jack_error("status error: %s", snd_strerror(res)); return -1; } -#ifdef __QNXNTO__ return status.status; #else - return snd_pcm_status_get_state(status); + return snd_pcm_state(pcm_handle); #endif } From fc1d6a5435197cb9a55a2ca2dab84ac81805b74f Mon Sep 17 00:00:00 2001 From: Timo Wischer Date: Tue, 7 May 2019 16:59:08 +0200 Subject: [PATCH 11/42] Support QNX build with WAF Change-Id: I9ee88d6b2531622c8d46f69872d30750168c4e4a Signed-off-by: Timo Wischer --- Makefile | 14 +++++++++++ common/wscript | 32 ++++++++++++++++++------ example-clients/wscript | 10 +++++--- tests/wscript | 2 ++ wscript | 54 ++++++++++++++++++++++++++++++----------- 5 files changed, 87 insertions(+), 25 deletions(-) create mode 100755 Makefile mode change 100644 => 100755 common/wscript mode change 100644 => 100755 wscript diff --git a/Makefile b/Makefile new file mode 100755 index 000000000..3ff583998 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +.PHONY: all +all: + CC=${QNX_HOST}/usr/bin/aarch64-unknown-nto-qnx7.0.0-gcc CXX=${QNX_HOST}/usr/bin/aarch64-unknown-nto-qnx7.0.0-g++ AR=${QNX_HOST}/usr/bin/aarch64-unknown-nto-qnx7.0.0-ar LDFLAGS="-L${INSTALL_ROOT_nto}/aarch64le/lib -L${INSTALL_ROOT_nto}/aarch64le/usr/lib -L${QNX_TARGET}/aarch64le/lib -L${QNX_TARGET}/aarch64le/usr/lib" PKG_CONFIG_LIBDIR=${INSTALL_ROOT_nto}/aarch64le/usr/lib/pkgconfig ./waf configure --platform=qnx --prefix=/usr --libdir=/usr/lib64 + ./waf build + ./waf install --destdir=${INSTALL_ROOT_nto}/aarch64le + +.PHONY: install +install: all + +.PHONY:clean +clean: + # ignore error codes otherwise it would for example fail if clean is called before configure + -./waf clean + diff --git a/common/wscript b/common/wscript old mode 100644 new mode 100755 index cb794e28b..c41089ea3 --- a/common/wscript +++ b/common/wscript @@ -28,6 +28,8 @@ def create_jack_process_obj(bld, target, sources, uselib = None, framework = Non env_includes = ['../macosx', '../posix', '../macosx/coreaudio'] if bld.env['IS_LINUX']: env_includes = ['../linux', '../posix', '../linux/alsa'] + if bld.env['IS_QNX']: + env_includes = ['../qnx', '../posix'] if bld.env['IS_SUN']: env_includes = ['../solaris', '../posix', '../solaris/oss'] if bld.env['IS_WINDOWS']: @@ -36,7 +38,7 @@ def create_jack_process_obj(bld, target, sources, uselib = None, framework = Non process.name = target process.target = target process.source = sources - if bld.env['IS_LINUX'] or bld.env['IS_MACOSX']: + if bld.env['IS_LINUX'] or bld.env['IS_MACOSX'] or bld.env['IS_QNX']: process.env.append_value('CPPFLAGS', '-fvisibility=hidden') process.install_path = '${ADDON_DIR}/' process.use = [uselib.name] @@ -92,6 +94,22 @@ def build(bld): uselib.append('RT') uselib.append('DL') + if bld.env['IS_QNX']: + common_libsources += [ + 'JackDebugClient.cpp', + 'timestamps.c', + 'promiscuous.c', + '../posix/JackPosixThread.cpp', + '../posix/JackPosixProcessSync.cpp', + '../posix/JackPosixMutex.cpp', + '../posix/JackSocket.cpp', + '../posix/JackFifo.cpp', + '../linux/JackLinuxTime.c', + ] + includes = ['../qnx', '../posix'] + includes + uselib.append('SOCKET') + # libdl and librt is included in libc in QNX + if bld.env['IS_SUN']: common_libsources += [ 'JackDebugClient.cpp', @@ -163,7 +181,7 @@ def build(bld): 'JackMetadata.cpp', ] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: clientlib.source += [ '../posix/JackSocketClientChannel.cpp', '../posix/JackPosixServerLaunch.cpp', @@ -192,7 +210,7 @@ def build(bld): if not bld.env['IS_WINDOWS']: clientlib.vnum = bld.env['JACK_API_VERSION'] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: clientlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_MACOSX']: @@ -270,7 +288,7 @@ def build(bld): 'JackMetadata.cpp', ] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: serverlib.source += [ '../posix/JackSocketServerChannel.cpp', '../posix/JackSocketNotifyChannel.cpp', @@ -307,7 +325,7 @@ def build(bld): if not bld.env['IS_WINDOWS']: serverlib.vnum = bld.env['JACK_API_VERSION'] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: serverlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_MACOSX']: @@ -333,7 +351,7 @@ def build(bld): if skipshared: netlib.env['SHLIB_MARKER'] = '' netlib.use += ['WS2_32', 'WINMM'] - elif not bld.env['IS_MACOSX']: + elif not bld.env['IS_MACOSX'] and not bld.env['IS_QNX']: netlib.use += ['RT'] netlib.install_path = '${LIBDIR}' netlib.source = [ @@ -347,7 +365,7 @@ def build(bld): 'JackGlobals.cpp', 'ringbuffer.c'] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: netlib.source += ['../posix/JackNetUnixSocket.cpp','../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', '../linux/JackLinuxTime.c'] netlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') diff --git a/example-clients/wscript b/example-clients/wscript index a8857aa79..065fcf157 100644 --- a/example-clients/wscript +++ b/example-clients/wscript @@ -49,6 +49,8 @@ def build(bld): os_incdir = ['../linux', '../posix'] if bld.env['IS_MACOSX']: os_incdir = ['../macosx', '../posix'] + if bld.env['IS_QNX']: + os_incdir = ['../qnx', '../posix'] if bld.env['IS_SUN']: os_incdir = ['../solaris', '../posix'] if bld.env['IS_WINDOWS']: @@ -76,7 +78,7 @@ def build(bld): prog.use = use if bld.env['IS_LINUX']: prog.use += ['RT', 'M'] - if bld.env['IS_SUN']: + if bld.env['IS_SUN'] or bld.env['IS_QNX']: prog.use += ['M'] #prog.cflags = ['-Wno-deprecated-declarations', '-Wno-misleading-indentation'] #prog.cxxflags = ['-Wno-deprecated-declarations', '-Wno-misleading-indentation'] @@ -90,7 +92,7 @@ def build(bld): prog.use = ['clientlib'] if bld.env['IS_LINUX']: prog.use += ['RT', 'READLINE'] - if bld.env['IS_MACOSX']: + if bld.env['IS_MACOSX'] or bld.env['IS_QNX']: prog.use += ['READLINE'] if bld.env['IS_WINDOWS']: prog.use += ['READLINE'] @@ -101,7 +103,7 @@ def build(bld): prog.includes = os_incdir + ['../common/jack', '../common'] prog.source = 'capture_client.c' prog.use = ['clientlib'] - if bld.env['IS_MACOSX']: + if bld.env['IS_MACOSX'] or bld.env['IS_QNX']: prog.use += ['SNDFILE'] if bld.env['IS_LINUX']: prog.use += ['RT', 'SNDFILE'] @@ -111,7 +113,7 @@ def build(bld): prog.uselib = ['SNDFILE'] prog.target = 'jack_rec' - if bld.env['IS_LINUX'] or bld.env['IS_MACOSX']: + if bld.env['IS_LINUX'] or bld.env['IS_MACOSX'] or bld.env['IS_QNX']: prog = bld(features = 'c cprogram') prog.includes = os_incdir + ['.', '..', '../common/jack', '../common'] prog.source = ['netsource.c', '../common/netjack_packet.c'] diff --git a/tests/wscript b/tests/wscript index 96a63bc9d..0dcae8c39 100644 --- a/tests/wscript +++ b/tests/wscript @@ -20,6 +20,8 @@ def build(bld): prog.includes = ['..','../macosx', '../posix', '../common/jack', '../common'] if bld.env['IS_LINUX']: prog.includes = ['..','../linux', '../posix', '../common/jack', '../common'] + if bld.env['IS_QNX']: + prog.includes = ['..','../qnx', '../posix', '../common/jack', '../common'] if bld.env['IS_SUN']: prog.includes = ['..','../solaris', '../posix', '../common/jack', '../common'] prog.source = test_program_sources diff --git a/wscript b/wscript old mode 100644 new mode 100755 index 14e561a36..7d46528fc --- a/wscript +++ b/wscript @@ -95,6 +95,15 @@ def options(opt): alsa.check_cfg( package='alsa >= 1.0.18', args='--cflags --libs') + # Check for QNX sound headers + qsa = opt.add_auto_option( + 'qsa', + help='Enable QNX sound driver', + conf_dest='BUILD_DRIVER_ALSA') + qsa.check(lib='asound', uselib_store='ALSA') + qsa.check( + header_name=['sys/asoundlib.h'], + msg='Checking for header sys/asoundlib.h') firewire = opt.add_auto_option( 'firewire', help='Enable FireWire driver (FFADO)', @@ -185,6 +194,7 @@ def detect_platform(conf): platforms = [ # ('KEY, 'Human readable name', ['strings', 'to', 'check', 'for']) ('IS_LINUX', 'Linux', ['gnu0', 'gnukfreebsd', 'linux', 'posix']), + ('IS_QNX', 'QNX', ['qnx']), ('IS_MACOSX', 'MacOS X', ['darwin']), ('IS_SUN', 'SunOS', ['sunos']), ('IS_WINDOWS', 'Windows', ['cygwin', 'msys', 'win32']) @@ -304,7 +314,11 @@ def configure(conf): conf.check_cc(fragment=fragment, define_name='HAVE_NGREG', mandatory=False, msg='Checking for NGREG') - conf.env['LIB_PTHREAD'] = ['pthread'] + if conf.env['IS_QNX']: + conf.env['LIB_PTHREAD'] = ['c'] + conf.env['LIB_SOCKET'] = ['socket'] + else: + conf.env['LIB_PTHREAD'] = ['pthread'] conf.env['LIB_DL'] = ['dl'] conf.env['LIB_RT'] = ['rt'] conf.env['LIB_M'] = ['m'] @@ -512,6 +526,9 @@ def obj_add_includes(bld, obj): if bld.env['IS_MACOSX']: obj.includes += ['macosx', 'posix'] + if bld.env['IS_QNX']: + obj.includes += ['qnx', 'posix'] + if bld.env['IS_SUN']: obj.includes += ['posix', 'solaris'] @@ -540,6 +557,10 @@ def build_jackd(bld): jackd.use += ['DL', 'PTHREAD'] jackd.framework = ['CoreFoundation'] + if bld.env['IS_QNX']: + jackd.use += ['M', 'PTHREAD'] + + if bld.env['IS_SUN']: jackd.use += ['DL', 'PTHREAD'] @@ -598,16 +619,20 @@ def build_drivers(bld): alsa_src = [ 'common/memops.c', 'linux/alsa/JackAlsaDriver.cpp', - 'linux/alsa/alsa_rawmidi.c', - 'linux/alsa/alsa_seqmidi.c', - 'linux/alsa/alsa_midi_jackmp.cpp', - 'linux/alsa/generic_hw.c', - 'linux/alsa/hdsp.c', - 'linux/alsa/alsa_driver.c', - 'linux/alsa/hammerfall.c', - 'linux/alsa/ice1712.c' + 'linux/alsa/alsa_driver.c' ] + if not bld.env['IS_QNX']: + alsa_src += [ + 'linux/alsa/alsa_rawmidi.c', + 'linux/alsa/alsa_seqmidi.c', + 'linux/alsa/alsa_midi_jackmp.cpp', + 'linux/alsa/generic_hw.c', + 'linux/alsa/hdsp.c', + 'linux/alsa/hammerfall.c', + 'linux/alsa/ice1712.c' + ] + alsarawmidi_src = [ 'linux/alsarawmidi/JackALSARawMidiDriver.cpp', 'linux/alsarawmidi/JackALSARawMidiInputPort.cpp', @@ -704,11 +729,12 @@ def build_drivers(bld): target = 'alsa', source = alsa_src, use = ['ALSA']) - create_driver_obj( - bld, - target = 'alsarawmidi', - source = alsarawmidi_src, - use = ['ALSA']) + if not bld.env['IS_QNX']: + create_driver_obj( + bld, + target = 'alsarawmidi', + source = alsarawmidi_src, + use = ['ALSA']) if bld.env['BUILD_DRIVER_FFADO']: create_driver_obj( From 831f4e64b4ac434285b8428b4243e9ffdcbcfba4 Mon Sep 17 00:00:00 2001 From: Laxmi Devi Date: Mon, 20 Jan 2020 10:58:29 +0530 Subject: [PATCH 12/42] Workaround: JackClient: Fix printf NULL issue vsnprintf implementation on QNX can not handle NULL string parameter and will interrupt, This is a workaround for the same Change-Id: I18c57ec4b596336cea6f3061caf49c0b33afe198 Signed-off-by: Laxmi Devi --- common/JackClient.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/common/JackClient.cpp b/common/JackClient.cpp index b03dbc25d..b8303ab0e 100644 --- a/common/JackClient.cpp +++ b/common/JackClient.cpp @@ -34,6 +34,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. using namespace std; +#ifdef __QNXNTO__ +void __attribute__((constructor)) init_output_for_percent_s_NULL_for_clients(); +void init_output_for_percent_s_NULL_for_clients() +{ + extern const char *output_for_percent_s_NULL; + output_for_percent_s_NULL = "(null)"; +} +#endif + namespace Jack { From 92911ea1696713388bfb7fc8dd838e3f1b69966d Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Mon, 23 Sep 2019 14:02:32 +0200 Subject: [PATCH 13/42] alsa: Add multi dev support linux release 1.0 what was tested: - playing and recording from multiple devices - linking devices, fallback for case when some or all devices are not linked - xrun recovery notes: - we should be using epoll instead of poll, ~ +1 manday to implement - solution can be simplified to remove doubling parameters for capture and playback device, instead one struct for abstract "device" could be created that has parameter capture or playaback, this would simplify code and reduce duplicate lines of code Change-Id: I457b8e7e8ff0d04db18333b44bf64bf26a253bac Signed-off-by: Adam Miartus Signed-off-by: Timo Wischer --- linux/alsa/JackAlsaDriver.cpp | 204 +++-- linux/alsa/JackAlsaDriver.h | 4 +- linux/alsa/alsa_driver.c | 1558 +++++++++++++++++---------------- linux/alsa/alsa_driver.h | 166 ++-- 4 files changed, 1034 insertions(+), 898 deletions(-) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index 6fcdf4889..98594da5a 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -32,6 +32,11 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include #include #include +#include +#include +#include +#include +#include #include "JackAlsaDriver.h" #include "JackEngineControl.h" @@ -126,7 +131,7 @@ void JackAlsaDriver::UpdateLatencies() int JackAlsaDriver::Attach() { JackPort* port; - jack_port_id_t port_index; + jack_port_id_t port_id; unsigned long port_flags = (unsigned long)CaptureDriverFlags; char name[REAL_JACK_PORT_NAME_SIZE+1]; char alias[REAL_JACK_PORT_NAME_SIZE+1]; @@ -145,41 +150,47 @@ int JackAlsaDriver::Attach() jack_log("JackAlsaDriver::Attach fBufferSize %ld fSampleRate %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); - for (int i = 0; i < fCaptureChannels; i++) { - snprintf(alias, sizeof(alias), "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); - snprintf(name, sizeof(name), "%s:capture_%d", fClientControl.fName, i + 1); - if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize, &port_index) < 0) { + for (int i = 0, port_list_index = 0; i < alsa_driver->devices_c_count; ++i) { + alsa_device_t *device = &alsa_driver->devices[i]; + for (int j = 0; j < device->capture_nchannels; ++j, ++port_list_index) { + snprintf(name, sizeof(name), "%s:capture_%d", fClientControl.fName, port_list_index + 1); + snprintf(alias, sizeof(alias), "%s:%s:capture_%d", fAliasName, device->capture_name, j + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize, &port_id) < 0) { jack_error("driver: cannot register port for %s", name); - return -1; + return -1; + } + port = fGraphManager->GetPort(port_id); + port->SetAlias(alias); + fCapturePortList[port_list_index] = port_id; + jack_log("JackAlsaDriver::Attach fCapturePortList[i] %ld ", port_id); } - port = fGraphManager->GetPort(port_index); - port->SetAlias(alias); - fCapturePortList[i] = port_index; - jack_log("JackAlsaDriver::Attach fCapturePortList[i] %ld ", port_index); } port_flags = (unsigned long)PlaybackDriverFlags; - for (int i = 0; i < fPlaybackChannels; i++) { - snprintf(alias, sizeof(alias), "%s:%s:in%d", fAliasName, fPlaybackDriverName, i + 1); - snprintf(name, sizeof(name), "%s:playback_%d", fClientControl.fName, i + 1); - if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize, &port_index) < 0) { - jack_error("driver: cannot register port for %s", name); - return -1; - } - port = fGraphManager->GetPort(port_index); - port->SetAlias(alias); - fPlaybackPortList[i] = port_index; - jack_log("JackAlsaDriver::Attach fPlaybackPortList[i] %ld ", port_index); + for (int i = 0, port_list_index = 0; i < alsa_driver->devices_p_count; ++i) { + alsa_device_t *device = &alsa_driver->devices[i]; + for (int j = 0; j < device->playback_nchannels; ++j, ++port_list_index) { + snprintf(name, sizeof(name), "%s:playback_%d", fClientControl.fName, port_list_index + 1); + snprintf(alias, sizeof(alias), "%s:%s:playback_%d", fAliasName, device->playback_name, j + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize, &port_id) < 0) { + jack_error("driver: cannot register port for %s", name); + return -1; + } + port = fGraphManager->GetPort(port_id); + port->SetAlias(alias); + fPlaybackPortList[port_list_index] = port_id; + jack_log("JackAlsaDriver::Attach fPlaybackPortList[i] %ld ", port_id); - // Monitor ports - if (fWithMonitorPorts) { - jack_log("Create monitor port"); - snprintf(name, sizeof(name), "%s:monitor_%d", fClientControl.fName, i + 1); - if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, MonitorDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { - jack_error("ALSA: cannot register monitor port for %s", name); - } else { - fMonitorPortList[i] = port_index; + // Monitor ports + if (fWithMonitorPorts) { + jack_log("Create monitor port"); + snprintf(name, sizeof(name), "%s:monitor_%d", fClientControl.fName, port_list_index + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, MonitorDriverFlags, fEngineControl->fBufferSize, &port_id) < 0) { + jack_error("ALSA: cannot register monitor port for %s", name); + } else { + fMonitorPortList[port_list_index] = port_id; + } } } } @@ -297,13 +308,23 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, jack_nframes_t playback_latency, const char* midi_driver_name) { + struct array_string_t capture_drivers = array_string_split(capture_driver_name, ' '); + struct array_string_t playback_drivers = array_string_split(playback_driver_name, ' '); + // Generic JackAudioDriver Open if (JackAudioDriver::Open(nframes, samplerate, capturing, playing, - inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, + inchannels, outchannels, monitor, + capture_drivers.size > 1 ? capture_drivers.data[0] : capture_driver_name, + playback_drivers.size > 1 ? playback_drivers.data[0] : playback_driver_name, capture_latency, playback_latency) != 0) { + array_string_free(&capture_drivers); + array_string_free(&playback_drivers); return -1; } + jack_log("JackAlsaDriver::Open capture_driver_name = %s", capture_driver_name); + jack_log("JackAlsaDriver::Open playback_driver_name = %s", playback_driver_name); + alsa_midi_t *midi = 0; #ifndef __QNXNTO__ @@ -314,15 +335,18 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, midi = alsa_rawmidi_new((jack_client_t*)this); #endif + // FIXME: needs adaptation for multiple drivers if (JackServerGlobals::on_device_acquire != NULL) { - int capture_card = card_to_num(capture_driver_name); - int playback_card = card_to_num(playback_driver_name); + int capture_card = card_to_num(capture_drivers.data[0]); + int playback_card = card_to_num(playback_drivers.data[0]); char audio_name[32]; if (capture_card >= 0) { snprintf(audio_name, sizeof(audio_name), "Audio%d", capture_card); if (!JackServerGlobals::on_device_acquire(audio_name)) { - jack_error("Audio device %s cannot be acquired...", capture_driver_name); + jack_error("Audio device %s cannot be acquired...", capture_drivers.data[0]); + array_string_free(&capture_drivers); + array_string_free(&playback_drivers); return -1; } } @@ -330,26 +354,32 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, if (playback_card >= 0 && playback_card != capture_card) { snprintf(audio_name, sizeof(audio_name), "Audio%d", playback_card); if (!JackServerGlobals::on_device_acquire(audio_name)) { - jack_error("Audio device %s cannot be acquired...", playback_driver_name); + jack_error("Audio device %s cannot be acquired...", playback_drivers.data[0]); if (capture_card >= 0) { snprintf(audio_name, sizeof(audio_name), "Audio%d", capture_card); JackServerGlobals::on_device_release(audio_name); } + array_string_free(&capture_drivers); + array_string_free(&playback_drivers); return -1; } } } #endif - fDriver = alsa_driver_new ((char*)"alsa_pcm", (char*)playback_driver_name, (char*)capture_driver_name, + fDriver = alsa_driver_new ((char*)"alsa_pcm", + capture_drivers.data, + playback_drivers.data, + capture_driver_name, + playback_driver_name, NULL, nframes, user_nperiods, samplerate, hw_monitoring, hw_metering, - capturing, - playing, + capturing ? capture_drivers.size : 0, + playing ? playback_drivers.size : 0, dither, soft_mode, monitor, @@ -359,6 +389,10 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, capture_latency, playback_latency, midi); + + array_string_free(&capture_drivers); + array_string_free(&playback_drivers); + if (fDriver) { // ALSA driver may have changed the in/out values fCaptureChannels = ((alsa_driver_t *)fDriver)->capture_nchannels; @@ -393,6 +427,7 @@ int JackAlsaDriver::Close() JackPosixThread::StopImp(fReservationLoopThread); } + // FIXME: needs adaptation for multiple drivers if (JackServerGlobals::on_device_release != NULL) { char audio_name[32]; @@ -471,12 +506,15 @@ int JackAlsaDriver::Write() return alsa_driver_write((alsa_driver_t *)fDriver, fEngineControl->fBufferSize); } -void JackAlsaDriver::ReadInputAux(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread) +void JackAlsaDriver::ReadInputAux(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread) { - for (int chn = 0; chn < fCaptureChannels; chn++) { - if (fGraphManager->GetConnectionsNum(fCapturePortList[chn]) > 0) { - jack_default_audio_sample_t* buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[chn], orig_nframes); - alsa_driver_read_from_channel((alsa_driver_t *)fDriver, chn, buf + nread, contiguous); + /* global channel offset to fCapturePortList of this capture alsa device */ + channel_t port_n = device->capture_channel_offset; + + for (channel_t chn = 0; chn < device->capture_nchannels; ++chn, ++port_n) { + if (fGraphManager->GetConnectionsNum(fCapturePortList[port_n]) > 0) { + jack_default_audio_sample_t* buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[port_n], orig_nframes); + alsa_driver_read_from_channel((alsa_driver_t *)fDriver, device, chn, buf + nread, contiguous); } } } @@ -510,16 +548,19 @@ int JackAlsaDriver::PortSetDefaultMetadata(jack_port_id_t port_id, const char* p return fEngine->PortSetDefaultMetadata(fClientControl.fRefNum, port_id, pretty_name); } -void JackAlsaDriver::WriteOutputAux(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten) +void JackAlsaDriver::WriteOutputAux(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten) { - for (int chn = 0; chn < fPlaybackChannels; chn++) { + /* global channel offset to fPlaybackPortList of this playback alsa device */ + channel_t port_n = device->playback_channel_offset; + + for (channel_t chn = 0; chn < device->playback_nchannels; ++chn, ++port_n) { // Output ports - if (fGraphManager->GetConnectionsNum(fPlaybackPortList[chn]) > 0) { - jack_default_audio_sample_t* buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[chn], orig_nframes); - alsa_driver_write_to_channel(((alsa_driver_t *)fDriver), chn, buf + nwritten, contiguous); + if (fGraphManager->GetConnectionsNum(fPlaybackPortList[port_n]) > 0) { + jack_default_audio_sample_t* buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[port_n], orig_nframes); + alsa_driver_write_to_channel(((alsa_driver_t *)fDriver), device, chn, buf + nwritten, contiguous); // Monitor ports - if (fWithMonitorPorts && fGraphManager->GetConnectionsNum(fMonitorPortList[chn]) > 0) { - jack_default_audio_sample_t* monbuf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fMonitorPortList[chn], orig_nframes); + if (fWithMonitorPorts && fGraphManager->GetConnectionsNum(fMonitorPortList[port_n]) > 0) { + jack_default_audio_sample_t* monbuf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fMonitorPortList[port_n], orig_nframes); memcpy(monbuf + nwritten, buf + nwritten, contiguous * sizeof(jack_default_audio_sample_t)); } } @@ -797,6 +838,63 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () return desc; } +struct array_string_t +{ + uint64_t size; + char **data; +}; + +void array_string_free(struct array_string_t *obj) +{ + if (obj == NULL) { + return; + } + if (obj->data == NULL) { + return; + } + for (size_t i = 0; i < obj->size; ++i) { + free(obj->data[i]); + } + free(obj->data); + obj->data = NULL; + obj->size = 0; +} + +struct array_string_t array_string_split(const char *str, const char sep) +{ + struct array_string_t result; + result.size = 0; + + std::stringstream stream; + stream << std::string(str); + if (stream.str().find(sep) == std::string::npos) { + result.data = (char**) calloc(1, sizeof(char*)); + result.data[0] = (char*) calloc(JACK_CLIENT_NAME_SIZE + 1, sizeof(char)); + result.size = 1; + strncpy(result.data[0], str, JACK_CLIENT_NAME_SIZE); + result.data[0][JACK_CLIENT_NAME_SIZE] = '\0'; + return result; + } + + std::string driver; + std::vector drivers; + while (std::getline(stream, driver, sep)) { + driver.erase(std::remove_if(driver.begin(), driver.end(), isspace), driver.end()); + if (std::find(drivers.begin(), drivers.end(), driver) != drivers.end()) + continue; + char *str = (char*) calloc(JACK_CLIENT_NAME_SIZE + 1, sizeof(char)); + strncpy(str, driver.c_str(), JACK_CLIENT_NAME_SIZE); + str[JACK_CLIENT_NAME_SIZE] = '\0'; + drivers.push_back(str); + } + + result.data = (char**) calloc(driver.size(), sizeof(char*)); + result.size = drivers.size(); + memcpy(result.data, drivers.data(), result.size * sizeof(char*)); + + return result; +} + static Jack::JackAlsaDriver* g_alsa_driver; SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) @@ -943,9 +1041,9 @@ SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLocke // Code to be used in alsa_driver.c -void ReadInput(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread) +void ReadInput(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread) { - g_alsa_driver->ReadInputAux(orig_nframes, contiguous, nread); + g_alsa_driver->ReadInputAux(device, orig_nframes, contiguous, nread); } void MonitorInput() { @@ -955,9 +1053,9 @@ void ClearOutput() { g_alsa_driver->ClearOutputAux(); } -void WriteOutput(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten) +void WriteOutput(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten) { - g_alsa_driver->WriteOutputAux(orig_nframes, contiguous, nwritten); + g_alsa_driver->WriteOutputAux(device, orig_nframes, contiguous, nwritten); } void SetTime(jack_time_t time) { diff --git a/linux/alsa/JackAlsaDriver.h b/linux/alsa/JackAlsaDriver.h index da055f673..f83cd2dd9 100644 --- a/linux/alsa/JackAlsaDriver.h +++ b/linux/alsa/JackAlsaDriver.h @@ -88,10 +88,10 @@ class JackAlsaDriver : public JackAudioDriver int SetBufferSize(jack_nframes_t buffer_size); - void ReadInputAux(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread); + void ReadInputAux(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread); void MonitorInputAux(); void ClearOutputAux(); - void WriteOutputAux(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten); + void WriteOutputAux(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten); void SetTimetAux(jack_time_t time); int PortSetDefaultMetadata(jack_port_id_t port_id, const char* pretty_name); diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index a8f5916dd..8c3e373b9 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -113,48 +113,57 @@ alsa_driver_prepare (snd_pcm_t *handle, int is_capture) } static void -alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) +alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver, alsa_device_t *device) { - bitset_destroy (&driver->channels_done); - bitset_destroy (&driver->channels_not_done); + bitset_destroy (&device->channels_done); + bitset_destroy (&device->channels_not_done); - if (driver->playback_addr) { - free (driver->playback_addr); - driver->playback_addr = 0; + device->capture_channel_offset = 0; + device->playback_channel_offset = 0; + + /* if we have only 1 device reuse user requested channels, otherwise 0 will attemp to allocate max channels on next setup pass */ + if (driver->devices_count != 1) { + driver->capture_nchannels = 0; + driver->playback_nchannels = 0; + device->capture_nchannels = 0; + device->playback_nchannels = 0; + } + + if (device->playback_addr) { + free (device->playback_addr); + device->playback_addr = 0; } - if (driver->capture_addr) { - free (driver->capture_addr); - driver->capture_addr = 0; + if (device->capture_addr) { + free (device->capture_addr); + device->capture_addr = 0; } #ifdef __QNXNTO__ - if (driver->playback_areas_ptr) { - free(driver->playback_areas_ptr); - driver->playback_areas = NULL; - driver->playback_areas_ptr = NULL; + if (device->playback_areas_ptr) { + free(device->playback_areas_ptr); + device->playback_areas_ptr = NULL; } - if (driver->capture_areas_ptr) { - free(driver->capture_areas_ptr); - driver->capture_areas = NULL; - driver->capture_areas_ptr = NULL; + if (device->capture_areas_ptr) { + free(device->capture_areas_ptr); + device->capture_areas_ptr = NULL; } #endif - if (driver->playback_interleave_skip) { - free (driver->playback_interleave_skip); - driver->playback_interleave_skip = NULL; + if (device->playback_interleave_skip) { + free (device->playback_interleave_skip); + device->playback_interleave_skip = NULL; } - if (driver->capture_interleave_skip) { - free (driver->capture_interleave_skip); - driver->capture_interleave_skip = NULL; + if (device->capture_interleave_skip) { + free (device->capture_interleave_skip); + device->capture_interleave_skip = NULL; } - if (driver->silent) { - free (driver->silent); - driver->silent = 0; + if (device->silent) { + free (device->silent); + device->silent = 0; } if (driver->dither_state) { @@ -166,7 +175,7 @@ alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) #ifndef __QNXNTO__ static int -alsa_driver_check_capabilities (alsa_driver_t *driver) +alsa_driver_check_capabilities (alsa_driver_t *driver, alsa_device_t *device) { return 0; } @@ -174,7 +183,7 @@ alsa_driver_check_capabilities (alsa_driver_t *driver) char* get_control_device_name(const char * device_name); static int -alsa_driver_check_card_type (alsa_driver_t *driver) +alsa_driver_check_card_type (alsa_driver_t *driver, alsa_device_t *device) { int err; snd_ctl_card_info_t *card_info; @@ -182,7 +191,7 @@ alsa_driver_check_card_type (alsa_driver_t *driver) snd_ctl_card_info_alloca (&card_info); - ctl_name = get_control_device_name(driver->alsa_name_playback); + ctl_name = get_control_device_name(device->playback_name); // XXX: I don't know the "right" way to do this. Which to use // driver->alsa_name_playback or driver->alsa_name_capture. @@ -191,35 +200,35 @@ alsa_driver_check_card_type (alsa_driver_t *driver) snd_strerror(err)); } else if ((err = snd_ctl_card_info(driver->ctl_handle, card_info)) < 0) { jack_error ("control hardware info \"%s\" (%s)", - driver->alsa_name_playback, snd_strerror (err)); + device->playback_name, snd_strerror (err)); snd_ctl_close (driver->ctl_handle); } - driver->alsa_driver = strdup(snd_ctl_card_info_get_driver (card_info)); + device->alsa_driver = strdup(snd_ctl_card_info_get_driver (card_info)); free(ctl_name); - return alsa_driver_check_capabilities (driver); + return alsa_driver_check_capabilities (driver, device); } static int -alsa_driver_hammerfall_hardware (alsa_driver_t *driver) +alsa_driver_hammerfall_hardware (alsa_driver_t *driver, alsa_device_t *device) { - driver->hw = jack_alsa_hammerfall_hw_new (driver); + device->hw = jack_alsa_hammerfall_hw_new (driver); return 0; } static int -alsa_driver_hdsp_hardware (alsa_driver_t *driver) +alsa_driver_hdsp_hardware (alsa_driver_t *driver, alsa_device_t *device) { - driver->hw = jack_alsa_hdsp_hw_new (driver); + device->hw = jack_alsa_hdsp_hw_new (driver); return 0; } static int -alsa_driver_ice1712_hardware (alsa_driver_t *driver) +alsa_driver_ice1712_hardware (alsa_driver_t *driver, alsa_device_t *device) { - driver->hw = jack_alsa_ice1712_hw_new (driver); + device->hw = jack_alsa_ice1712_hw_new (driver); return 0; } @@ -234,46 +243,46 @@ alsa_driver_usx2y_hardware (alsa_driver_t *driver) */ static int -alsa_driver_generic_hardware (alsa_driver_t *driver) +alsa_driver_generic_hardware (alsa_driver_t *driver, alsa_device_t *device) { - driver->hw = jack_alsa_generic_hw_new (driver); + device->hw = jack_alsa_generic_hw_new (driver); return 0; } static int -alsa_driver_hw_specific (alsa_driver_t *driver, int hw_monitoring, +alsa_driver_hw_specific (alsa_driver_t *driver, alsa_device_t *device, int hw_monitoring, int hw_metering) { int err; - if (!strcmp(driver->alsa_driver, "RME9652")) { - if ((err = alsa_driver_hammerfall_hardware (driver)) != 0) { + if (!strcmp(device->alsa_driver, "RME9652")) { + if ((err = alsa_driver_hammerfall_hardware (driver, device)) != 0) { return err; } - } else if (!strcmp(driver->alsa_driver, "H-DSP")) { - if ((err = alsa_driver_hdsp_hardware (driver)) !=0) { + } else if (!strcmp(device->alsa_driver, "H-DSP")) { + if ((err = alsa_driver_hdsp_hardware (driver, device)) !=0) { return err; } - } else if (!strcmp(driver->alsa_driver, "ICE1712")) { - if ((err = alsa_driver_ice1712_hardware (driver)) !=0) { + } else if (!strcmp(device->alsa_driver, "ICE1712")) { + if ((err = alsa_driver_ice1712_hardware (driver, device)) !=0) { return err; } } // JACK2 /* - else if (!strcmp(driver->alsa_driver, "USB US-X2Y")) { - if ((err = alsa_driver_usx2y_hardware (driver)) !=0) { + else if (!strcmp(device->alsa_driver, "USB US-X2Y")) { + if ((err = alsa_driver_usx2y_hardware (driver, device)) !=0) { return err; } } */ else { - if ((err = alsa_driver_generic_hardware (driver)) != 0) { + if ((err = alsa_driver_generic_hardware (driver, device)) != 0) { return err; } } - if (driver->hw->capabilities & Cap_HardwareMonitoring) { + if (device->hw->capabilities & Cap_HardwareMonitoring) { driver->has_hw_monitoring = TRUE; /* XXX need to ensure that this is really FALSE or * TRUE or whatever*/ @@ -283,13 +292,13 @@ alsa_driver_hw_specific (alsa_driver_t *driver, int hw_monitoring, driver->hw_monitoring = FALSE; } - if (driver->hw->capabilities & Cap_ClockLockReporting) { + if (device->hw->capabilities & Cap_ClockLockReporting) { driver->has_clock_sync_reporting = TRUE; } else { driver->has_clock_sync_reporting = FALSE; } - if (driver->hw->capabilities & Cap_HardwareMetering) { + if (device->hw->capabilities & Cap_HardwareMetering) { driver->has_hw_metering = TRUE; driver->hw_metering = hw_metering; } else { @@ -302,38 +311,38 @@ alsa_driver_hw_specific (alsa_driver_t *driver, int hw_monitoring, #endif static void -alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) +alsa_driver_setup_io_function_pointers (alsa_driver_t *driver, alsa_device_t *device) { - if (driver->playback_handle) { - if (SND_PCM_FORMAT_FLOAT_LE == driver->playback_sample_format) { - driver->write_via_copy = sample_move_dS_floatLE; + if (device->playback_handle) { + if (SND_PCM_FORMAT_FLOAT_LE == device->playback_sample_format) { + device->write_via_copy = sample_move_dS_floatLE; } else { - switch (driver->playback_sample_bytes) { + switch (device->playback_sample_bytes) { case 2: switch (driver->dither) { case Rectangular: jack_info("Rectangular dithering at 16 bits"); - driver->write_via_copy = driver->quirk_bswap? + device->write_via_copy = device->quirk_bswap? sample_move_dither_rect_d16_sSs: sample_move_dither_rect_d16_sS; break; case Triangular: jack_info("Triangular dithering at 16 bits"); - driver->write_via_copy = driver->quirk_bswap? + device->write_via_copy = device->quirk_bswap? sample_move_dither_tri_d16_sSs: sample_move_dither_tri_d16_sS; break; case Shaped: jack_info("Noise-shaped dithering at 16 bits"); - driver->write_via_copy = driver->quirk_bswap? + device->write_via_copy = device->quirk_bswap? sample_move_dither_shaped_d16_sSs: sample_move_dither_shaped_d16_sS; break; default: - driver->write_via_copy = driver->quirk_bswap? + device->write_via_copy = device->quirk_bswap? sample_move_d16_sSs : sample_move_d16_sS; break; @@ -341,43 +350,43 @@ alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) break; case 3: /* NO DITHER */ - driver->write_via_copy = driver->quirk_bswap? + device->write_via_copy = device->quirk_bswap? sample_move_d24_sSs: sample_move_d24_sS; break; case 4: /* NO DITHER */ - driver->write_via_copy = driver->quirk_bswap? + device->write_via_copy = device->quirk_bswap? sample_move_d32u24_sSs: sample_move_d32u24_sS; break; default: jack_error ("impossible sample width (%d) discovered!", - driver->playback_sample_bytes); + device->playback_sample_bytes); exit (1); } } } - if (driver->capture_handle) { - if (SND_PCM_FORMAT_FLOAT_LE == driver->capture_sample_format) { - driver->read_via_copy = sample_move_floatLE_sSs; + if (device->capture_handle) { + if (SND_PCM_FORMAT_FLOAT_LE == device->capture_sample_format) { + device->read_via_copy = sample_move_floatLE_sSs; } else { - switch (driver->capture_sample_bytes) { + switch (device->capture_sample_bytes) { case 2: - driver->read_via_copy = driver->quirk_bswap? + device->read_via_copy = device->quirk_bswap? sample_move_dS_s16s: sample_move_dS_s16; break; case 3: - driver->read_via_copy = driver->quirk_bswap? + device->read_via_copy = device->quirk_bswap? sample_move_dS_s24s: sample_move_dS_s24; break; case 4: - driver->read_via_copy = driver->quirk_bswap? + device->read_via_copy = device->quirk_bswap? sample_move_dS_s32u24s: sample_move_dS_s32u24; break; @@ -389,7 +398,7 @@ alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) #ifdef __QNXNTO__ static int -alsa_driver_allocate_buffer(alsa_driver_t *driver, int frames, int channels, bool is_capture) +alsa_driver_allocate_buffer(alsa_driver_t *driver, alsa_device_t *device, int frames, int channels, bool is_capture) { const long ALIGNMENT = 32; @@ -400,11 +409,11 @@ alsa_driver_allocate_buffer(alsa_driver_t *driver, int frames, int channels, boo char* const aligned_buffer = (char*)((uintptr_t)fBuffer & ~(ALIGNMENT-1)) + ALIGNMENT; if(is_capture) { - driver->capture_areas_ptr = fBuffer; - driver->capture_areas = aligned_buffer; + device->capture_areas_ptr = fBuffer; + device->capture_areas = aligned_buffer; } else { - driver->playback_areas_ptr = fBuffer; - driver->playback_areas = aligned_buffer; + device->playback_areas_ptr = fBuffer; + device->playback_areas = aligned_buffer; } return 0; @@ -415,7 +424,7 @@ alsa_driver_allocate_buffer(alsa_driver_t *driver, int frames, int channels, boo } static int -alsa_driver_get_setup (alsa_driver_t *driver, snd_pcm_channel_setup_t *setup, bool is_capture) +alsa_driver_get_setup (alsa_driver_t *driver, alsa_device_t *device, snd_pcm_channel_setup_t *setup, bool is_capture) { int err = 0; @@ -423,13 +432,13 @@ alsa_driver_get_setup (alsa_driver_t *driver, snd_pcm_channel_setup_t *setup, bo setup->channel = is_capture; if(is_capture) { - err = snd_pcm_plugin_setup(driver->capture_handle, setup); + err = snd_pcm_plugin_setup(device->capture_handle, setup); } else { - err = snd_pcm_plugin_setup(driver->playback_handle, setup); + err = snd_pcm_plugin_setup(device->playback_handle, setup); } if (err < 0) { jack_error("couldn't get channel setup for %s, err = %s ", - is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + is_capture ? device->capture_name : device->playback_name, strerror(err)); return -1; } @@ -438,7 +447,7 @@ alsa_driver_get_setup (alsa_driver_t *driver, snd_pcm_channel_setup_t *setup, bo } static int -alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, +alsa_driver_configure_stream (alsa_driver_t *driver, alsa_device_t *device, char *device_name, const char *stream_name, snd_pcm_t *handle, unsigned int *nperiodsp, @@ -449,8 +458,8 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, int err = 0; snd_pcm_channel_info_t ch_info; snd_pcm_channel_params_t ch_params; - const unsigned long sample_size = is_capture ? driver->capture_sample_bytes - : driver->playback_sample_bytes; + const unsigned long sample_size = is_capture ? device->capture_sample_bytes + : device->playback_sample_bytes; memset(&ch_info, 0, sizeof(ch_info)); /*A pointer to a snd_pcm_channel_info_t structure that snd_pcm_plugin_info() fills in with information about the PCM channel. @@ -496,11 +505,11 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, * (periods * period_size) because the silence prefill will fill the * complete buffer */ - return alsa_driver_allocate_buffer(driver, driver->frames_per_cycle * *nperiodsp, *nchns, is_capture); + return alsa_driver_allocate_buffer(driver, device, driver->frames_per_cycle * *nperiodsp, *nchns, is_capture); } #else static int -alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, +alsa_driver_configure_stream (alsa_driver_t *driver, alsa_device_t *device, char *device_name, const char *stream_name, snd_pcm_t *handle, snd_pcm_hw_params_t *hw_params, @@ -575,9 +584,9 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, } } else { if (formats[format].swapped) { - driver->quirk_bswap = 1; + device->quirk_bswap = 1; } else { - driver->quirk_bswap = 0; + device->quirk_bswap = 0; } jack_info ("ALSA: final selected sample format for %s: %s", stream_name, formats[format].Name); break; @@ -724,7 +733,7 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, } #endif - if (handle == driver->playback_handle) + if (handle == device->playback_handle) err = snd_pcm_sw_params_set_avail_min ( handle, sw_params, driver->frames_per_cycle @@ -794,27 +803,28 @@ alsa_driver_check_format (snd_pcm_format_t format) } static void -alsa_driver_set_sample_bytes (alsa_driver_t *driver) +alsa_driver_set_sample_bytes (alsa_driver_t *driver, alsa_device_t *device) { #ifdef __QNXNTO__ - driver->playback_sample_bytes = - snd_pcm_format_width (driver->playback_sample_format) + device->playback_sample_bytes = + snd_pcm_format_width (device->playback_sample_format) / 8; - driver->capture_sample_bytes = - snd_pcm_format_width (driver->capture_sample_format) + device->capture_sample_bytes = + snd_pcm_format_width (device->capture_sample_format) / 8; #else - driver->playback_sample_bytes = - snd_pcm_format_physical_width (driver->playback_sample_format) + device->playback_sample_bytes = + snd_pcm_format_physical_width (device->playback_sample_format) / 8; - driver->capture_sample_bytes = - snd_pcm_format_physical_width (driver->capture_sample_format) + device->capture_sample_bytes = + snd_pcm_format_physical_width (device->capture_sample_format) / 8; #endif } static int alsa_driver_set_parameters (alsa_driver_t *driver, + alsa_device_t *device, jack_nframes_t frames_per_cycle, jack_nframes_t user_nperiods, jack_nframes_t rate) @@ -838,32 +848,35 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->frames_per_cycle = frames_per_cycle; driver->user_nperiods = user_nperiods; - jack_info ("configuring for %" PRIu32 "Hz, period = %" + jack_info ("configuring C: '%s' P: '%s' %" PRIu32 "Hz, period = %" PRIu32 " frames (%.1f ms), buffer = %" PRIu32 " periods", + device->capture_name != NULL ? device->capture_name : "-", device->playback_name != NULL ? device->playback_name : "-", rate, frames_per_cycle, (((float)frames_per_cycle / (float) rate) * 1000.0f), user_nperiods); - if (driver->capture_handle) { + if (device->capture_handle) { #ifdef __QNXNTO__ err = alsa_driver_configure_stream ( driver, - driver->alsa_name_capture, + device, + device->capture_name, "capture", - driver->capture_handle, + device->capture_handle, &driver->capture_nperiods, - &driver->capture_nchannels, - driver->capture_sample_bytes, + &device->capture_nchannels, + device->capture_sample_bytes, SND_PCM_CHANNEL_CAPTURE); #else err = alsa_driver_configure_stream ( driver, - driver->alsa_name_capture, + device, + device->capture_name, "capture", - driver->capture_handle, + device->capture_handle, driver->capture_hw_params, driver->capture_sw_params, &driver->capture_nperiods, - &driver->capture_nchannels, - driver->capture_sample_bytes); + &device->capture_nchannels, + device->capture_sample_bytes); #endif if (err) { jack_error ("ALSA: cannot configure capture channel"); @@ -871,28 +884,30 @@ alsa_driver_set_parameters (alsa_driver_t *driver, } } - if (driver->playback_handle) { + if (device->playback_handle) { #ifdef __QNXNTO__ err = alsa_driver_configure_stream ( driver, - driver->alsa_name_playback, + device, + device->playback_name, "playback", - driver->playback_handle, + device->playback_handle, &driver->playback_nperiods, - &driver->playback_nchannels, - driver->playback_sample_bytes, + &device->playback_nchannels, + device->playback_sample_bytes, SND_PCM_CHANNEL_PLAYBACK); #else err = alsa_driver_configure_stream ( driver, - driver->alsa_name_playback, + device, + device->playback_name, "playback", - driver->playback_handle, + device->playback_handle, driver->playback_hw_params, driver->playback_sw_params, &driver->playback_nperiods, - &driver->playback_nchannels, - driver->playback_sample_bytes); + &device->playback_nchannels, + device->playback_sample_bytes); #endif if (err) { jack_error ("ALSA: cannot configure playback channel"); @@ -901,46 +916,46 @@ alsa_driver_set_parameters (alsa_driver_t *driver, } #ifdef __QNXNTO__ - if (driver->capture_handle) { - err = alsa_driver_get_setup(driver, &c_setup, SND_PCM_CHANNEL_CAPTURE); + if (device->capture_handle) { + err = alsa_driver_get_setup(driver, device, &c_setup, SND_PCM_CHANNEL_CAPTURE); if(err < 0) { return -1; } cr = c_setup.format.rate; - c_period_size = c_setup.buf.block.frag_size / driver->capture_nchannels - / driver->capture_sample_bytes; + c_period_size = c_setup.buf.block.frag_size / device->capture_nchannels + / device->capture_sample_bytes; c_periods = c_setup.buf.block.frags; - driver->capture_sample_format = c_setup.format.format; - driver->capture_interleaved = c_setup.format.interleave; + device->capture_sample_format = c_setup.format.format; + device->capture_interleaved = c_setup.format.interleave; } - if (driver->playback_handle) { - err = alsa_driver_get_setup(driver, &p_setup, SND_PCM_CHANNEL_PLAYBACK); + if (device->playback_handle) { + err = alsa_driver_get_setup(driver, device, &p_setup, SND_PCM_CHANNEL_PLAYBACK); if(err < 0) { return -1; } pr = p_setup.format.rate; - p_period_size = p_setup.buf.block.frag_size / driver->playback_nchannels - / driver->playback_sample_bytes; + p_period_size = p_setup.buf.block.frag_size / device->playback_nchannels + / device->playback_sample_bytes; p_periods = p_setup.buf.block.frags; - driver->playback_sample_format = p_setup.format.format; - driver->playback_interleaved = p_setup.format.interleave; + device->playback_sample_format = p_setup.format.format; + device->playback_interleaved = p_setup.format.interleave; } #else /* check the rate, since that's rather important */ - if (driver->playback_handle) { + if (device->playback_handle) { snd_pcm_hw_params_get_rate (driver->playback_hw_params, &pr, &dir); } - if (driver->capture_handle) { + if (device->capture_handle) { snd_pcm_hw_params_get_rate (driver->capture_hw_params, - &cr, &dir); + &cr, &dir); } #endif - if (driver->capture_handle && driver->playback_handle) { + if (device->capture_handle && device->playback_handle) { if (cr != pr) { jack_error ("playback and capture sample rates do " "not match (%d vs. %d)", pr, cr); @@ -959,13 +974,13 @@ alsa_driver_set_parameters (alsa_driver_t *driver, } } - else if (driver->capture_handle && cr != driver->frame_rate) { + else if (device->capture_handle && cr != driver->frame_rate) { jack_error ("capture sample rate in use (%d Hz) does not " "match requested rate (%d Hz)", cr, driver->frame_rate); driver->frame_rate = cr; } - else if (driver->playback_handle && pr != driver->frame_rate) { + else if (device->playback_handle && pr != driver->frame_rate) { jack_error ("playback sample rate in use (%d Hz) does not " "match requested rate (%d Hz)", pr, driver->frame_rate); @@ -975,7 +990,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, /* check the fragment size, since that's non-negotiable */ - if (driver->playback_handle) { + if (device->playback_handle) { #ifndef __QNXNTO__ snd_pcm_access_t access; @@ -983,10 +998,10 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->playback_hw_params, &p_period_size, &dir); err = snd_pcm_hw_params_get_format ( driver->playback_hw_params, - &(driver->playback_sample_format)); + &(device->playback_sample_format)); err = snd_pcm_hw_params_get_access (driver->playback_hw_params, &access); - driver->playback_interleaved = + device->playback_interleaved = (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) || (access == SND_PCM_ACCESS_MMAP_COMPLEX); #endif @@ -1010,7 +1025,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, #endif } - if (driver->capture_handle) { + if (device->capture_handle) { #ifndef __QNXNTO__ snd_pcm_access_t access; @@ -1018,10 +1033,10 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->capture_hw_params, &c_period_size, &dir); err = snd_pcm_hw_params_get_format ( driver->capture_hw_params, - &(driver->capture_sample_format)); + &(device->capture_sample_format)); err = snd_pcm_hw_params_get_access (driver->capture_hw_params, &access); - driver->capture_interleaved = + device->capture_interleaved = (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) || (access == SND_PCM_ACCESS_MMAP_COMPLEX); #endif @@ -1048,10 +1063,10 @@ alsa_driver_set_parameters (alsa_driver_t *driver, #endif } - alsa_driver_set_sample_bytes(driver); + alsa_driver_set_sample_bytes(driver, device); - if (driver->playback_handle) { - err = alsa_driver_check_format(driver->playback_sample_format); + if (device->playback_handle) { + err = alsa_driver_check_format(device->playback_sample_format); if(err < 0) { jack_error ("programming error: unhandled format " "type for playback"); @@ -1059,8 +1074,8 @@ alsa_driver_set_parameters (alsa_driver_t *driver, } } - if (driver->capture_handle) { - err = alsa_driver_check_format(driver->capture_sample_format); + if (device->capture_handle) { + err = alsa_driver_check_format(device->capture_sample_format); if(err < 0) { jack_error ("programming error: unhandled format " "type for capture"); @@ -1068,51 +1083,58 @@ alsa_driver_set_parameters (alsa_driver_t *driver, } } - if (driver->playback_interleaved) { + if (device->playback_interleaved) { #ifndef __QNXNTO__ const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames; - if (snd_pcm_mmap_begin(driver->playback_handle, + if (snd_pcm_mmap_begin(device->playback_handle, &my_areas, &offset, &frames) < 0) { jack_error ("ALSA: %s: mmap areas info error", - driver->alsa_name_playback); + device->playback_name); return -1; } // TODO does not work for capture only - driver->interleave_unit = + device->interleave_unit = snd_pcm_format_physical_width ( - driver->playback_sample_format) / 8; + device->playback_sample_format) / 8; #else - driver->interleave_unit = snd_pcm_format_width( - driver->playback_sample_format) / 8; + device->interleave_unit = snd_pcm_format_width( + device->playback_sample_format) / 8; #endif } else { - driver->interleave_unit = 0; /* NOT USED */ + device->interleave_unit = 0; /* NOT USED */ } #ifndef __QNXNTO__ - if (driver->capture_interleaved) { + if (device->capture_interleaved) { const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames; - if (snd_pcm_mmap_begin(driver->capture_handle, + if (snd_pcm_mmap_begin(device->capture_handle, &my_areas, &offset, &frames) < 0) { jack_error ("ALSA: %s: mmap areas info error", - driver->alsa_name_capture); + device->capture_name); return -1; } } #endif - if (driver->playback_nchannels > driver->capture_nchannels) { - driver->max_nchannels = driver->playback_nchannels; - driver->user_nchannels = driver->capture_nchannels; + if (device->playback_nchannels > device->capture_nchannels) { + device->max_nchannels = device->playback_nchannels; + device->user_nchannels = device->capture_nchannels; } else { - driver->max_nchannels = driver->capture_nchannels; - driver->user_nchannels = driver->playback_nchannels; + device->max_nchannels = device->capture_nchannels; + device->user_nchannels = device->playback_nchannels; } - alsa_driver_setup_io_function_pointers (driver); + /* device local channel offset to offsets in driver, used by Jack2 */ + device->capture_channel_offset = driver->capture_nchannels; + device->playback_channel_offset = driver->playback_nchannels; + + driver->capture_nchannels += device->capture_nchannels; + driver->playback_nchannels += device->playback_nchannels; + + alsa_driver_setup_io_function_pointers (driver, device); /* Allocate and initialize structures that rely on the channels counts. @@ -1123,49 +1145,44 @@ alsa_driver_set_parameters (alsa_driver_t *driver, that potentially need to be silenced. */ - bitset_create (&driver->channels_done, driver->max_nchannels); - bitset_create (&driver->channels_not_done, driver->max_nchannels); - - if (driver->playback_handle) { - driver->playback_addr = (char **) - malloc (sizeof (char *) * driver->playback_nchannels); - memset (driver->playback_addr, 0, - sizeof (char *) * driver->playback_nchannels); - driver->playback_interleave_skip = (unsigned long *) - malloc (sizeof (unsigned long *) * driver->playback_nchannels); - memset (driver->playback_interleave_skip, 0, - sizeof (unsigned long *) * driver->playback_nchannels); - driver->silent = (unsigned long *) + bitset_create (&device->channels_done, device->max_nchannels); + bitset_create (&device->channels_not_done, device->max_nchannels); + + if (device->playback_handle) { + device->playback_addr = (char **) + malloc (sizeof (char *) * device->playback_nchannels); + memset (device->playback_addr, 0, + sizeof (char *) * device->playback_nchannels); + device->playback_interleave_skip = (unsigned long *) + malloc (sizeof (unsigned long *) * device->playback_nchannels); + memset (device->playback_interleave_skip, 0, + sizeof (unsigned long *) * device->playback_nchannels); + device->silent = (unsigned long *) malloc (sizeof (unsigned long) - * driver->playback_nchannels); + * device->playback_nchannels); - for (chn = 0; chn < driver->playback_nchannels; chn++) { - driver->silent[chn] = 0; + for (chn = 0; chn < device->playback_nchannels; chn++) { + device->silent[chn] = 0; } - for (chn = 0; chn < driver->playback_nchannels; chn++) { - bitset_add (driver->channels_done, chn); + for (chn = 0; chn < device->playback_nchannels; chn++) { + bitset_add (device->channels_done, chn); } - driver->dither_state = (dither_state_t *) - calloc ( driver->playback_nchannels, - sizeof (dither_state_t)); + driver->dither_state = (dither_state_t *) calloc (device->playback_nchannels, sizeof (dither_state_t)); } - if (driver->capture_handle) { - driver->capture_addr = (char **) - malloc (sizeof (char *) * driver->capture_nchannels); - memset (driver->capture_addr, 0, - sizeof (char *) * driver->capture_nchannels); - driver->capture_interleave_skip = (unsigned long *) - malloc (sizeof (unsigned long *) * driver->capture_nchannels); - memset (driver->capture_interleave_skip, 0, - sizeof (unsigned long *) * driver->capture_nchannels); + if (device->capture_handle) { + device->capture_addr = (char **) + malloc (sizeof (char *) * device->capture_nchannels); + memset (device->capture_addr, 0, + sizeof (char *) * device->capture_nchannels); + device->capture_interleave_skip = (unsigned long *) + malloc (sizeof (unsigned long *) * device->capture_nchannels); + memset (device->capture_interleave_skip, 0, + sizeof (unsigned long *) * device->capture_nchannels); } - driver->clock_sync_data = (ClockSyncStatus *) - malloc (sizeof (ClockSyncStatus) * driver->max_nchannels); - driver->period_usecs = (jack_time_t) floor ((((float) driver->frames_per_cycle) / driver->frame_rate) * 1000000.0f); @@ -1194,11 +1211,18 @@ alsa_driver_reset_parameters (alsa_driver_t *driver, jack_nframes_t user_nperiods, jack_nframes_t rate) { + int err = 0; + /* XXX unregister old ports ? */ - alsa_driver_release_channel_dependent_memory (driver); - return alsa_driver_set_parameters (driver, - frames_per_cycle, - user_nperiods, rate); + for (int i = 0; i < driver->devices_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + alsa_driver_release_channel_dependent_memory (driver, device); + if ((err = alsa_driver_set_parameters (driver, device, frames_per_cycle, user_nperiods, rate)) != 0) { + return err; + } + } + + return err; } #ifdef __QNXNTO__ @@ -1220,6 +1244,7 @@ snd_pcm_poll_descriptors_revents(snd_pcm_t *pcm, struct pollfd *pfds, static int alsa_driver_get_channel_addresses (alsa_driver_t *driver, + alsa_device_t *device, snd_pcm_uframes_t *capture_avail, snd_pcm_uframes_t *playback_avail, snd_pcm_uframes_t *capture_offset, @@ -1231,32 +1256,32 @@ alsa_driver_get_channel_addresses (alsa_driver_t *driver, #ifndef __QNXNTO__ int err; if ((err = snd_pcm_mmap_begin ( - driver->capture_handle, &driver->capture_areas, + device->capture_handle, &device->capture_areas, (snd_pcm_uframes_t *) capture_offset, (snd_pcm_uframes_t *) capture_avail)) < 0) { jack_error ("ALSA: %s: mmap areas info error", - driver->alsa_name_capture); + device->capture_name); return -1; } - for (chn = 0; chn < driver->capture_nchannels; chn++) { + for (chn = 0; chn < device->capture_nchannels; chn++) { const snd_pcm_channel_area_t *a = - &driver->capture_areas[chn]; - driver->capture_addr[chn] = (char *) a->addr + &device->capture_areas[chn]; + device->capture_addr[chn] = (char *) a->addr + ((a->first + a->step * *capture_offset) / 8); - driver->capture_interleave_skip[chn] = (unsigned long ) (a->step / 8); + device->capture_interleave_skip[chn] = (unsigned long ) (a->step / 8); } #else - for (chn = 0; chn < driver->capture_nchannels; chn++) { - char* const a = driver->capture_areas; - if (driver->capture_interleaved) { - driver->capture_addr[chn] = &a[chn * driver->capture_sample_bytes]; - driver->capture_interleave_skip[chn] = driver->capture_nchannels * - driver->capture_sample_bytes; + for (chn = 0; chn < device->capture_nchannels; chn++) { + char* const a = device->capture_areas; + if (device->capture_interleaved) { + device->capture_addr[chn] = &a[chn * device->capture_sample_bytes]; + device->capture_interleave_skip[chn] = device->capture_nchannels * + device->capture_sample_bytes; } else { - driver->capture_addr[chn] = &a[chn * - driver->capture_sample_bytes * driver->frames_per_cycle]; - driver->capture_interleave_skip[chn] = driver->capture_sample_bytes; + device->capture_addr[chn] = &a[chn * + device->capture_sample_bytes * driver->frames_per_cycle]; + device->capture_interleave_skip[chn] = device->capture_sample_bytes; } } #endif @@ -1266,32 +1291,32 @@ alsa_driver_get_channel_addresses (alsa_driver_t *driver, #ifndef __QNXNTO__ int err; if ((err = snd_pcm_mmap_begin ( - driver->playback_handle, &driver->playback_areas, + device->playback_handle, &device->playback_areas, (snd_pcm_uframes_t *) playback_offset, (snd_pcm_uframes_t *) playback_avail)) < 0) { jack_error ("ALSA: %s: mmap areas info error ", - driver->alsa_name_playback); + device->playback_name); return -1; } - for (chn = 0; chn < driver->playback_nchannels; chn++) { + for (chn = 0; chn < device->playback_nchannels; chn++) { const snd_pcm_channel_area_t *a = - &driver->playback_areas[chn]; - driver->playback_addr[chn] = (char *) a->addr + &device->playback_areas[chn]; + device->playback_addr[chn] = (char *) a->addr + ((a->first + a->step * *playback_offset) / 8); - driver->playback_interleave_skip[chn] = (unsigned long ) (a->step / 8); + device->playback_interleave_skip[chn] = (unsigned long ) (a->step / 8); } #else - for (chn = 0; chn < driver->playback_nchannels; chn++) { - char* const a = driver->playback_areas; - if (driver->playback_interleaved) { - driver->playback_addr[chn] = &a[chn * driver->playback_sample_bytes]; - driver->playback_interleave_skip[chn] = driver->playback_nchannels * - driver->playback_sample_bytes; + for (chn = 0; chn < device->playback_nchannels; chn++) { + char* const a = device->playback_areas; + if (device->playback_interleaved) { + device->playback_addr[chn] = &a[chn * device->playback_sample_bytes]; + device->playback_interleave_skip[chn] = device->playback_nchannels * + device->playback_sample_bytes; } else { - driver->playback_addr[chn] = &a[chn * - driver->playback_sample_bytes * driver->frames_per_cycle]; - driver->playback_interleave_skip[chn] = driver->playback_sample_bytes; + device->playback_addr[chn] = &a[chn * + device->playback_sample_bytes * driver->frames_per_cycle]; + device->playback_interleave_skip[chn] = device->playback_sample_bytes; } } #endif @@ -1324,51 +1349,56 @@ alsa_driver_start (alsa_driver_t *driver) driver->poll_last = 0; driver->poll_next = 0; - if (driver->playback_handle) { - if ((err = alsa_driver_prepare (driver->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { - jack_error ("ALSA: prepare error for playback on " - "\"%s\" (%s)", driver->alsa_name_playback, - snd_strerror(err)); - return -1; - } - } + for (int i = 0; i < driver->devices_c_count; ++i) { + alsa_device_t *device = &driver->devices[i]; - if ((driver->capture_handle && driver->capture_and_playback_not_synced) - || !driver->playback_handle) { - if ((err = alsa_driver_prepare (driver->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { - jack_error ("ALSA: prepare error for capture on \"%s\"" - " (%s)", driver->alsa_name_capture, - snd_strerror(err)); - return -1; + if (device->capture_handle && !device->capture_linked) { + jack_log("prepare device %s", device->capture_name); + if ((err = alsa_driver_prepare (device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { + jack_error ("ALSA: failed to prepare device '%s' (%s)", device->capture_name, snd_strerror(err)); + return -1; + } } } - if (driver->hw_monitoring) { - if (driver->input_monitor_mask || driver->all_monitor_in) { - if (driver->all_monitor_in) { - driver->hw->set_input_monitor_mask (driver->hw, ~0U); - } else { - driver->hw->set_input_monitor_mask ( - driver->hw, driver->input_monitor_mask); + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + if (device->playback_handle && !device->playback_linked) { + jack_log("prepare device %s", device->playback_name); + if ((err = alsa_driver_prepare (device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { + jack_error ("ALSA: failed to prepare device '%s' (%s)", device->playback_name, snd_strerror(err)); + return -1; } - } else { - driver->hw->set_input_monitor_mask (driver->hw, - driver->input_monitor_mask); } } - if (driver->playback_handle) { - driver->playback_nfds = - snd_pcm_poll_descriptors_count (driver->playback_handle); - } else { - driver->playback_nfds = 0; - } + // TODO amiartus +// if (driver->hw_monitoring) { +// if (driver->input_monitor_mask || driver->all_monitor_in) { +// if (driver->all_monitor_in) { +// driver->hw->set_input_monitor_mask (driver->hw, ~0U); +// } else { +// driver->hw->set_input_monitor_mask ( +// driver->hw, driver->input_monitor_mask); +// } +// } else { +// driver->hw->set_input_monitor_mask (driver->hw, +// driver->input_monitor_mask); +// } +// } - if (driver->capture_handle) { - driver->capture_nfds = - snd_pcm_poll_descriptors_count (driver->capture_handle); - } else { - driver->capture_nfds = 0; + driver->capture_nfds = 0; + driver->playback_nfds = 0; + + for (int i = 0; i < driver->devices_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (device->capture_handle) { + driver->capture_nfds += snd_pcm_poll_descriptors_count (device->capture_handle); + } + if (device->playback_handle) { + driver->playback_nfds += snd_pcm_poll_descriptors_count (device->playback_handle); + } } if (driver->pfd) { @@ -1382,7 +1412,12 @@ alsa_driver_start (alsa_driver_t *driver) if (driver->midi && !driver->xrun_recovery) (driver->midi->start)(driver->midi); - if (driver->playback_handle) { + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (!device->playback_handle) { + continue; + } + const jack_nframes_t silence_frames = driver->frames_per_cycle * driver->playback_nperiods; /* fill playback buffer with zeroes, and mark @@ -1390,15 +1425,14 @@ alsa_driver_start (alsa_driver_t *driver) */ #ifndef __QNXNTO__ - pavail = snd_pcm_avail_update (driver->playback_handle); - + pavail = snd_pcm_avail_update (device->playback_handle); if (pavail != silence_frames) { jack_error ("ALSA: full buffer not available at start"); return -1; } #endif - if (alsa_driver_get_channel_addresses (driver, + if (alsa_driver_get_channel_addresses (driver, device, 0, &pavail, 0, &poffset)) { jack_error("silence failed, get channel addresses"); return -1; @@ -1413,37 +1447,46 @@ alsa_driver_start (alsa_driver_t *driver) buffer. */ - for (chn = 0; chn < driver->playback_nchannels; chn++) { + for (chn = 0; chn < device->playback_nchannels; chn++) { alsa_driver_silence_on_channel ( - driver, chn, silence_frames); + driver, device, chn, silence_frames); } #ifdef __QNXNTO__ - const size_t bytes = silence_frames * driver->playback_nchannels * - driver->playback_sample_bytes; - if ((err = snd_pcm_plugin_write(driver->playback_handle, - driver->playback_areas, bytes)) < bytes) { + const size_t bytes = silence_frames * device->playback_nchannels * + device->playback_sample_bytes; + if ((err = snd_pcm_plugin_write(device->playback_handle, + device->playback_areas, bytes)) < bytes) { jack_error ("ALSA: could not complete write of %" PRIu32 " frames: error = %d", silence_frames, err); return -1; } #else - snd_pcm_mmap_commit (driver->playback_handle, poffset, silence_frames); + snd_pcm_mmap_commit (device->playback_handle, poffset, silence_frames); #endif + } - if ((err = alsa_driver_stream_start (driver->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { - jack_error ("ALSA: could not start playback (%s)", - snd_strerror (err)); - return -1; + for (int i = 0; i < driver->devices_c_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (device->capture_handle && !device->capture_linked) { + jack_log("start device %s", device->capture_name); + if ((err = alsa_driver_stream_start (device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { + jack_error ("ALSA: failed to start device C: '%s' (%s)", device->capture_name, + snd_strerror(err)); + return -1; + } } } - if ((driver->capture_handle && driver->capture_and_playback_not_synced) - || !driver->playback_handle) { - if ((err = alsa_driver_stream_start (driver->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { - jack_error ("ALSA: could not start capture (%s)", - snd_strerror (err)); - return -1; + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (device->playback_handle && !device->playback_linked) { + jack_log("start device %s", device->playback_name); + if ((err = alsa_driver_stream_start (device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { + jack_error ("ALSA: failed to start device P: '%s' (%s)", device->playback_name, + snd_strerror(err)); + return -1; + } } } @@ -1479,44 +1522,45 @@ alsa_driver_stop (alsa_driver_t *driver) // JACK2 ClearOutput(); - if (driver->playback_handle) { + for (int i = 0; i < driver->devices_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + if (device->playback_handle && !device->playback_linked) { #ifdef __QNXNTO__ - /* In case of playback: Drain discards the frames */ - err = snd_pcm_plugin_playback_drain(driver->playback_handle); + /* In case of playback: Drain discards the frames */ + err = snd_pcm_plugin_playback_drain(device->playback_handle); #else - err = snd_pcm_drop (driver->playback_handle); + jack_info("flush device %s", device->playback_name); + err = snd_pcm_drop (device->playback_handle); #endif - if (err < 0) { - jack_error ("ALSA: channel flush for playback " - "failed (%s)", snd_strerror (err)); - return -1; + if (err < 0) { + jack_error ("ALSA: failed to flush device (%s)", snd_strerror (err)); + return -1; + } } - } - if (!driver->playback_handle - || driver->capture_and_playback_not_synced) { - if (driver->capture_handle) { + if (device->capture_handle && !device->capture_linked) { #ifdef __QNXNTO__ /* In case of capture: Flush discards the frames */ - err = snd_pcm_plugin_flush(driver->capture_handle, SND_PCM_CHANNEL_CAPTURE); + err = snd_pcm_plugin_flush(device->capture_handle, SND_PCM_CHANNEL_CAPTURE); #else - err = snd_pcm_drop (driver->capture_handle); + jack_info("flush device %s", device->capture_name); + err = snd_pcm_drop (device->capture_handle); #endif if (err < 0) { - jack_error ("ALSA: channel flush for " - "capture failed (%s)", - snd_strerror (err)); + jack_error ("ALSA: failed to flush device (%s)", snd_strerror (err)); return -1; } } } - if (driver->hw_monitoring) { - driver->hw->set_input_monitor_mask (driver->hw, 0); - } +// TODO: amiartus +// if (driver->hw_monitoring) { +// driver->hw->set_input_monitor_mask (driver->hw, 0); +// } - if (driver->midi && !driver->xrun_recovery) - (driver->midi->stop)(driver->midi); +// if (driver->midi && !driver->xrun_recovery) +// (driver->midi->stop)(driver->midi); return 0; } @@ -1542,85 +1586,74 @@ alsa_driver_restart (alsa_driver_t *driver) } static int -alsa_driver_get_status (alsa_driver_t *driver) +alsa_driver_get_state (snd_pcm_t *handle, int is_capture) { - snd_pcm_t *pcm_handle; - #ifdef __QNXNTO__ int res; snd_pcm_channel_status_t status; -#endif - - if (driver->capture_handle) { - pcm_handle = driver->capture_handle; - } else { - pcm_handle = driver->playback_handle; - } -#ifdef __QNXNTO__ memset (&status, 0, sizeof (status)); - status.channel = driver->capture_handle ? SND_PCM_CHANNEL_CAPTURE : - SND_PCM_CHANNEL_PLAYBACK; - res = snd_pcm_plugin_status(pcm_handle, &status); + status.channel = is_capture; + res = snd_pcm_plugin_status(handle, &status); if (res < 0) { jack_error("status error: %s", snd_strerror(res)); return -1; } return status.status; #else - return snd_pcm_state(pcm_handle); + return snd_pcm_state(handle); #endif } static int alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs) { - int status; - int res; - - status = alsa_driver_get_status(driver); - if (status == SND_PCM_STATE_SUSPENDED) { - if (driver->capture_handle) { - if ((res = alsa_driver_prepare(driver->capture_handle, SND_PCM_STREAM_CAPTURE)) - < 0) { - jack_error("error preparing after suspend: %s", snd_strerror(res)); - } - } - if (driver->playback_handle) { - if ((res = alsa_driver_prepare(driver->playback_handle, SND_PCM_STREAM_PLAYBACK)) - < 0) { - jack_error("error preparing after suspend: %s", snd_strerror(res)); + int state; + + for (int i = 0; i < driver->devices_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (device->capture_handle) { + state = alsa_driver_get_state(device->capture_handle, SND_PCM_STREAM_CAPTURE); + // TODO overrun + if (state == SND_PCM_STATE_XRUN) { + driver->xrun_count++; +#ifdef __QNXNTO__ + /* Timestamp api's are not available as per QNX Documentation */ + *delayed_usecs = 0; +#else + snd_pcm_status_t *status; + snd_pcm_status_alloca(&status); + snd_pcm_status(device->capture_handle, status); + struct timeval now, diff, tstamp; + snd_pcm_status_get_tstamp(status,&now); + snd_pcm_status_get_trigger_tstamp(status, &tstamp); + timersub(&now, &tstamp, &diff); + *delayed_usecs = diff.tv_sec * 1000000.0 + diff.tv_usec; +#endif + jack_log("**** alsa_pcm: xrun of at least %.3f msecs",*delayed_usecs / 1000.0); } } - } - // TODO overrun - if (status == SND_PCM_STATE_XRUN - && driver->process_count > XRUN_REPORT_DELAY) { - driver->xrun_count++; + if (device->playback_handle) { + state = alsa_driver_get_state(device->playback_handle, SND_PCM_STREAM_PLAYBACK); + + // TODO overrun + if (state == SND_PCM_STATE_XRUN) { + driver->xrun_count++; #ifdef __QNXNTO__ - /* Timestamp api's are not available as per QNX Documentation */ - *delayed_usecs = 0; + /* Timestamp api's are not available as per QNX Documentation */ + *delayed_usecs = 0; #else - struct timeval now, diff, tstamp; - snd_pcm_status_get_tstamp(status,&now); - snd_pcm_status_get_trigger_tstamp(status, &tstamp); - timersub(&now, &tstamp, &diff); - *delayed_usecs = diff.tv_sec * 1000000.0 + diff.tv_usec; + snd_pcm_status_t *status; + snd_pcm_status_alloca(&status); + snd_pcm_status(device->playback_handle, status); + struct timeval now, diff, tstamp; + snd_pcm_status_get_tstamp(status,&now); + snd_pcm_status_get_trigger_tstamp(status, &tstamp); + timersub(&now, &tstamp, &diff); + *delayed_usecs = diff.tv_sec * 1000000.0 + diff.tv_usec; #endif - jack_log("**** alsa_pcm: xrun of at least %.3f msecs",*delayed_usecs / 1000.0); - if (driver->capture_handle) { - jack_log("Repreparing capture"); - if ((res = alsa_driver_prepare(driver->capture_handle, - SND_PCM_STREAM_CAPTURE)) < 0) { - jack_error("error preparing after xrun: %s", snd_strerror(res)); - } - } - if (driver->playback_handle) { - jack_log("Repreparing playback"); - if ((res = alsa_driver_prepare(driver->playback_handle, - SND_PCM_STREAM_PLAYBACK)) < 0) { - jack_error("error preparing after xrun: %s", snd_strerror(res)); + jack_log("**** alsa_pcm: xrun of at least %.3f msecs",*delayed_usecs / 1000.0); } } } @@ -1633,19 +1666,19 @@ alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs) } static void -alsa_driver_silence_untouched_channels (alsa_driver_t *driver, +alsa_driver_silence_untouched_channels (alsa_driver_t *driver, alsa_device_t *device, jack_nframes_t nframes) { channel_t chn; jack_nframes_t buffer_frames = driver->frames_per_cycle * driver->playback_nperiods; - for (chn = 0; chn < driver->playback_nchannels; chn++) { - if (bitset_contains (driver->channels_not_done, chn)) { - if (driver->silent[chn] < buffer_frames) { + for (chn = 0; chn < device->playback_nchannels; chn++) { + if (bitset_contains (device->channels_not_done, chn)) { + if (device->silent[chn] < buffer_frames) { alsa_driver_silence_on_channel_no_mark ( - driver, chn, nframes); - driver->silent[chn] += nframes; + driver, device, chn, nframes); + device->silent[chn] += nframes; } } } @@ -1659,7 +1692,7 @@ alsa_driver_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int s pfds->events = POLLHUP|POLLNVAL; pfds->events |= (is_capture == SND_PCM_STREAM_PLAYBACK) ? POLLOUT : POLLIN; - return 0; + return snd_pcm_poll_descriptors_count(pcm); } static snd_pcm_sframes_t @@ -1691,62 +1724,90 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float snd_pcm_sframes_t avail = 0; snd_pcm_sframes_t capture_avail = 0; snd_pcm_sframes_t playback_avail = 0; - int xrun_detected = FALSE; - int need_capture; - int need_playback; int retry_cnt = 0; - unsigned int i; jack_time_t poll_enter; jack_time_t poll_ret = 0; *status = -1; *delayed_usecs = 0; - need_capture = driver->capture_handle ? 1 : 0; + int cap_revents[driver->devices_c_count]; + memset(cap_revents, 0, sizeof(cap_revents)); + int play_revents[driver->devices_p_count]; + memset(play_revents, 0, sizeof(play_revents)); - if (extra_fd >= 0) { - need_playback = 0; - } else { - need_playback = driver->playback_handle ? 1 : 0; - } - - again: - - while ((need_playback || need_capture) && !xrun_detected) { + int pfd_cap_count[driver->devices_c_count]; + int pfd_play_count[driver->devices_p_count]; + /* In case if extra_fd is positive number then should be added to pfd_count + * since at present extra_fd is always negative this is not changed now. + */ + int pfd_count = driver->capture_nfds + driver->playback_nfds; + do { int poll_result; - unsigned int ci = 0; - unsigned int nfds; - unsigned short revents; - - nfds = 0; + int pfd_index = 0; - if (need_playback) { - alsa_driver_poll_descriptors (driver->playback_handle, - &driver->pfd[0], - driver->playback_nfds, SND_PCM_STREAM_PLAYBACK); - nfds += driver->playback_nfds; - } - - if (need_capture) { - alsa_driver_poll_descriptors (driver->capture_handle, - &driver->pfd[nfds], - driver->capture_nfds, SND_PCM_STREAM_CAPTURE); - ci = nfds; - nfds += driver->capture_nfds; + /* collect capture poll descriptors */ + for (int i = 0; i < driver->devices_c_count; ++i) { + /* this device already triggered poll event before */ + if (cap_revents[i]) { + continue; + } + alsa_device_t *device = &driver->devices[i]; + pfd_cap_count[i] = alsa_driver_poll_descriptors (device->capture_handle, + &driver->pfd[pfd_index], + pfd_count - pfd_index, + SND_PCM_STREAM_CAPTURE); + if (pfd_cap_count[i] < 0) { + jack_log ("alsa_driver_poll_descriptors failed pfd_cap_count[%d]=%d", i ,pfd_cap_count[i] ); + /* In case of xrun -EPIPE is returned perform xrun recovery*/ + if (pfd_cap_count[i] == -EPIPE) { + goto xrun; + } + /* for any other error return negative wait status to caller */ + *status = ALSA_DRIVER_WAIT_ERROR; + return 0; + } else { + pfd_index += pfd_cap_count[i]; + } } - /* ALSA doesn't set POLLERR in some versions of 0.9.X */ - - for (i = 0; i < nfds; i++) { - driver->pfd[i].events |= POLLERR; + /* collect playback poll descriptors */ + for (int i = 0; i < driver->devices_p_count; ++i) { + /* this device already triggered poll event before */ + if (play_revents[i]) { + continue; + } + alsa_device_t *device = &driver->devices[i]; + pfd_play_count[i] = alsa_driver_poll_descriptors (device->playback_handle, + &driver->pfd[pfd_index], + pfd_count - pfd_index, + SND_PCM_STREAM_PLAYBACK); + if (pfd_play_count[i] < 0) { + jack_log ("alsa_driver_poll_descriptors failed pfd_play_count[%d]=%d", i ,pfd_play_count[i] ); + /* In case of xrun -EPIPE is returned perform xrun recovery*/ + if (pfd_cap_count[i] == -EPIPE) { + goto xrun; + } + /* for any other error return negative wait status to caller */ + *status = ALSA_DRIVER_WAIT_ERROR; + return 0; + } else { + pfd_index += pfd_play_count[i]; + } } if (extra_fd >= 0) { - driver->pfd[nfds].fd = extra_fd; - driver->pfd[nfds].events = + driver->pfd[pfd_index].fd = extra_fd; + driver->pfd[pfd_index].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; - nfds++; + pfd_index++; + } + + /* nothing to poll on */ + if (pfd_index == 0 && extra_fd < 0) { + *status = -1; + return 0; } poll_enter = jack_get_microseconds (); @@ -1764,7 +1825,7 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float #ifdef __ANDROID__ poll_result = poll (driver->pfd, nfds, -1); //fix for sleep issue #else - poll_result = poll (driver->pfd, nfds, driver->poll_timeout_ms); + poll_result = poll (driver->pfd,(unsigned int) pfd_count, driver->poll_timeout_ms); #endif if (poll_result < 0) { @@ -1774,7 +1835,7 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float // under gdb, or when exiting due to a signal if (under_gdb) { jack_info(poll_log); - goto again; + continue; } jack_error(poll_log); *status = -2; @@ -1803,10 +1864,7 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float " usecs, Retrying with a recovery, retry cnt = %d", poll_ret - poll_enter, retry_cnt); *status = alsa_driver_xrun_recovery (driver, delayed_usecs); - if(*status != 0) { - jack_error ("ALSA: poll time out, recovery failed with status = %d", *status); - return 0; - } + return 0; } // JACK2 @@ -1818,128 +1876,133 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float } driver->poll_last = poll_ret; driver->poll_next = poll_ret + driver->period_usecs; -// JACK2 -/* - driver->engine->transport_cycle_start (driver->engine, - poll_ret); -*/ - } - -#ifdef DEBUG_WAKEUP - fprintf (stderr, "%" PRIu64 ": checked %d fds, started at %" PRIu64 " %" PRIu64 " usecs since poll entered\n", - poll_ret, nfds, poll_enter, poll_ret - poll_enter); -#endif - - /* check to see if it was the extra FD that caused us - * to return from poll */ - - if (extra_fd >= 0) { - - if (driver->pfd[nfds-1].revents == 0) { + } else { + /* check to see if it was the extra FD that caused us + * to return from poll */ + if (driver->pfd[pfd_index-1].revents == 0) { /* we timed out on the extra fd */ - *status = -4; return -1; } - /* if POLLIN was the only bit set, we're OK */ - *status = 0; - return (driver->pfd[nfds-1].revents == POLLIN) ? 0 : -1; + return (driver->pfd[pfd_index-1].revents == POLLIN) ? 0 : -1; } - if (need_playback) { - if (snd_pcm_poll_descriptors_revents - (driver->playback_handle, &driver->pfd[0], - driver->playback_nfds, &revents) < 0) { - jack_error ("ALSA: playback revents failed"); - *status = -6; - return 0; - } - - if (revents & POLLNVAL) { - jack_error ("ALSA: playback device disconnected"); - *status = -7; - return 0; - } - - if (revents & POLLERR) { - xrun_detected = TRUE; - } - - if (revents & POLLOUT) { - need_playback = 0; #ifdef DEBUG_WAKEUP - fprintf (stderr, "%" PRIu64 - " playback stream ready\n", - poll_ret); + fprintf (stderr, "%" PRIu64 ": checked %d fds, started at %" PRIu64 " %" PRIu64 " usecs since poll entered\n", + poll_ret, desc_count, poll_enter, poll_ret - poll_enter); #endif + + pfd_index = 0; + + for (int i = 0; i < driver->devices_c_count; ++i) { + /* this device already triggered poll event before */ + if (cap_revents[i]) { + continue; } - } + alsa_device_t *device = &driver->devices[i]; - if (need_capture) { - if (snd_pcm_poll_descriptors_revents - (driver->capture_handle, &driver->pfd[ci], - driver->capture_nfds, &revents) < 0) { + unsigned short collect_revs = 0; + if (snd_pcm_poll_descriptors_revents (device->capture_handle, &driver->pfd[pfd_index], + pfd_cap_count[i], &collect_revs) != 0) { jack_error ("ALSA: capture revents failed"); *status = -6; return 0; } - - if (revents & POLLNVAL) { - jack_error ("ALSA: capture device disconnected"); - *status = -7; - return 0; + pfd_index += pfd_cap_count[i]; + if (collect_revs & (POLLERR | POLLIN)) { + if (collect_revs & POLLERR) { + /* optimization, no point in polling more if we already have xrun on one device */ + jack_error ("ALSA: device reported xrun C: '%s'", device->capture_name); + *status = alsa_driver_xrun_recovery (driver, delayed_usecs); + return 0; + } + if (collect_revs & POLLIN) { + } + /* on next poll round skip fds from this device */ + cap_revents[i] = collect_revs; + pfd_count -= pfd_cap_count[i]; } + } - if (revents & POLLERR) { - xrun_detected = TRUE; + for (int i = 0; i < driver->devices_p_count; ++i) { + /* this device already triggered poll event before */ + if (play_revents[i]) { + continue; } + alsa_device_t *device = &driver->devices[i]; - if (revents & POLLIN) { - need_capture = 0; -#ifdef DEBUG_WAKEUP - fprintf (stderr, "%" PRIu64 - " capture stream ready\n", - poll_ret); -#endif + unsigned short collect_revs = 0; + if (snd_pcm_poll_descriptors_revents (device->playback_handle, &driver->pfd[pfd_index], + pfd_play_count[i], &collect_revs) != 0) { + jack_error ("ALSA: playback revents failed"); + *status = -6; + return 0; + } + pfd_index += pfd_play_count[i]; + if (collect_revs & (POLLERR | POLLOUT)) { + if (collect_revs & POLLERR) { + /* optimization, no point in polling more if we already have xrun on one device */ + jack_error ("ALSA: device reported xrun P: '%s'", device->playback_name); + *status = alsa_driver_xrun_recovery (driver, delayed_usecs); + return 0; + } + if (collect_revs & POLLNVAL) { + jack_error ("ALSA: playback device disconnected"); + *status = -7; + return 0; + } + if (collect_revs & POLLOUT) { + } + /* on next poll round skip fds from this device */ + play_revents[i] = collect_revs; + pfd_count -= pfd_play_count[i]; } } - } + } while (pfd_count > 0); + + /* TODO: amiartus; I assume all devices are snd_pcm_link-ed and running on the same clock source, + * therefore should have the same avail frames, however in practice, this might have to be reworked, + * since we should check carefully for avail frames on each device, make sure it matches and handle corner cases + */ - if (driver->capture_handle) { - if ((capture_avail = alsa_driver_avail (driver, - driver->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { - if (capture_avail == -EPIPE) { - xrun_detected = TRUE; + capture_avail = INT_MAX; + + for (int i = 0; i < driver->devices_c_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + snd_pcm_sframes_t avail = 0; + + if ((avail = alsa_driver_avail (driver, device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { + if (avail == -EPIPE) { + jack_error ("ALSA: avail_update xrun on capture dev '%s'", device->capture_name); + *status = alsa_driver_xrun_recovery (driver, delayed_usecs); + return 0; } else { - jack_error ("unknown ALSA avail_update return" - " value (%u)", capture_avail); + jack_error ("unknown ALSA avail_update return value (%u)", capture_avail); } } - } else { - /* odd, but see min() computation below */ - capture_avail = INT_MAX; + jack_log("ALSA: avail frames C: %s %ld", device->capture_name, avail); + capture_avail = capture_avail < avail ? capture_avail : avail; } - if (driver->playback_handle) { - if ((playback_avail = alsa_driver_avail (driver, - driver->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { - if (playback_avail == -EPIPE) { - xrun_detected = TRUE; + playback_avail = INT_MAX; + + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + snd_pcm_sframes_t avail = 0; + + if ((avail = alsa_driver_avail (driver, device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { + if (avail == -EPIPE) { + jack_error ("ALSA: avail_update xrun on playback dev '%s'", device->playback_name); + *status = alsa_driver_xrun_recovery (driver, delayed_usecs); + return 0; } else { - jack_error ("unknown ALSA avail_update return" - " value (%u)", playback_avail); + jack_error ("unknown ALSA avail_update return value (%u)", playback_avail); } } - } else { - /* odd, but see min() computation below */ - playback_avail = INT_MAX; - } - - if (xrun_detected) { - *status = alsa_driver_xrun_recovery (driver, delayed_usecs); - return 0; + jack_log("ALSA: avail frames P: %s %ld", device->playback_name, avail); + playback_avail = playback_avail < avail ? playback_avail : avail; } *status = 0; @@ -1954,8 +2017,10 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float #endif /* mark all channels not done for now. read/write will change this */ - - bitset_copy (driver->channels_not_done, driver->channels_done); + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + bitset_copy (device->channels_not_done, device->channels_done); + } /* constrain the available count to the nearest (round down) number of periods. @@ -1964,61 +2029,46 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float return avail - (avail % driver->frames_per_cycle); } - int alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) { snd_pcm_sframes_t contiguous; snd_pcm_sframes_t nread; snd_pcm_uframes_t offset; - jack_nframes_t orig_nframes; -// jack_default_audio_sample_t* buf; -// channel_t chn; -// JSList *node; -// jack_port_t* port; int err; if (nframes > driver->frames_per_cycle) { return -1; } -// JACK2 -/* - if (driver->engine->freewheeling) { - return 0; - } -*/ - if (driver->midi) - (driver->midi->read)(driver->midi, nframes); - - if (!driver->capture_handle) { - return 0; - } + for (size_t i = 0; i < driver->devices_c_count; ++i) { + alsa_device_t *device = &driver->devices[i]; - nread = 0; - contiguous = 0; - orig_nframes = nframes; + nread = 0; + contiguous = 0; + jack_nframes_t frames_remain = nframes; - while (nframes) { + while (frames_remain) { - contiguous = nframes; + contiguous = frames_remain; - if (alsa_driver_get_channel_addresses ( - driver, - (snd_pcm_uframes_t *) &contiguous, - (snd_pcm_uframes_t *) 0, - &offset, 0) < 0) { - return -1; - } + if (alsa_driver_get_channel_addresses ( + driver, + device, + (snd_pcm_uframes_t *) &contiguous, + (snd_pcm_uframes_t *) 0, + &offset, 0) < 0) { + return -1; + } #ifdef __QNXNTO__ - const size_t bytes = contiguous * driver->capture_nchannels * driver->capture_sample_bytes; - if ((err = snd_pcm_plugin_read(driver->capture_handle, - driver->capture_areas, bytes)) < bytes) { - jack_error ("ALSA: could not complete read of %" - PRIu32 " frames: error = %d", contiguous, err); - return -1; - } + const size_t bytes = contiguous * device->capture_nchannels * device->capture_sample_bytes; + if ((err = snd_pcm_plugin_read(device->capture_handle, + device->capture_areas, bytes)) < bytes) { + jack_error ("ALSA: could not complete read of %" + PRIu32 " frames: error = %d", contiguous, err); + return -1; + } #endif // JACK2 @@ -2037,19 +2087,20 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) buf + nread, contiguous); } */ - ReadInput(orig_nframes, contiguous, nread); + ReadInput(device, nframes, contiguous, nread); #ifndef __QNXNTO__ - if ((err = snd_pcm_mmap_commit (driver->capture_handle, - offset, contiguous)) < 0) { - jack_error ("ALSA: could not complete read of %" - PRIu32 " frames: error = %d", contiguous, err); - return -1; - } + if ((err = snd_pcm_mmap_commit (device->capture_handle, + offset, contiguous)) < 0) { + jack_error ("ALSA: could not complete read of %" + PRIu32 " frames: error = %d", contiguous, err); + return -1; + } #endif - nframes -= contiguous; - nread += contiguous; + frames_remain -= contiguous; + nread += contiguous; + } } return 0; @@ -2058,77 +2109,54 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) int alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) { -// channel_t chn; -// JSList *node; -// JSList *mon_node; -// jack_default_audio_sample_t* buf; -// jack_default_audio_sample_t* monbuf; - jack_nframes_t orig_nframes; - snd_pcm_sframes_t nwritten; snd_pcm_sframes_t contiguous; + snd_pcm_sframes_t nwritten; snd_pcm_uframes_t offset; -// jack_port_t *port; int err; driver->process_count++; -// JACK2 -/* - if (!driver->playback_handle || driver->engine->freewheeling) { - return 0; - } -*/ - if (!driver->playback_handle) { - return 0; - } - if (nframes > driver->frames_per_cycle) { return -1; } - if (driver->midi) - (driver->midi->write)(driver->midi, nframes); - - nwritten = 0; - contiguous = 0; - orig_nframes = nframes; - - /* check current input monitor request status */ + for (size_t i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; - driver->input_monitor_mask = 0; -// JACK2 -/* - for (chn = 0, node = driver->capture_ports; node; - node = jack_slist_next (node), chn++) { - if (((jack_port_t *) node->data)->shared->monitor_requests) { - driver->input_monitor_mask |= (1<midi) + (driver->midi->write)(driver->midi, nframes); - if (driver->hw_monitoring) { - if ((driver->hw->input_monitor_mask - != driver->input_monitor_mask) - && !driver->all_monitor_in) { - driver->hw->set_input_monitor_mask ( - driver->hw, driver->input_monitor_mask); - } - } + nwritten = 0; + contiguous = 0; + jack_nframes_t frames_remain = nframes; - while (nframes) { + /* check current input monitor request status */ + driver->input_monitor_mask = 0; - contiguous = nframes; + MonitorInput(); - if (alsa_driver_get_channel_addresses ( - driver, - (snd_pcm_uframes_t *) 0, - (snd_pcm_uframes_t *) &contiguous, - 0, &offset) < 0) { - return -1; + if (driver->hw_monitoring) { + if ((device->hw->input_monitor_mask + != driver->input_monitor_mask) + && !driver->all_monitor_in) { + device->hw->set_input_monitor_mask ( + device->hw, driver->input_monitor_mask); + } } + while (frames_remain) { + + contiguous = frames_remain; + + if (alsa_driver_get_channel_addresses ( + driver, + device, + (snd_pcm_uframes_t *) 0, + (snd_pcm_uframes_t *) &contiguous, + 0, &offset) < 0) { + return -1; + } // JACK2 /* for (chn = 0, node = driver->playback_ports, mon_node=driver->monitor_ports; @@ -2156,34 +2184,34 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) } */ - // JACK2 - WriteOutput(orig_nframes, contiguous, nwritten); + // JACK2 + WriteOutput(device, nframes, contiguous, nwritten); - if (!bitset_empty (driver->channels_not_done)) { - alsa_driver_silence_untouched_channels (driver, - contiguous); - } + if (!bitset_empty (device->channels_not_done)) { + alsa_driver_silence_untouched_channels (driver, device, contiguous); + } #ifdef __QNXNTO__ - const size_t bytes = contiguous * driver->playback_nchannels * driver->playback_sample_bytes; - if ((err = snd_pcm_plugin_write(driver->playback_handle, - driver->playback_areas, bytes)) < bytes) { - jack_error ("ALSA: could not complete write of %" - PRIu32 " frames: error = %d", contiguous, err); - return -1; - } -#else - if ((err = snd_pcm_mmap_commit (driver->playback_handle, - offset, contiguous)) < 0) { - jack_error ("ALSA: could not complete playback of %" - PRIu32 " frames: error = %d", contiguous, err); - if (err != -EPIPE && err != -ESTRPIPE) + const size_t bytes = contiguous * device->playback_nchannels * device->playback_sample_bytes; + if ((err = snd_pcm_plugin_write(device->playback_handle, + device->playback_areas, bytes)) < bytes) { + jack_error ("ALSA: could not complete write of %" + PRIu32 " frames: error = %d", contiguous, err); return -1; - } + } +#else + if ((err = snd_pcm_mmap_commit (device->playback_handle, + offset, contiguous)) < 0) { + jack_error ("ALSA: could not complete playback of %" + PRIu32 " frames: error = %d", contiguous, err); + if (err != -EPIPE && err != -ESTRPIPE) + return -1; + } #endif - nframes -= contiguous; - nwritten += contiguous; + frames_remain -= contiguous; + nwritten += contiguous; + } } return 0; @@ -2256,14 +2284,27 @@ alsa_driver_delete (alsa_driver_t *driver) driver->ctl_handle = 0; } - if (driver->capture_handle) { - snd_pcm_close (driver->capture_handle); - driver->capture_handle = 0; - } + for (int i = 0; i < driver->devices_count; ++i) { + if (driver->devices[i].capture_handle) { + snd_pcm_close (driver->devices[i].capture_handle); + driver->devices[i].capture_handle = 0; + } + + if (driver->devices[i].playback_handle) { + snd_pcm_close (driver->devices[i].playback_handle); + driver->devices[i].playback_handle = 0; + } - if (driver->playback_handle) { - snd_pcm_close (driver->playback_handle); - driver->capture_handle = 0; + free(driver->devices[i].capture_name); + free(driver->devices[i].playback_name); + free(driver->devices[i].alsa_driver); + + alsa_driver_release_channel_dependent_memory (driver, &driver->devices[i]); + + if (driver->devices[i].hw) { + driver->devices[i].hw->release (driver->devices[i].hw); + driver->devices[i].hw = 0; + } } #ifndef __QNXNTO__ @@ -2291,16 +2332,6 @@ alsa_driver_delete (alsa_driver_t *driver) if (driver->pfd) { free (driver->pfd); } - - if (driver->hw) { - driver->hw->release (driver->hw); - driver->hw = 0; - } - free(driver->alsa_name_playback); - free(driver->alsa_name_capture); - free(driver->alsa_driver); - - alsa_driver_release_channel_dependent_memory (driver); //JACK2 //jack_driver_nt_finish ((jack_driver_nt_t *) driver); free (driver); @@ -2408,30 +2439,30 @@ discover_alsa_using_apps () } static int -alsa_driver_open (alsa_driver_t *driver, bool is_capture) +alsa_driver_open (alsa_driver_t *driver, alsa_device_t *device, bool is_capture) { int err = 0; char* current_apps; if(is_capture) { #ifdef __QNXNTO__ - err = snd_pcm_open_name (&driver->capture_handle, - driver->alsa_name_capture, + err = snd_pcm_open_name (&device->capture_handle, + device->capture_name, SND_PCM_OPEN_CAPTURE | SND_PCM_OPEN_NONBLOCK); #else - err = snd_pcm_open (&driver->capture_handle, - driver->alsa_name_capture, + err = snd_pcm_open (&device->capture_handle, + device->capture_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); #endif } else { #ifdef __QNXNTO__ - err = snd_pcm_open_name (&driver->playback_handle, - driver->alsa_name_playback, + err = snd_pcm_open_name (&device->playback_handle, + device->playback_name, SND_PCM_OPEN_PLAYBACK | SND_PCM_OPEN_NONBLOCK); #else - err = snd_pcm_open (&driver->playback_handle, - driver->alsa_name_playback, + err = snd_pcm_open (&device->playback_handle, + device->playback_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); #endif @@ -2444,7 +2475,7 @@ alsa_driver_open (alsa_driver_t *driver, bool is_capture) "already in use. Please stop the" " application using it and " "run JACK again", - is_capture ? driver->alsa_name_capture : driver->alsa_name_playback); + is_capture ? device->alsa_name_capture : device->alsa_name_playback); #else current_apps = discover_alsa_using_apps (); if (current_apps) { @@ -2453,7 +2484,7 @@ alsa_driver_open (alsa_driver_t *driver, bool is_capture) " are using your soundcard(s) so you should " " check them and stop them as necessary before " " trying to start JACK again:\n\n%s", - is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + is_capture ? device->capture_name : device->playback_name, current_apps); free (current_apps); } else { @@ -2461,7 +2492,7 @@ alsa_driver_open (alsa_driver_t *driver, bool is_capture) "already in use. Please stop the" " application using it and " "run JACK again", - is_capture ? driver->alsa_name_capture : driver->alsa_name_playback); + is_capture ? device->capture_name : device->playback_name); } #endif break; @@ -2469,53 +2500,52 @@ alsa_driver_open (alsa_driver_t *driver, bool is_capture) case EPERM: jack_error ("you do not have permission to open " "the audio device \"%s\" for playback", - is_capture ? driver->alsa_name_capture : driver->alsa_name_playback); + is_capture ? device->capture_name : device->playback_name); break; case EINVAL: jack_error ("the state of handle or the mode is invalid " "or invalid state change occured \"%s\" for %s", - is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + is_capture ? device->capture_name : device->playback_name, is_capture ? "capture" : "playback"); break; case ENOENT: jack_error ("device \"%s\" does not exist for %s", - is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + is_capture ? device->capture_name : device->playback_name, is_capture ? "capture" : "playback"); break; case ENOMEM: jack_error ("Not enough memory available for allocation for \"%s\" for %s", - is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + is_capture ? device->capture_name : device->playback_name, is_capture ? "capture" : "playback"); break; case SND_ERROR_INCOMPATIBLE_VERSION: jack_error ("Version mismatch \"%s\" for %s", - is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + is_capture ? device->capture_name : device->playback_name, is_capture ? "capture" : "playback"); break; } - alsa_driver_delete (driver); if(is_capture) { - driver->capture_handle = NULL; + device->capture_handle = NULL; } else { - driver->playback_handle = NULL; + device->playback_handle = NULL; } } - if (is_capture && driver->capture_handle) { + if (is_capture && device->capture_handle) { #ifdef __QNXNTO__ - snd_pcm_nonblock_mode (driver->capture_handle, 0); + snd_pcm_nonblock_mode (device->capture_handle, 0); #else - snd_pcm_nonblock (driver->capture_handle, 0); + snd_pcm_nonblock (device->capture_handle, 0); #endif - } else if(!is_capture && driver->playback_handle) { + } else if(!is_capture && device->playback_handle) { #ifdef __QNXNTO__ - snd_pcm_nonblock_mode (driver->playback_handle, 0); + snd_pcm_nonblock_mode (device->playback_handle, 0); #else - snd_pcm_nonblock (driver->playback_handle, 0); + snd_pcm_nonblock (device->playback_handle, 0); #endif } @@ -2523,16 +2553,18 @@ alsa_driver_open (alsa_driver_t *driver, bool is_capture) } jack_driver_t * -alsa_driver_new (char *name, char *playback_alsa_device, - char *capture_alsa_device, +alsa_driver_new (char *name, char **capture_alsa_devices, + char **playback_alsa_devices, + const char *capture_names, + const char *playback_names, jack_client_t *client, jack_nframes_t frames_per_cycle, jack_nframes_t user_nperiods, jack_nframes_t rate, int hw_monitoring, int hw_metering, - int capturing, - int playing, + int capturing_count, + int playing_count, DitherAlgorithm dither, int soft_mode, int monitor, @@ -2547,10 +2579,12 @@ alsa_driver_new (char *name, char *playback_alsa_device, int err; alsa_driver_t *driver; - jack_info ("creating alsa driver ... %s|%s|%" PRIu32 "|%" PRIu32 + jack_info ("creating alsa driver ... %s|%" PRIu32 "|%s|%" PRIu32 "|%" PRIu32 "|%" PRIu32 "|%" PRIu32"|%" PRIu32"|%" PRIu32 "|%s|%s|%s|%s", - playing ? playback_alsa_device : "-", - capturing ? capture_alsa_device : "-", + capturing_count > 0 ? capture_names : "-", + capturing_count, + playing_count > 0 ? playback_names : "-", + playing_count, frames_per_cycle, user_nperiods, rate, user_capture_nchnls,user_playback_nchnls, hw_monitoring ? "hwmon": "nomon", @@ -2575,43 +2609,16 @@ alsa_driver_new (char *name, char *playback_alsa_device, driver->nt_run_cycle = (JackDriverNTRunCycleFunction) alsa_driver_run_cycle; */ - driver->playback_handle = NULL; - driver->capture_handle = NULL; driver->ctl_handle = 0; - driver->hw = 0; - driver->capture_and_playback_not_synced = FALSE; - driver->max_nchannels = 0; - driver->user_nchannels = 0; - driver->playback_nchannels = user_playback_nchnls; - driver->capture_nchannels = user_capture_nchnls; - driver->playback_sample_bytes = (shorts_first ? 2:4); - driver->capture_sample_bytes = (shorts_first ? 2:4); driver->capture_frame_latency = capture_latency; driver->playback_frame_latency = playback_latency; - driver->playback_addr = 0; - driver->capture_addr = 0; - driver->playback_interleave_skip = NULL; - driver->capture_interleave_skip = NULL; -#ifdef __QNXNTO__ - driver->playback_areas = NULL; - driver->playback_areas_ptr = NULL; - driver->capture_areas = NULL; - driver->capture_areas_ptr = NULL; -#endif - - - driver->silent = 0; driver->all_monitor_in = FALSE; driver->with_monitor_ports = monitor; driver->clock_mode = ClockMaster; /* XXX is it? */ driver->input_monitor_mask = 0; /* XXX is it? */ - driver->capture_ports = 0; - driver->playback_ports = 0; - driver->monitor_ports = 0; - driver->pfd = 0; driver->playback_nfds = 0; driver->capture_nfds = 0; @@ -2619,8 +2626,6 @@ alsa_driver_new (char *name, char *playback_alsa_device, driver->dither = dither; driver->soft_mode = soft_mode; - driver->quirk_bswap = 0; - pthread_mutex_init (&driver->clock_sync_lock, 0); driver->clock_sync_listeners = 0; @@ -2628,73 +2633,51 @@ alsa_driver_new (char *name, char *playback_alsa_device, driver->xrun_count = 0; driver->process_count = 0; - driver->alsa_name_playback = strdup (playback_alsa_device); - driver->alsa_name_capture = strdup (capture_alsa_device); - driver->midi = midi_driver; driver->xrun_recovery = 0; -#ifndef __QNXNTO__ - if (alsa_driver_check_card_type (driver)) { - alsa_driver_delete (driver); - return NULL; + driver->devices_c_count = capturing_count; + driver->devices_p_count = playing_count; + driver->devices_count = capturing_count > playing_count ? capturing_count : playing_count; + driver->devices = (alsa_device_t*) calloc(driver->devices_count, sizeof(*driver->devices)); + + if (driver->devices_count == 1) { + driver->devices[0].capture_nchannels = user_capture_nchnls; + driver->devices[0].playback_nchannels = user_playback_nchnls; } - alsa_driver_hw_specific (driver, hw_monitoring, hw_metering); -#endif + for (int i = 0; i < driver->devices_c_count; ++i) { + driver->devices[i].capture_sample_bytes = (shorts_first ? 2:4); + driver->devices[i].capture_name = strdup(capture_alsa_devices[i]); - if (playing) { - err = alsa_driver_open(driver, SND_PCM_STREAM_PLAYBACK); - if(err < 0) { - jack_error ("\n\nATTENTION: Opening of the playback device \"%s\" failed.", - playback_alsa_device); - return NULL; - } - } - if(capturing) { - err = alsa_driver_open(driver, SND_PCM_STREAM_CAPTURE); - if(err < 0) { + err = alsa_driver_open(driver, &driver->devices[i], SND_PCM_STREAM_CAPTURE); + if (err < 0) { jack_error ("\n\nATTENTION: Opening of the capture device \"%s\" failed.", - capture_alsa_device); + driver->devices[i].capture_name); + alsa_driver_delete (driver); return NULL; } } - if (driver->playback_handle == NULL) { - if (playing) { - - /* they asked for playback, but we can't do it */ + for (int i = 0; i < driver->devices_p_count; ++i) { + driver->devices[i].playback_sample_bytes = (shorts_first ? 2:4); + driver->devices[i].playback_name = strdup(playback_alsa_devices[i]); - jack_error ("ALSA: Cannot open PCM device %s for " - "playback. Falling back to capture-only" - " mode", name); - - if (driver->capture_handle == NULL) { - /* can't do anything */ - alsa_driver_delete (driver); - return NULL; - } - - playing = FALSE; +#ifndef __QNXNTO__ + if (alsa_driver_check_card_type (driver, &driver->devices[i])) { + alsa_driver_delete (driver); + return NULL; } - } - - if (driver->capture_handle == NULL) { - if (capturing) { - - /* they asked for capture, but we can't do it */ - jack_error ("ALSA: Cannot open PCM device %s for " - "capture. Falling back to playback-only" - " mode", name); - - if (driver->playback_handle == NULL) { - /* can't do anything */ - alsa_driver_delete (driver); - return NULL; - } + alsa_driver_hw_specific (driver, &driver->devices[i], hw_monitoring, hw_metering); +#endif - capturing = FALSE; + err = alsa_driver_open(driver, &driver->devices[i], SND_PCM_STREAM_PLAYBACK); + if (err < 0) { + jack_error ("\n\nATTENTION: Opening of the playback device \"%s\" failed.", + driver->devices[i].playback_name); + alsa_driver_delete (driver); + return NULL; } } @@ -2704,7 +2687,7 @@ alsa_driver_new (char *name, char *playback_alsa_device, driver->playback_sw_params = 0; driver->capture_sw_params = 0; - if (driver->playback_handle) { + if (driver->devices[0].playback_handle) { if ((err = snd_pcm_hw_params_malloc ( &driver->playback_hw_params)) < 0) { jack_error ("ALSA: could not allocate playback hw" @@ -2722,7 +2705,7 @@ alsa_driver_new (char *name, char *playback_alsa_device, } } - if (driver->capture_handle) { + if (driver->devices[0].capture_handle) { if ((err = snd_pcm_hw_params_malloc ( &driver->capture_hw_params)) < 0) { jack_error ("ALSA: could not allocate capture hw" @@ -2740,19 +2723,50 @@ alsa_driver_new (char *name, char *playback_alsa_device, } } #endif - if (alsa_driver_set_parameters (driver, frames_per_cycle, - user_nperiods, rate)) { - alsa_driver_delete (driver); - return NULL; + for (int i = 0; i < driver->devices_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + if (alsa_driver_set_parameters (driver, device, frames_per_cycle, user_nperiods, rate)) { + jack_error ("ALSA: failed to set parameters"); + alsa_driver_delete (driver); + return NULL; + } } - driver->capture_and_playback_not_synced = FALSE; + for (int i = 0; i < driver->devices_c_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + device->capture_linked = 0; + + if (i == 0) { + continue; + } + + if (snd_pcm_link (driver->devices[0].capture_handle, device->capture_handle) != 0) { + jack_error ("failed to add device to link group C: '%s'", device->capture_name); + continue; + } + device->capture_linked = 1; + } + + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + device->playback_linked = 0; + + snd_pcm_t *handle = driver->devices[0].capture_handle; + if (!handle) { + if (i == 0) { + continue; + } + handle = driver->devices[0].playback_handle; + } - if (driver->capture_handle && driver->playback_handle) { - if (snd_pcm_link (driver->playback_handle, - driver->capture_handle) != 0) { - driver->capture_and_playback_not_synced = TRUE; + if (snd_pcm_link (handle, device->playback_handle) != 0) { + jack_error ("failed to add device to link group P: '%s'", device->playback_name); + continue; } + device->playback_linked = 1; } driver->client = client; diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h index 2acc07dc4..da5266bba 100644 --- a/linux/alsa/alsa_driver.h +++ b/linux/alsa/alsa_driver.h @@ -77,10 +77,7 @@ typedef void (*WriteCopyFunction) (char *dst, jack_default_audio_sample_t *src, unsigned long dst_skip_bytes, dither_state_t *state); -typedef struct _alsa_driver { - - JACK_DRIVER_NT_DECL - +typedef struct _alsa_device { #ifdef __QNXNTO__ unsigned int playback_sample_format; unsigned int capture_sample_format; @@ -93,6 +90,60 @@ typedef struct _alsa_driver { snd_pcm_format_t capture_sample_format; const snd_pcm_channel_area_t *capture_areas; const snd_pcm_channel_area_t *playback_areas; +#endif + snd_pcm_t *playback_handle; + snd_pcm_t *capture_handle; + + char *playback_name; + char *capture_name; + + char **playback_addr; + char **capture_addr; + + channel_t playback_channel_offset; + channel_t capture_channel_offset; + + channel_t playback_nchannels; + channel_t capture_nchannels; + channel_t max_nchannels; + channel_t user_nchannels; + + bitset_t channels_not_done; + bitset_t channels_done; + + char quirk_bswap; + + ReadCopyFunction read_via_copy; + WriteCopyFunction write_via_copy; + + unsigned long interleave_unit; + unsigned long *capture_interleave_skip; + unsigned long *playback_interleave_skip; + + char playback_interleaved; + char capture_interleaved; + + unsigned long *silent; + + unsigned long playback_sample_bytes; + unsigned long capture_sample_bytes; + + /* is this device linked to first device */ + int capture_linked; + int playback_linked; + + int capture_xrun_count; + int playback_xrun_count; + + jack_hardware_t *hw; + char *alsa_driver; +} alsa_device_t; + +typedef struct _alsa_driver { + + JACK_DRIVER_NT_DECL + +#ifndef __QNXNTO__ snd_pcm_hw_params_t *playback_hw_params; snd_pcm_sw_params_t *playback_sw_params; snd_pcm_hw_params_t *capture_hw_params; @@ -101,46 +152,22 @@ typedef struct _alsa_driver { int poll_timeout_ms; jack_time_t poll_last; jack_time_t poll_next; - char **playback_addr; - char **capture_addr; struct pollfd *pfd; unsigned int playback_nfds; unsigned int capture_nfds; - unsigned long interleave_unit; - unsigned long *capture_interleave_skip; - unsigned long *playback_interleave_skip; - channel_t max_nchannels; - channel_t user_nchannels; channel_t playback_nchannels; channel_t capture_nchannels; - unsigned long playback_sample_bytes; - unsigned long capture_sample_bytes; jack_nframes_t frame_rate; jack_nframes_t frames_per_cycle; jack_nframes_t capture_frame_latency; jack_nframes_t playback_frame_latency; - unsigned long *silent; - char *alsa_name_playback; - char *alsa_name_capture; - char *alsa_driver; - bitset_t channels_not_done; - bitset_t channels_done; - float max_sample_val; unsigned long user_nperiods; unsigned int playback_nperiods; unsigned int capture_nperiods; - unsigned long last_mask; snd_ctl_t *ctl_handle; - snd_pcm_t *playback_handle; - snd_pcm_t *capture_handle; - jack_hardware_t *hw; - ClockSyncStatus *clock_sync_data; jack_client_t *client; - JSList *capture_ports; - JSList *playback_ports; - JSList *monitor_ports; unsigned long input_monitor_mask; @@ -148,17 +175,10 @@ typedef struct _alsa_driver { char hw_monitoring; char hw_metering; char all_monitor_in; - char capture_and_playback_not_synced; - char playback_interleaved; - char capture_interleaved; char with_monitor_ports; char has_clock_sync_reporting; char has_hw_monitoring; char has_hw_metering; - char quirk_bswap; - - ReadCopyFunction read_via_copy; - WriteCopyFunction write_via_copy; int dither; dither_state_t *dither_state; @@ -166,10 +186,6 @@ typedef struct _alsa_driver { SampleClockMode clock_mode; JSList *clock_sync_listeners; pthread_mutex_t clock_sync_lock; - unsigned long next_clock_sync_listener_id; - - int running; - int run; int poll_late; int xrun_count; @@ -178,69 +194,75 @@ typedef struct _alsa_driver { alsa_midi_t *midi; int xrun_recovery; + alsa_device_t *devices; + int devices_count; + int devices_c_count; + int devices_p_count; } alsa_driver_t; static inline void -alsa_driver_mark_channel_done (alsa_driver_t *driver, channel_t chn) { - bitset_remove (driver->channels_not_done, chn); - driver->silent[chn] = 0; +alsa_driver_mark_channel_done (alsa_driver_t *driver, alsa_device_t *device, channel_t chn) { + bitset_remove (device->channels_not_done, chn); + device->silent[chn] = 0; } static inline void -alsa_driver_silence_on_channel (alsa_driver_t *driver, channel_t chn, +alsa_driver_silence_on_channel (alsa_driver_t *driver, alsa_device_t *device, channel_t chn, jack_nframes_t nframes) { - if (driver->playback_interleaved) { + if (device->playback_interleaved) { memset_interleave - (driver->playback_addr[chn], - 0, nframes * driver->playback_sample_bytes, - driver->interleave_unit, - driver->playback_interleave_skip[chn]); + (device->playback_addr[chn], + 0, nframes * device->playback_sample_bytes, + device->interleave_unit, + device->playback_interleave_skip[chn]); } else { - memset (driver->playback_addr[chn], 0, - nframes * driver->playback_sample_bytes); + memset (device->playback_addr[chn], 0, + nframes * device->playback_sample_bytes); } - alsa_driver_mark_channel_done (driver,chn); + alsa_driver_mark_channel_done (driver, device, chn); } static inline void -alsa_driver_silence_on_channel_no_mark (alsa_driver_t *driver, channel_t chn, +alsa_driver_silence_on_channel_no_mark (alsa_driver_t *driver, alsa_device_t *device, channel_t chn, jack_nframes_t nframes) { - if (driver->playback_interleaved) { + if (device->playback_interleaved) { memset_interleave - (driver->playback_addr[chn], - 0, nframes * driver->playback_sample_bytes, - driver->interleave_unit, - driver->playback_interleave_skip[chn]); + (device->playback_addr[chn], + 0, nframes * device->playback_sample_bytes, + device->interleave_unit, + device->playback_interleave_skip[chn]); } else { - memset (driver->playback_addr[chn], 0, - nframes * driver->playback_sample_bytes); + memset (device->playback_addr[chn], 0, + nframes * device->playback_sample_bytes); } } static inline void alsa_driver_read_from_channel (alsa_driver_t *driver, + alsa_device_t *device, channel_t channel, jack_default_audio_sample_t *buf, jack_nframes_t nsamples) { - driver->read_via_copy (buf, - driver->capture_addr[channel], + device->read_via_copy (buf, + device->capture_addr[channel], nsamples, - driver->capture_interleave_skip[channel]); + device->capture_interleave_skip[channel]); } static inline void alsa_driver_write_to_channel (alsa_driver_t *driver, + alsa_device_t *device, channel_t channel, jack_default_audio_sample_t *buf, jack_nframes_t nsamples) { - driver->write_via_copy (driver->playback_addr[channel], + device->write_via_copy (device->playback_addr[channel], buf, nsamples, - driver->playback_interleave_skip[channel], + device->playback_interleave_skip[channel], driver->dither_state+channel); - alsa_driver_mark_channel_done (driver, channel); + alsa_driver_mark_channel_done (driver, device, channel); } int @@ -250,16 +272,18 @@ alsa_driver_reset_parameters (alsa_driver_t *driver, jack_nframes_t rate); jack_driver_t * -alsa_driver_new (char *name, char *playback_alsa_device, - char *capture_alsa_device, +alsa_driver_new (char *name, char **capture_alsa_devices, + char **playback_alsa_devices, + const char *capture_names, + const char *playback_names, jack_client_t *client, jack_nframes_t frames_per_cycle, jack_nframes_t user_nperiods, jack_nframes_t rate, int hw_monitoring, int hw_metering, - int capturing, - int playing, + int capturing_count, + int playing_count, DitherAlgorithm dither, int soft_mode, int monitor, @@ -291,10 +315,10 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes); // Code implemented in JackAlsaDriver.cpp -void ReadInput(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread); +void ReadInput(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread); void MonitorInput(); void ClearOutput(); -void WriteOutput(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten); +void WriteOutput(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten); void SetTime(jack_time_t time); int Restart(); From eb0739d5050d5f1c926e127652c929800c527870 Mon Sep 17 00:00:00 2001 From: "Signed-off-by: Kapildev Patel" Date: Fri, 7 Feb 2020 14:56:37 +0100 Subject: [PATCH 14/42] jack: alsa_driver: add check alsa_driver_poll_descriptor Signed-off-by: Kapildev Patel - adapted to latest version - removed redundant check Change-Id: Idf5208ac2d40ece6f1598f8fa2a7d8b80da52309 Signed-off-by: Adam Miartus (cherry picked from commit 58775de5353c9a198ed22ce41775015ef9f95211) Signed-off-by: Timo Wischer --- linux/alsa/alsa_driver.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 8c3e373b9..c8b87ff8e 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -1804,12 +1804,6 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float pfd_index++; } - /* nothing to poll on */ - if (pfd_index == 0 && extra_fd < 0) { - *status = -1; - return 0; - } - poll_enter = jack_get_microseconds (); if (poll_enter > driver->poll_next) { From 1ef6fb90e69e8984e8a3d9d25205e7cfdc69b7ca Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Fri, 27 Sep 2019 15:47:02 +0200 Subject: [PATCH 15/42] JackRequest: Add missing comma allowed by compiler and helps with clean commit history when adding changes Change-Id: Iaafbe0a01970cf1b176a24d8fdc8cc6ee5c97e3b Signed-off-by: Adam Miartus --- common/JackRequest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/JackRequest.h b/common/JackRequest.h index a1ede642c..3c2c8bef1 100644 --- a/common/JackRequest.h +++ b/common/JackRequest.h @@ -90,7 +90,7 @@ struct JackRequest kGetUUIDByClient = 37, kClientHasSessionCallback = 38, kComputeTotalLatencies = 39, - kPropertyChangeNotify = 40 + kPropertyChangeNotify = 40, }; RequestType fType; From 4ed6112e60b2be73deba73190a046a0a470fe80e Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Fri, 27 Sep 2019 15:47:11 +0200 Subject: [PATCH 16/42] jack common: add api jack_client_reload_master later used to restart JackAlsaDriver and allow closing unused devices (devices whose ports are are not connected) Change-Id: I886e4b23949e2a5e321194c18043f00f434537cc Signed-off-by: Adam Miartus (cherry picked from commit 39a31b50b3efbf0a118cf36f679001991147306d) Signed-off-by: Timo Wischer --- common/JackAPI.cpp | 16 ++++++++++++++++ common/JackChannel.h | 3 +++ common/JackClient.cpp | 7 +++++++ common/JackClient.h | 2 ++ common/JackDriver.cpp | 5 +++++ common/JackDriver.h | 2 ++ common/JackGenericClientChannel.cpp | 7 +++++++ common/JackGenericClientChannel.h | 2 ++ common/JackRequest.h | 26 ++++++++++++++++++++++++++ common/JackRequestDecoder.cpp | 10 ++++++++++ common/JackServer.cpp | 5 +++++ common/JackServer.h | 1 + common/JackThreadedDriver.cpp | 22 ++++++++++++++++++++++ common/JackThreadedDriver.h | 1 + common/jack/jack.h | 16 ++++++++++++++++ 15 files changed, 125 insertions(+) diff --git a/common/JackAPI.cpp b/common/JackAPI.cpp index 6789d9875..7a58f6de2 100644 --- a/common/JackAPI.cpp +++ b/common/JackAPI.cpp @@ -277,6 +277,8 @@ extern "C" LIB_EXPORT void jack_uuid_unparse(jack_uuid_t, char buf[JACK_UUID_STRING_SIZE]); LIB_EXPORT int jack_uuid_empty(jack_uuid_t); + LIB_EXPORT int jack_client_reload_master(jack_client_t* ext_client); + #ifdef __cplusplus } #endif @@ -2140,3 +2142,17 @@ LIB_EXPORT int jack_uuid_empty(jack_uuid_t u) { return u == JACK_UUID_EMPTY_INITIALIZER; } + +LIB_EXPORT int jack_client_reload_master(jack_client_t* ext_client) +{ + JackGlobals::CheckContext("jack_client_reload_master"); + + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_client_reload_master called with a NULL client"); + return -1; + } else { + WaitGraphChange(); + return client->ClientReloadMaster(); + } +} diff --git a/common/JackChannel.h b/common/JackChannel.h index 566870432..35aeda8f6 100644 --- a/common/JackChannel.h +++ b/common/JackChannel.h @@ -137,6 +137,9 @@ class JackClientChannelInterface virtual void ClientDeactivate(int refnum, int* result) {} + virtual void ClientReloadMaster(int* result) + {} + virtual void PortRegister(int refnum, const char* name, const char* type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result) {} virtual void PortUnRegister(int refnum, jack_port_id_t port_index, int* result) diff --git a/common/JackClient.cpp b/common/JackClient.cpp index b8303ab0e..01a00deee 100644 --- a/common/JackClient.cpp +++ b/common/JackClient.cpp @@ -502,6 +502,13 @@ int JackClient::Deactivate() if (IsRealTime()) { fThread.Kill(); } + return result; +} + +int JackClient::ClientReloadMaster() +{ + int result = -1; + fChannel->ClientReloadMaster(&result); return result; } diff --git a/common/JackClient.h b/common/JackClient.h index a0e506e7d..d18fd774a 100644 --- a/common/JackClient.h +++ b/common/JackClient.h @@ -145,6 +145,8 @@ class SERVER_EXPORT JackClient : public JackClientInterface, public JackRunnable virtual int Activate(); virtual int Deactivate(); + virtual int ClientReloadMaster(); + // Context virtual int SetBufferSize(jack_nframes_t buffer_size); virtual int SetFreeWheel(int onoff); diff --git a/common/JackDriver.cpp b/common/JackDriver.cpp index 8700b1c9d..fb4bbf36c 100644 --- a/common/JackDriver.cpp +++ b/common/JackDriver.cpp @@ -351,6 +351,11 @@ int JackDriver::Stop() return StopSlaves(); } +int JackDriver::Reload() +{ + return 0; +} + int JackDriver::StartSlaves() { int res = 0; diff --git a/common/JackDriver.h b/common/JackDriver.h index c66e2500e..9caa884a1 100644 --- a/common/JackDriver.h +++ b/common/JackDriver.h @@ -72,6 +72,7 @@ class SERVER_EXPORT JackDriverInterface virtual int Start() = 0; virtual int Stop() = 0; + virtual int Reload() = 0; virtual bool IsFixedBufferSize() = 0; virtual int SetBufferSize(jack_nframes_t buffer_size) = 0; @@ -227,6 +228,7 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface virtual int Start(); virtual int Stop(); + virtual int Reload(); // For "master" driver int ProcessReadSlaves(); diff --git a/common/JackGenericClientChannel.cpp b/common/JackGenericClientChannel.cpp index 96401d29d..c81512401 100644 --- a/common/JackGenericClientChannel.cpp +++ b/common/JackGenericClientChannel.cpp @@ -137,6 +137,13 @@ void JackGenericClientChannel::ClientDeactivate(int refnum, int* result) ServerSyncCall(&req, &res, result); } +void JackGenericClientChannel::ClientReloadMaster(int *result) +{ + JackClientReloadMasterRequest req; + JackResult res; + ServerSyncCall(&req, &res, result); +} + void JackGenericClientChannel::PortRegister(int refnum, const char* name, const char* type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result) { JackPortRegisterRequest req(refnum, name, type, flags, buffer_size); diff --git a/common/JackGenericClientChannel.h b/common/JackGenericClientChannel.h index b2c7b4ad0..a82b4a618 100644 --- a/common/JackGenericClientChannel.h +++ b/common/JackGenericClientChannel.h @@ -62,6 +62,8 @@ class JackGenericClientChannel : public detail::JackClientChannelInterface void ClientActivate(int refnum, int is_real_time, int* result); void ClientDeactivate(int refnum, int* result); + void ClientReloadMaster(int* result); + void PortRegister(int refnum, const char* name, const char* type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result); void PortUnRegister(int refnum, jack_port_id_t port_index, int* result); diff --git a/common/JackRequest.h b/common/JackRequest.h index 3c2c8bef1..2048ef5bd 100644 --- a/common/JackRequest.h +++ b/common/JackRequest.h @@ -91,6 +91,7 @@ struct JackRequest kClientHasSessionCallback = 38, kComputeTotalLatencies = 39, kPropertyChangeNotify = 40, + kClientReloadMaster = 41, }; RequestType fType; @@ -1736,6 +1737,31 @@ struct JackClientNotification }; +/*! +\brief Restart Master Backend Request. +*/ + +struct JackClientReloadMasterRequest : public JackRequest +{ + JackClientReloadMasterRequest(): JackRequest(JackRequest::kClientReloadMaster) + { + } + + int Read(detail::JackChannelTransactionInterface* trans) + { + CheckSize(); + return 0; + } + + int Write(detail::JackChannelTransactionInterface* trans) + { + CheckRes(JackRequest::Write(trans, Size())); + return 0; + } + + int Size() { return 0; } +}; + } // end of namespace #endif diff --git a/common/JackRequestDecoder.cpp b/common/JackRequestDecoder.cpp index 46bbd1f3c..533d2e504 100644 --- a/common/JackRequestDecoder.cpp +++ b/common/JackRequestDecoder.cpp @@ -346,6 +346,16 @@ int JackRequestDecoder::HandleRequest(detail::JackChannelTransactionInterface* s break; } + case JackRequest::kClientReloadMaster: { + jack_log("JackRequest::ClientReloadMaster"); + JackClientReloadMasterRequest req; + JackResult res; + CheckRead(req, socket); + res.fResult = fServer->ReloadMaster(); + CheckWrite("JackRequest::ClientReloadMaster", socket); + break; + } + default: jack_error("Unknown request %ld", type); return -1; diff --git a/common/JackServer.cpp b/common/JackServer.cpp index 5cee13fbd..0f9b4cd35 100644 --- a/common/JackServer.cpp +++ b/common/JackServer.cpp @@ -430,6 +430,11 @@ int JackServer::SwitchMaster(jack_driver_desc_t* driver_desc, JSList* driver_par return -1; } +int JackServer::ReloadMaster() +{ + return fAudioDriver->Reload(); +} + //---------------------- // Transport management //---------------------- diff --git a/common/JackServer.h b/common/JackServer.h index 71192e9e1..c7921b033 100644 --- a/common/JackServer.h +++ b/common/JackServer.h @@ -98,6 +98,7 @@ class SERVER_EXPORT JackServer JackDriverInfo* AddSlave(jack_driver_desc_t* driver_desc, JSList* driver_params); void RemoveSlave(JackDriverInfo* info); int SwitchMaster(jack_driver_desc_t* driver_desc, JSList* driver_params); + int ReloadMaster(); // Object access JackLockedEngine* GetEngine(); diff --git a/common/JackThreadedDriver.cpp b/common/JackThreadedDriver.cpp index b56a7343a..40d03e52b 100644 --- a/common/JackThreadedDriver.cpp +++ b/common/JackThreadedDriver.cpp @@ -239,6 +239,28 @@ int JackThreadedDriver::Stop() return 0; } +int JackThreadedDriver::Reload() +{ + if (Stop() < 0) { + jack_error("JackThreadedDriver::Reload stop failed"); + return -1; + } + + // not able to use Close() and Open() since we dont have original Open() parameters, these + // are internal to fDriver, reload should reopen with same parameters + if (fDriver->Reload() < 0) { + jack_error("JackThreadedDriver::Reload reload failed"); + return -1; + } + + if (Start() < 0) { + jack_error("JackThreadedDriver::Reload start failed"); + return -1; + } + + return 0; +} + bool JackThreadedDriver::Execute() { return (Process() == 0); diff --git a/common/JackThreadedDriver.h b/common/JackThreadedDriver.h index cdf10abbb..1f83ecad7 100644 --- a/common/JackThreadedDriver.h +++ b/common/JackThreadedDriver.h @@ -70,6 +70,7 @@ class SERVER_EXPORT JackThreadedDriver : public JackDriverClientInterface, publi virtual int Start(); virtual int Stop(); + virtual int Reload(); virtual bool IsFixedBufferSize(); virtual int SetBufferSize(jack_nframes_t buffer_size); diff --git a/common/jack/jack.h b/common/jack/jack.h index e982b6dff..c5eb27ed0 100644 --- a/common/jack/jack.h +++ b/common/jack/jack.h @@ -139,6 +139,22 @@ int jack_client_close (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; */ int jack_client_name_size (void) JACK_OPTIONAL_WEAK_EXPORT; +/** + * Reloads audio backend. + * + * This is useful when client wants audio backend to reload its state. + * Currently used only for alsa audio backend. + * + * Alsa backend will close or open audio devices on reload depending on + * the state of the ports asociated with audio device. If all ports + * are disconnected audio device is closed, opened otherwise. This + * behaviour is modifiable by alsa backend options provided to jackd + * on startup. + * + * @return 0 if successful. + */ +int jack_client_reload_master(jack_client_t* ext_client) JACK_OPTIONAL_WEAK_EXPORT; + /** * @return pointer to actual client name. This is useful when @ref * JackUseExactName is not specified on open and @ref From 521874dcd8bacc900575e4c42f08c3edf45e0dc2 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Wed, 2 Oct 2019 13:22:44 +0200 Subject: [PATCH 17/42] jack alsa: introduce alsa_driver_info_t [2/2] will be used to ease transporting data between functions, later will be used as part of alsa_driver_t struct parsing of input devices moved to driver_initialize Change-Id: I17ab2f51595aa3c8a77e36f68adfd4b60d8220a1 Signed-off-by: Adam Miartus (cherry picked from commit 9414ef5852f98af27e3d78b66f198b14e54c4da0) Signed-off-by: Timo Wischer --- common/JackDriver.cpp | 4 + linux/alsa/JackAlsaDriver.cpp | 263 ++++++++++++++++++---------------- linux/alsa/JackAlsaDriver.h | 19 +-- linux/alsa/alsa_driver.c | 77 ++++------ linux/alsa/alsa_driver.h | 58 +++++--- 5 files changed, 206 insertions(+), 215 deletions(-) diff --git a/common/JackDriver.cpp b/common/JackDriver.cpp index fb4bbf36c..8974b2d65 100644 --- a/common/JackDriver.cpp +++ b/common/JackDriver.cpp @@ -110,6 +110,10 @@ int JackDriver::Open(jack_nframes_t buffer_size, fClientControl.fRefNum = refnum; fClientControl.fActive = true; fEngineControl->fDriverNum++; + if (buffer_size > BUFFER_SIZE_MAX) { + jack_error("Buffer size %ld exceeds BUFFER_SIZE_MAX %d", buffer_size, BUFFER_SIZE_MAX); + return -1; + } if (buffer_size > 0) { fEngineControl->fBufferSize = buffer_size; } diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index 98594da5a..68dcb1671 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -289,64 +289,45 @@ static int card_to_num(const char* device) } #endif -int JackAlsaDriver::Open(jack_nframes_t nframes, - jack_nframes_t user_nperiods, - jack_nframes_t samplerate, - bool hw_monitoring, - bool hw_metering, - bool capturing, - bool playing, - DitherAlgorithm dither, - bool soft_mode, - bool monitor, - int inchannels, - int outchannels, - bool shorts_first, - const char* capture_driver_name, - const char* playback_driver_name, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency, - const char* midi_driver_name) +int JackAlsaDriver::Open(alsa_driver_info_t info) { - struct array_string_t capture_drivers = array_string_split(capture_driver_name, ' '); - struct array_string_t playback_drivers = array_string_split(playback_driver_name, ' '); - // Generic JackAudioDriver Open - if (JackAudioDriver::Open(nframes, samplerate, capturing, playing, - inchannels, outchannels, monitor, - capture_drivers.size > 1 ? capture_drivers.data[0] : capture_driver_name, - playback_drivers.size > 1 ? playback_drivers.data[0] : playback_driver_name, - capture_latency, playback_latency) != 0) { - array_string_free(&capture_drivers); - array_string_free(&playback_drivers); + if (JackAudioDriver::Open( + info.frames_per_period, + info.frame_rate, + info.devices_capture_size > 0, + info.devices_playback_size > 0, + -1, + -1, + info.monitor, + info.devices_capture_size > 0 ? info.devices[0].capture_name : "-", + info.devices_playback_size > 0 ? info.devices[0].playback_name : "-", + info.capture_latency, + info.playback_latency) != 0) { return -1; } - jack_log("JackAlsaDriver::Open capture_driver_name = %s", capture_driver_name); - jack_log("JackAlsaDriver::Open playback_driver_name = %s", playback_driver_name); - - alsa_midi_t *midi = 0; + jack_log("JackAlsaDriver::Open capture_driver_name = %s", info.devices_capture_size > 0 ? info.devices[0].capture_name : "-"); + jack_log("JackAlsaDriver::Open playback_driver_name = %s", info.devices_playback_size > 0 ? info.devices[0].playback_name : "-"); #ifndef __QNXNTO__ #ifndef __ANDROID__ - if (strcmp(midi_driver_name, "seq") == 0) - midi = alsa_seqmidi_new((jack_client_t*)this, 0); - else if (strcmp(midi_driver_name, "raw") == 0) - midi = alsa_rawmidi_new((jack_client_t*)this); + if (strcmp(info.midi_name, "seq") == 0) + info.midi_driver = alsa_seqmidi_new((jack_client_t*)this, 0); + else if (strcmp(info.midi_name, "raw") == 0) + info.midi_driver = alsa_rawmidi_new((jack_client_t*)this); #endif // FIXME: needs adaptation for multiple drivers if (JackServerGlobals::on_device_acquire != NULL) { - int capture_card = card_to_num(capture_drivers.data[0]); - int playback_card = card_to_num(playback_drivers.data[0]); + int capture_card = card_to_num(info.devices_capture_size > 0 ? info.devices[0].capture_name : "-"); + int playback_card = card_to_num(info.devices_playback_size > 0 ? info.devices[0].playback_name : "-"); char audio_name[32]; if (capture_card >= 0) { snprintf(audio_name, sizeof(audio_name), "Audio%d", capture_card); if (!JackServerGlobals::on_device_acquire(audio_name)) { - jack_error("Audio device %s cannot be acquired...", capture_drivers.data[0]); - array_string_free(&capture_drivers); - array_string_free(&playback_drivers); + jack_error("Audio device %s cannot be acquired...", info.devices_capture_size > 0 ? info.devices[0].capture_name : "-"); return -1; } } @@ -354,44 +335,18 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, if (playback_card >= 0 && playback_card != capture_card) { snprintf(audio_name, sizeof(audio_name), "Audio%d", playback_card); if (!JackServerGlobals::on_device_acquire(audio_name)) { - jack_error("Audio device %s cannot be acquired...", playback_drivers.data[0]); + jack_error("Audio device %s cannot be acquired...",info.devices_playback_size > 0 ? info.devices[0].playback_name : "-" ); if (capture_card >= 0) { snprintf(audio_name, sizeof(audio_name), "Audio%d", capture_card); JackServerGlobals::on_device_release(audio_name); } - array_string_free(&capture_drivers); - array_string_free(&playback_drivers); return -1; } } } #endif - fDriver = alsa_driver_new ((char*)"alsa_pcm", - capture_drivers.data, - playback_drivers.data, - capture_driver_name, - playback_driver_name, - NULL, - nframes, - user_nperiods, - samplerate, - hw_monitoring, - hw_metering, - capturing ? capture_drivers.size : 0, - playing ? playback_drivers.size : 0, - dither, - soft_mode, - monitor, - inchannels, - outchannels, - shorts_first, - capture_latency, - playback_latency, - midi); - - array_string_free(&capture_drivers); - array_string_free(&playback_drivers); + fDriver = alsa_driver_new ((char*)"alsa_pcm", info, NULL); if (fDriver) { // ALSA driver may have changed the in/out values @@ -810,9 +765,9 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () "Dithering mode", NULL); - value.ui = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "inchannels", 'i', JackDriverParamUInt, &value, NULL, "Number of capture channels (defaults to hardware max)", NULL); - jack_driver_descriptor_add_parameter(desc, &filler, "outchannels", 'o', JackDriverParamUInt, &value, NULL, "Number of playback channels (defaults to hardware max)", NULL); + strcpy(value.str, "none"); + jack_driver_descriptor_add_parameter(desc, &filler, "inchannels", 'i', JackDriverParamString, &value, NULL, "List of device capture channels (defaults to hardware max)", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "outchannels", 'o', JackDriverParamString, &value, NULL, "List of device playback channels (defaults to hardware max)", NULL); value.i = FALSE; jack_driver_descriptor_add_parameter(desc, &filler, "shorts", 'S', JackDriverParamBool, &value, NULL, "Try 16-bit samples before 32-bit", NULL); @@ -899,26 +854,31 @@ static Jack::JackAlsaDriver* g_alsa_driver; SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) { - jack_nframes_t srate = 48000; - jack_nframes_t frames_per_interrupt = 1024; - unsigned long user_nperiods = 2; - const char *playback_pcm_name = "hw:0"; - const char *capture_pcm_name = "hw:0"; - int hw_monitoring = FALSE; - int hw_metering = FALSE; - int capture = FALSE; - int playback = FALSE; - int soft_mode = FALSE; - int monitor = FALSE; - DitherAlgorithm dither = None; - int user_capture_nchnls = 0; - int user_playback_nchnls = 0; - int shorts_first = FALSE; - jack_nframes_t systemic_input_latency = 0; - jack_nframes_t systemic_output_latency = 0; const JSList * node; const jack_driver_param_t * param; - const char *midi_driver = "none"; + + alsa_driver_info_t info = {}; + info.devices = NULL; + info.midi_name = strdup("none"); + info.hw_monitoring = FALSE; + info.hw_metering = FALSE; + info.monitor = FALSE; + info.soft_mode = FALSE; + info.frame_rate = 48000; + info.frames_per_period = 1024; + info.periods_n = 2; + info.dither = None; + info.shorts_first = FALSE; + info.capture_latency = 0; + info.playback_latency = 0; + + char *capture_names_param = NULL; + char *playback_names_param = NULL; + + char *capture_channels_param = NULL; + char *playback_channels_param = NULL; + + int duplex = FALSE; for (node = params; node; node = jack_slist_next (node)) { param = (const jack_driver_param_t *) node->data; @@ -926,112 +886,163 @@ SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLocke switch (param->character) { case 'C': - capture = TRUE; if (strcmp (param->value.str, "none") != 0) { - capture_pcm_name = strdup (param->value.str); - jack_log("capture device %s", capture_pcm_name); + capture_names_param = strdup (param->value.str); + jack_log("capture device %s", capture_names_param); } break; case 'P': - playback = TRUE; if (strcmp (param->value.str, "none") != 0) { - playback_pcm_name = strdup (param->value.str); - jack_log("playback device %s", playback_pcm_name); + playback_names_param = strdup (param->value.str); + jack_log("playback device %s", playback_names_param); } break; case 'D': - playback = TRUE; - capture = TRUE; + duplex = TRUE; break; case 'd': if (strcmp (param->value.str, "none") != 0) { - playback_pcm_name = strdup (param->value.str); - capture_pcm_name = strdup (param->value.str); - jack_log("playback device %s", playback_pcm_name); - jack_log("capture device %s", capture_pcm_name); + playback_names_param = strdup (param->value.str); + capture_names_param = strdup (param->value.str); + jack_log("playback device %s", playback_names_param); + jack_log("capture device %s", capture_names_param); } break; case 'H': - hw_monitoring = param->value.i; + info.hw_monitoring = param->value.i; break; case 'm': - monitor = param->value.i; + info.monitor = param->value.i; break; case 'M': - hw_metering = param->value.i; + info.hw_metering = param->value.i; break; case 'r': - srate = param->value.ui; - jack_log("apparent rate = %d", srate); + info.frame_rate = param->value.ui; + jack_log("apparent rate = %d", info.frame_rate); break; case 'p': - frames_per_interrupt = param->value.ui; - jack_log("frames per period = %d", frames_per_interrupt); + info.frames_per_period = param->value.ui; + jack_log("frames per period = %d", info.frames_per_period); break; case 'n': - user_nperiods = param->value.ui; - if (user_nperiods < 2) { /* enforce minimum value */ - user_nperiods = 2; + info.periods_n = param->value.ui; + if (info.periods_n < 2) { /* enforce minimum value */ + info.periods_n = 2; } break; case 's': - soft_mode = param->value.i; + info.soft_mode = param->value.i; break; case 'z': - if (dither_opt (param->value.c, &dither)) { + if (dither_opt (param->value.c, &info.dither)) { return NULL; } break; case 'i': - user_capture_nchnls = param->value.ui; + capture_channels_param = strdup(param->value.str); break; case 'o': - user_playback_nchnls = param->value.ui; + playback_channels_param = strdup(param->value.str); break; case 'S': - shorts_first = param->value.i; + info.shorts_first = param->value.i; break; case 'I': - systemic_input_latency = param->value.ui; + info.capture_latency = param->value.ui; break; case 'O': - systemic_output_latency = param->value.ui; + info.playback_latency = param->value.ui; break; case 'X': - midi_driver = strdup(param->value.str); + free(info.midi_name); + info.midi_name = strdup(param->value.str); break; } } /* duplex is the default */ - if (!capture && !playback) { - capture = TRUE; - playback = TRUE; + if (!capture_names_param && !playback_names_param) { + duplex = TRUE; + } + + if (duplex) { + if (!capture_names_param) { + capture_names_param = strdup("hw:0"); + } + if (!playback_names_param) { + playback_names_param = strdup("hw:0"); + } + } + + struct array_string_t capture_names = {}; + if (capture_names_param) { + capture_names = array_string_split(capture_names_param, ' '); + free(capture_names_param); + } + + struct array_string_t playback_names = {}; + if (playback_names_param) { + playback_names = array_string_split(playback_names_param, ' '); + free(playback_names_param); + } + + struct array_string_t capture_channels = {}; + if (capture_channels_param) { + capture_channels = array_string_split(capture_channels_param, ' '); + free(capture_channels_param); + } + + struct array_string_t playback_channels = {}; + if (playback_channels_param) { + playback_channels = array_string_split(playback_channels_param, ' '); + free(playback_channels_param); + } + + info.devices_capture_size = capture_names.size; + info.devices_playback_size = playback_names.size; + info.devices = (alsa_device_info_t*) calloc(std::max(info.devices_capture_size, info.devices_playback_size), sizeof(alsa_device_info_t)); + for (size_t i = 0; i < std::max(info.devices_capture_size, info.devices_playback_size); ++i) { + if (i < capture_names.size) { + info.devices[i].capture_name = strdup(capture_names.data[i]); + } + if (i < capture_channels.size) { + info.devices[i].capture_channels = atoi(capture_channels.data[i]); + } + if (i < playback_names.size) { + info.devices[i].playback_name = strdup(playback_names.data[i]); + } + if (i < playback_channels.size) { + info.devices[i].playback_channels = atoi(playback_channels.data[i]); + } } + array_string_free(&capture_names); + array_string_free(&playback_names); + array_string_free(&capture_channels); + array_string_free(&playback_channels); + g_alsa_driver = new Jack::JackAlsaDriver("system", "alsa_pcm", engine, table); Jack::JackDriverClientInterface* threaded_driver = new Jack::JackThreadedDriver(g_alsa_driver); // Special open for ALSA driver... - if (g_alsa_driver->Open(frames_per_interrupt, user_nperiods, srate, hw_monitoring, hw_metering, capture, playback, dither, soft_mode, monitor, - user_capture_nchnls, user_playback_nchnls, shorts_first, capture_pcm_name, playback_pcm_name, - systemic_input_latency, systemic_output_latency, midi_driver) == 0) { + if (g_alsa_driver->Open(info) == 0) { return threaded_driver; } else { delete threaded_driver; // Delete the decorated driver diff --git a/linux/alsa/JackAlsaDriver.h b/linux/alsa/JackAlsaDriver.h index f83cd2dd9..5ef75f794 100644 --- a/linux/alsa/JackAlsaDriver.h +++ b/linux/alsa/JackAlsaDriver.h @@ -51,24 +51,7 @@ class JackAlsaDriver : public JackAudioDriver virtual ~JackAlsaDriver() {} - int Open(jack_nframes_t buffer_size, - jack_nframes_t user_nperiods, - jack_nframes_t samplerate, - bool hw_monitoring, - bool hw_metering, - bool capturing, - bool playing, - DitherAlgorithm dither, - bool soft_mode, - bool monitor, - int inchannels, - int outchannels, - bool shorts_first, - const char* capture_driver_name, - const char* playback_driver_name, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency, - const char* midi_driver_name); + int Open(alsa_driver_info_t info); int Close(); int Attach(); diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index c8b87ff8e..f4a846894 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -2547,44 +2547,23 @@ alsa_driver_open (alsa_driver_t *driver, alsa_device_t *device, bool is_capture) } jack_driver_t * -alsa_driver_new (char *name, char **capture_alsa_devices, - char **playback_alsa_devices, - const char *capture_names, - const char *playback_names, - jack_client_t *client, - jack_nframes_t frames_per_cycle, - jack_nframes_t user_nperiods, - jack_nframes_t rate, - int hw_monitoring, - int hw_metering, - int capturing_count, - int playing_count, - DitherAlgorithm dither, - int soft_mode, - int monitor, - int user_capture_nchnls, - int user_playback_nchnls, - int shorts_first, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency, - alsa_midi_t *midi_driver - ) +alsa_driver_new (char *name, alsa_driver_info_t info, jack_client_t *client) { int err; alsa_driver_t *driver; jack_info ("creating alsa driver ... %s|%" PRIu32 "|%s|%" PRIu32 "|%" PRIu32 "|%" PRIu32 "|%" PRIu32"|%" PRIu32"|%" PRIu32 "|%s|%s|%s|%s", - capturing_count > 0 ? capture_names : "-", - capturing_count, - playing_count > 0 ? playback_names : "-", - playing_count, - frames_per_cycle, user_nperiods, rate, - user_capture_nchnls,user_playback_nchnls, - hw_monitoring ? "hwmon": "nomon", - hw_metering ? "hwmeter":"swmeter", - soft_mode ? "soft-mode":"-", - shorts_first ? "16bit":"32bit"); + info.devices_capture_size > 0 ? info.devices[0].capture_name : "-", + info.devices_capture_size, + info.devices_playback_size > 0 ? info.devices[0].playback_name : "-", + info.devices_playback_size, + info.frames_per_period, info.periods_n, info.frame_rate, + info.devices[0].capture_channels, info.devices[0].playback_channels, + info.hw_monitoring ? "hwmon": "nomon", + info.hw_metering ? "hwmeter":"swmeter", + info.soft_mode ? "soft-mode":"-", + info.shorts_first ? "16bit":"32bit"); driver = (alsa_driver_t *) calloc (1, sizeof (alsa_driver_t)); @@ -2604,11 +2583,11 @@ alsa_driver_new (char *name, char **capture_alsa_devices, */ driver->ctl_handle = 0; - driver->capture_frame_latency = capture_latency; - driver->playback_frame_latency = playback_latency; + driver->capture_frame_latency = info.capture_latency; + driver->playback_frame_latency = info.playback_latency; driver->all_monitor_in = FALSE; - driver->with_monitor_ports = monitor; + driver->with_monitor_ports = info.monitor; driver->clock_mode = ClockMaster; /* XXX is it? */ driver->input_monitor_mask = 0; /* XXX is it? */ @@ -2617,8 +2596,8 @@ alsa_driver_new (char *name, char **capture_alsa_devices, driver->playback_nfds = 0; driver->capture_nfds = 0; - driver->dither = dither; - driver->soft_mode = soft_mode; + driver->dither = info.dither; + driver->soft_mode = info.soft_mode; pthread_mutex_init (&driver->clock_sync_lock, 0); driver->clock_sync_listeners = 0; @@ -2627,22 +2606,22 @@ alsa_driver_new (char *name, char **capture_alsa_devices, driver->xrun_count = 0; driver->process_count = 0; - driver->midi = midi_driver; + driver->midi = info.midi_driver; driver->xrun_recovery = 0; - driver->devices_c_count = capturing_count; - driver->devices_p_count = playing_count; - driver->devices_count = capturing_count > playing_count ? capturing_count : playing_count; + driver->devices_c_count = info.devices_capture_size; + driver->devices_p_count = info.devices_playback_size; + driver->devices_count = info.devices_capture_size > info.devices_playback_size ? info.devices_capture_size : info.devices_playback_size; driver->devices = (alsa_device_t*) calloc(driver->devices_count, sizeof(*driver->devices)); if (driver->devices_count == 1) { - driver->devices[0].capture_nchannels = user_capture_nchnls; - driver->devices[0].playback_nchannels = user_playback_nchnls; + driver->devices[0].capture_nchannels = info.devices[0].capture_channels; + driver->devices[0].playback_nchannels = info.devices[0].playback_channels; } for (int i = 0; i < driver->devices_c_count; ++i) { - driver->devices[i].capture_sample_bytes = (shorts_first ? 2:4); - driver->devices[i].capture_name = strdup(capture_alsa_devices[i]); + driver->devices[i].capture_sample_bytes = (info.shorts_first ? 2:4); + driver->devices[i].capture_name = strdup(info.devices[i].capture_name); err = alsa_driver_open(driver, &driver->devices[i], SND_PCM_STREAM_CAPTURE); if (err < 0) { @@ -2654,8 +2633,8 @@ alsa_driver_new (char *name, char **capture_alsa_devices, } for (int i = 0; i < driver->devices_p_count; ++i) { - driver->devices[i].playback_sample_bytes = (shorts_first ? 2:4); - driver->devices[i].playback_name = strdup(playback_alsa_devices[i]); + driver->devices[i].playback_sample_bytes = (info.shorts_first ? 2:4); + driver->devices[i].playback_name = strdup(info.devices[i].playback_name); #ifndef __QNXNTO__ if (alsa_driver_check_card_type (driver, &driver->devices[i])) { @@ -2663,7 +2642,7 @@ alsa_driver_new (char *name, char **capture_alsa_devices, return NULL; } - alsa_driver_hw_specific (driver, &driver->devices[i], hw_monitoring, hw_metering); + alsa_driver_hw_specific (driver, &driver->devices[i], driver->hw_monitoring, driver->hw_metering); #endif err = alsa_driver_open(driver, &driver->devices[i], SND_PCM_STREAM_PLAYBACK); @@ -2720,7 +2699,7 @@ alsa_driver_new (char *name, char **capture_alsa_devices, for (int i = 0; i < driver->devices_count; ++i) { alsa_device_t *device = &driver->devices[i]; - if (alsa_driver_set_parameters (driver, device, frames_per_cycle, user_nperiods, rate)) { + if (alsa_driver_set_parameters (driver, device, info.frames_per_period, info.periods_n, info.frame_rate)) { jack_error ("ALSA: failed to set parameters"); alsa_driver_delete (driver); return NULL; diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h index da5266bba..61b1e92e9 100644 --- a/linux/alsa/alsa_driver.h +++ b/linux/alsa/alsa_driver.h @@ -200,6 +200,40 @@ typedef struct _alsa_driver { int devices_p_count; } alsa_driver_t; +typedef struct _alsa_device_info { + char *capture_name; + char *playback_name; + + int capture_channels; + int playback_channels; +} alsa_device_info_t; + +typedef struct _alsa_driver_info { + alsa_device_info_t *devices; + uint32_t devices_capture_size; + uint32_t devices_playback_size; + + char *midi_name; + alsa_midi_t *midi_driver; + + jack_nframes_t frame_rate; + jack_nframes_t frames_per_period; + int periods_n; + + DitherAlgorithm dither; + + int shorts_first; + + jack_nframes_t capture_latency; + jack_nframes_t playback_latency; + + // these 4 should be reworked as struct.features + int hw_monitoring; + int hw_metering; + int monitor; + int soft_mode; +} alsa_driver_info_t; + static inline void alsa_driver_mark_channel_done (alsa_driver_t *driver, alsa_device_t *device, channel_t chn) { bitset_remove (device->channels_not_done, chn); @@ -272,28 +306,8 @@ alsa_driver_reset_parameters (alsa_driver_t *driver, jack_nframes_t rate); jack_driver_t * -alsa_driver_new (char *name, char **capture_alsa_devices, - char **playback_alsa_devices, - const char *capture_names, - const char *playback_names, - jack_client_t *client, - jack_nframes_t frames_per_cycle, - jack_nframes_t user_nperiods, - jack_nframes_t rate, - int hw_monitoring, - int hw_metering, - int capturing_count, - int playing_count, - DitherAlgorithm dither, - int soft_mode, - int monitor, - int user_capture_nchnls, - int user_playback_nchnls, - int shorts_first, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency, - alsa_midi_t *midi_driver - ); +alsa_driver_new (char *name, alsa_driver_info_t info, jack_client_t *client); + void alsa_driver_delete (alsa_driver_t *driver); From 78e97af2665ad8301eb72e5a042d4d8944f8d027 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Tue, 1 Oct 2019 18:46:27 +0200 Subject: [PATCH 18/42] jack alsa: implement restart request [1/3] on restart request desired state of devices is evaluated, devices will be closed, left prepared, started depending on selected options or their usage (number or connected ports) Change-Id: Id512e013556169dfc3837fd5f10845848a211d5b Signed-off-by: Adam Miartus (cherry picked from commit 9bdb9d09de12d5b11000d23d0276687a43a36ab2) Signed-off-by: Timo Wischer --- linux/alsa/JackAlsaDriver.cpp | 68 +++ linux/alsa/JackAlsaDriver.h | 5 + linux/alsa/alsa_driver.c | 843 ++++++++++++++++++++-------------- linux/alsa/alsa_driver.h | 19 +- 4 files changed, 601 insertions(+), 334 deletions(-) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index 68dcb1671..fb976f7af 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -349,6 +349,12 @@ int JackAlsaDriver::Open(alsa_driver_info_t info) fDriver = alsa_driver_new ((char*)"alsa_pcm", info, NULL); if (fDriver) { + /* we need to initialize variables for all devices, mainly channels count since this is required by Jack to setup ports */ + UpdateDriverTargetState(1); + if (alsa_driver_open((alsa_driver_t *)fDriver) < 0) { + Close(); + return -1; + } // ALSA driver may have changed the in/out values fCaptureChannels = ((alsa_driver_t *)fDriver)->capture_nchannels; fPlaybackChannels = ((alsa_driver_t *)fDriver)->playback_nchannels; @@ -360,6 +366,7 @@ int JackAlsaDriver::Open(alsa_driver_info_t info) } } #endif + return 0; } else { Close(); @@ -372,6 +379,8 @@ int JackAlsaDriver::Close() // Generic audio driver close int res = JackAudioDriver::Close(); + alsa_driver_close((alsa_driver_t *)fDriver); + if (fDriver) { alsa_driver_delete((alsa_driver_t*)fDriver); } @@ -424,6 +433,23 @@ int JackAlsaDriver::Stop() return res; } +int JackAlsaDriver::Reload() +{ + UpdateDriverTargetState(); + + alsa_driver_t* driver = (alsa_driver_t*) fDriver; + if (alsa_driver_close (driver) < 0) { + jack_error("JackAlsaDriver::Reload close failed"); + return -1; + } + if (alsa_driver_open (driver) < 0) { + jack_error("JackAlsaDriver::Reload open failed"); + return -1; + } + + return 0; +} + int JackAlsaDriver::Read() { /* Taken from alsa_driver_run_cycle */ @@ -503,6 +529,48 @@ int JackAlsaDriver::PortSetDefaultMetadata(jack_port_id_t port_id, const char* p return fEngine->PortSetDefaultMetadata(fClientControl.fRefNum, port_id, pretty_name); } +int JackAlsaDriver::UpdateDriverTargetState(int init) +{ + int c_list_index = 0, p_list_index = 0; + alsa_driver_t* driver = (alsa_driver_t*) fDriver; + + for (int i = 0; i < driver->devices_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + int capture_connections_count = 0; + for (int j = 0; j < device->capture_nchannels; ++j) { + capture_connections_count += fGraphManager->GetConnectionsNum(fCapturePortList[c_list_index]); + c_list_index++; + } + device->capture_target_state = TargetState(init, capture_connections_count); + + int playback_connections_count = 0; + for (int j = 0; j < device->playback_nchannels; ++j) { + playback_connections_count += fGraphManager->GetConnectionsNum(fPlaybackPortList[p_list_index]); + p_list_index++; + } + device->playback_target_state = TargetState(init, playback_connections_count); + } + + return 0; +} + +int JackAlsaDriver::TargetState(int init, int connections_count) +{ + alsa_driver_t* driver = (alsa_driver_t*) fDriver; + int state = SND_PCM_STATE_PREPARED; + + if (connections_count > 0) { + state = SND_PCM_STATE_RUNNING; + } else if (init) { + state = SND_PCM_STATE_RUNNING; + } else { + state = SND_PCM_STATE_PREPARED; + } + + return state; +} + void JackAlsaDriver::WriteOutputAux(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten) { /* global channel offset to fPlaybackPortList of this playback alsa device */ diff --git a/linux/alsa/JackAlsaDriver.h b/linux/alsa/JackAlsaDriver.h index 5ef75f794..bf9e679a9 100644 --- a/linux/alsa/JackAlsaDriver.h +++ b/linux/alsa/JackAlsaDriver.h @@ -59,6 +59,7 @@ class JackAlsaDriver : public JackAudioDriver int Start(); int Stop(); + int Reload(); int Read(); int Write(); @@ -79,6 +80,10 @@ class JackAlsaDriver : public JackAudioDriver int PortSetDefaultMetadata(jack_port_id_t port_id, const char* pretty_name); + int UpdateDriverTargetState(int init = 0); + + int TargetState(int init, int connections_count); + // JACK API emulation for the midi driver int is_realtime() const; int create_thread(pthread_t *thread, int prio, int rt, void *(*start_func)(void*), void *arg); diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index f4a846894..371dc5870 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -59,6 +59,8 @@ char* strcasestr(const char* haystack, const char* needle); /* Max re-try count for Alsa poll timeout handling */ #define MAX_RETRY_COUNT 5 +static int alsa_driver_open_device (alsa_driver_t *driver, alsa_device_t *device, bool is_capture); + void jack_driver_init (jack_driver_t *driver) { @@ -825,6 +827,8 @@ alsa_driver_set_sample_bytes (alsa_driver_t *driver, alsa_device_t *device) static int alsa_driver_set_parameters (alsa_driver_t *driver, alsa_device_t *device, + int do_capture, + int do_playback, jack_nframes_t frames_per_cycle, jack_nframes_t user_nperiods, jack_nframes_t rate) @@ -853,158 +857,202 @@ alsa_driver_set_parameters (alsa_driver_t *driver, device->capture_name != NULL ? device->capture_name : "-", device->playback_name != NULL ? device->playback_name : "-", rate, frames_per_cycle, (((float)frames_per_cycle / (float) rate) * 1000.0f), user_nperiods); - if (device->capture_handle) { -#ifdef __QNXNTO__ - err = alsa_driver_configure_stream ( - driver, - device, - device->capture_name, - "capture", - device->capture_handle, - &driver->capture_nperiods, - &device->capture_nchannels, - device->capture_sample_bytes, - SND_PCM_CHANNEL_CAPTURE); -#else - err = alsa_driver_configure_stream ( - driver, - device, - device->capture_name, - "capture", - device->capture_handle, - driver->capture_hw_params, - driver->capture_sw_params, - &driver->capture_nperiods, - &device->capture_nchannels, - device->capture_sample_bytes); -#endif - if (err) { - jack_error ("ALSA: cannot configure capture channel"); + if (do_capture) { + if (!device->capture_handle) { + jack_error ("ALSA: pcm capture handle not available"); return -1; } - } - - if (device->playback_handle) { #ifdef __QNXNTO__ - err = alsa_driver_configure_stream ( - driver, - device, - device->playback_name, - "playback", - device->playback_handle, - &driver->playback_nperiods, - &device->playback_nchannels, - device->playback_sample_bytes, - SND_PCM_CHANNEL_PLAYBACK); -#else - err = alsa_driver_configure_stream ( - driver, - device, - device->playback_name, - "playback", - device->playback_handle, - driver->playback_hw_params, - driver->playback_sw_params, - &driver->playback_nperiods, - &device->playback_nchannels, - device->playback_sample_bytes); -#endif + err = alsa_driver_configure_stream ( + driver, + device, + device->capture_name, + "capture", + device->capture_handle, + &driver->capture_nperiods, + &device->capture_nchannels, + device->capture_sample_bytes, + SND_PCM_CHANNEL_CAPTURE); + if (err) { - jack_error ("ALSA: cannot configure playback channel"); + jack_error ("ALSA: cannot configure capture channel"); return -1; } - } -#ifdef __QNXNTO__ - if (device->capture_handle) { err = alsa_driver_get_setup(driver, device, &c_setup, SND_PCM_CHANNEL_CAPTURE); if(err < 0) { + jack_error ("ALSA: get setup failed"); return -1; } + cr = c_setup.format.rate; c_period_size = c_setup.buf.block.frag_size / device->capture_nchannels / device->capture_sample_bytes; c_periods = c_setup.buf.block.frags; device->capture_sample_format = c_setup.format.format; device->capture_interleaved = c_setup.format.interleave; +#else + err = alsa_driver_configure_stream ( + driver, + device, + device->capture_name, + "capture", + device->capture_handle, + driver->capture_hw_params, + driver->capture_sw_params, + &driver->capture_nperiods, + &device->capture_nchannels, + device->capture_sample_bytes); + + if (err) { + jack_error ("ALSA: cannot configure capture channel"); + return -1; + } + + snd_pcm_hw_params_get_rate (driver->capture_hw_params, + &cr, &dir); + + snd_pcm_access_t access; + + err = snd_pcm_hw_params_get_period_size ( + driver->capture_hw_params, &c_period_size, &dir); + err = snd_pcm_hw_params_get_format ( + driver->capture_hw_params, + &(device->capture_sample_format)); + err = snd_pcm_hw_params_get_access (driver->capture_hw_params, + &access); + device->capture_interleaved = + (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) + || (access == SND_PCM_ACCESS_MMAP_COMPLEX); +#endif + if (err) { + jack_error ("ALSA: cannot configure capture channel"); + return -1; + } } - if (device->playback_handle) { + + if (do_playback) { + if (!device->playback_handle) { + jack_error ("ALSA: pcm playback handle not available"); + return -1; + } +#ifdef __QNXNTO__ + err = alsa_driver_configure_stream ( + driver, + device, + device->playback_name, + "playback", + device->playback_handle, + &driver->playback_nperiods, + &device->playback_nchannels, + device->playback_sample_bytes, + SND_PCM_CHANNEL_PLAYBACK); + + if (err) { + jack_error ("ALSA: cannot configure playback channel"); + return -1; + } + err = alsa_driver_get_setup(driver, device, &p_setup, SND_PCM_CHANNEL_PLAYBACK); if(err < 0) { + jack_error ("ALSA: get setup failed"); return -1; } + pr = p_setup.format.rate; p_period_size = p_setup.buf.block.frag_size / device->playback_nchannels / device->playback_sample_bytes; p_periods = p_setup.buf.block.frags; device->playback_sample_format = p_setup.format.format; device->playback_interleaved = p_setup.format.interleave; - } #else - /* check the rate, since that's rather important */ + err = alsa_driver_configure_stream ( + driver, + device, + device->playback_name, + "playback", + device->playback_handle, + driver->playback_hw_params, + driver->playback_sw_params, + &driver->playback_nperiods, + &device->playback_nchannels, + device->playback_sample_bytes); - if (device->playback_handle) { + if (err) { + jack_error ("ALSA: cannot configure playback channel"); + return -1; + } + + /* check the rate, since that's rather important */ snd_pcm_hw_params_get_rate (driver->playback_hw_params, &pr, &dir); - } - if (device->capture_handle) { - snd_pcm_hw_params_get_rate (driver->capture_hw_params, - &cr, &dir); - } + snd_pcm_access_t access; + + err = snd_pcm_hw_params_get_period_size ( + driver->playback_hw_params, &p_period_size, &dir); + err = snd_pcm_hw_params_get_format ( + driver->playback_hw_params, + &(device->playback_sample_format)); + err = snd_pcm_hw_params_get_access (driver->playback_hw_params, + &access); + device->playback_interleaved = + (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) + || (access == SND_PCM_ACCESS_MMAP_COMPLEX); #endif + } - if (device->capture_handle && device->playback_handle) { - if (cr != pr) { - jack_error ("playback and capture sample rates do " - "not match (%d vs. %d)", pr, cr); + /* original checks done for single device mode */ + if (driver->devices_count == 1) { + if (device->capture_handle && device->playback_handle) { + if (cr != pr) { + jack_error ("playback and capture sample rates do " + "not match (%d vs. %d)", pr, cr); + } + /* only change if *both* capture and playback rates + * don't match requested certain hardware actually + * still works properly in full-duplex with slightly + * different rate values between adc and dac + */ + if (cr != driver->frame_rate && pr != driver->frame_rate) { + jack_error ("sample rate in use (%d Hz) does not " + "match requested rate (%d Hz)", + cr, driver->frame_rate); + driver->frame_rate = cr; + } } - - /* only change if *both* capture and playback rates - * don't match requested certain hardware actually - * still works properly in full-duplex with slightly - * different rate values between adc and dac - */ - if (cr != driver->frame_rate && pr != driver->frame_rate) { - jack_error ("sample rate in use (%d Hz) does not " - "match requested rate (%d Hz)", - cr, driver->frame_rate); + else if (device->capture_handle && cr != driver->frame_rate) { + jack_error ("capture sample rate in use (%d Hz) does not " + "match requested rate (%d Hz)", + cr, driver->frame_rate); driver->frame_rate = cr; } - - } - else if (device->capture_handle && cr != driver->frame_rate) { - jack_error ("capture sample rate in use (%d Hz) does not " - "match requested rate (%d Hz)", - cr, driver->frame_rate); - driver->frame_rate = cr; - } - else if (device->playback_handle && pr != driver->frame_rate) { - jack_error ("playback sample rate in use (%d Hz) does not " - "match requested rate (%d Hz)", - pr, driver->frame_rate); - driver->frame_rate = pr; + else if (device->playback_handle && pr != driver->frame_rate) { + jack_error ("playback sample rate in use (%d Hz) does not " + "match requested rate (%d Hz)", + pr, driver->frame_rate); + driver->frame_rate = pr; + } + } else { + if (do_capture && cr != driver->frame_rate) { + jack_error ("capture sample rate in use (%d Hz) does not " + "match requested rate (%d Hz)", + cr, driver->frame_rate); + return -1; + } + if (do_playback && pr != driver->frame_rate) { + jack_error ("playback sample rate in use (%d Hz) does not " + "match requested rate (%d Hz)", + pr, driver->frame_rate); + return -1; + } } /* check the fragment size, since that's non-negotiable */ - if (device->playback_handle) { -#ifndef __QNXNTO__ - snd_pcm_access_t access; - - err = snd_pcm_hw_params_get_period_size ( - driver->playback_hw_params, &p_period_size, &dir); - err = snd_pcm_hw_params_get_format ( - driver->playback_hw_params, - &(device->playback_sample_format)); - err = snd_pcm_hw_params_get_access (driver->playback_hw_params, - &access); - device->playback_interleaved = - (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) - || (access == SND_PCM_ACCESS_MMAP_COMPLEX); -#endif + if (do_playback) { if (p_period_size != driver->frames_per_cycle) { jack_error ("alsa_pcm: requested an interrupt every %" PRIu32 @@ -1025,20 +1073,8 @@ alsa_driver_set_parameters (alsa_driver_t *driver, #endif } - if (device->capture_handle) { + if (do_capture) { #ifndef __QNXNTO__ - snd_pcm_access_t access; - - err = snd_pcm_hw_params_get_period_size ( - driver->capture_hw_params, &c_period_size, &dir); - err = snd_pcm_hw_params_get_format ( - driver->capture_hw_params, - &(device->capture_sample_format)); - err = snd_pcm_hw_params_get_access (driver->capture_hw_params, - &access); - device->capture_interleaved = - (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) - || (access == SND_PCM_ACCESS_MMAP_COMPLEX); #endif if (c_period_size != driver->frames_per_cycle) { jack_error ("alsa_pcm: requested an interrupt every %" @@ -1065,7 +1101,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, alsa_driver_set_sample_bytes(driver, device); - if (device->playback_handle) { + if (do_playback) { err = alsa_driver_check_format(device->playback_sample_format); if(err < 0) { jack_error ("programming error: unhandled format " @@ -1074,7 +1110,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, } } - if (device->capture_handle) { + if (do_capture) { err = alsa_driver_check_format(device->capture_sample_format); if(err < 0) { jack_error ("programming error: unhandled format " @@ -1083,7 +1119,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, } } - if (device->playback_interleaved) { + if (device->playback_interleaved && do_playback) { #ifndef __QNXNTO__ const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames; @@ -1102,13 +1138,12 @@ alsa_driver_set_parameters (alsa_driver_t *driver, device->interleave_unit = snd_pcm_format_width( device->playback_sample_format) / 8; #endif - } else { - + } else if (do_playback) { device->interleave_unit = 0; /* NOT USED */ } #ifndef __QNXNTO__ - if (device->capture_interleaved) { + if (device->capture_interleaved && do_capture) { const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames; if (snd_pcm_mmap_begin(device->capture_handle, @@ -1119,68 +1154,67 @@ alsa_driver_set_parameters (alsa_driver_t *driver, } } #endif - if (device->playback_nchannels > device->capture_nchannels) { - device->max_nchannels = device->playback_nchannels; - device->user_nchannels = device->capture_nchannels; - } else { - device->max_nchannels = device->capture_nchannels; - device->user_nchannels = device->playback_nchannels; - } - /* device local channel offset to offsets in driver, used by Jack2 */ - device->capture_channel_offset = driver->capture_nchannels; - device->playback_channel_offset = driver->playback_nchannels; + /* do only on first start */ + if (device->max_nchannels == 0) { + alsa_driver_setup_io_function_pointers (driver, device); - driver->capture_nchannels += device->capture_nchannels; - driver->playback_nchannels += device->playback_nchannels; + /* Allocate and initialize structures that rely on the + channels counts. - alsa_driver_setup_io_function_pointers (driver, device); + Set up the bit pattern that is used to record which + channels require action on every cycle. any bits that are + not set after the engine's process() call indicate channels + that potentially need to be silenced. + */ - /* Allocate and initialize structures that rely on the - channels counts. + device->max_nchannels = device->playback_nchannels > device->capture_nchannels ? + device->playback_nchannels : device->capture_nchannels; - Set up the bit pattern that is used to record which - channels require action on every cycle. any bits that are - not set after the engine's process() call indicate channels - that potentially need to be silenced. - */ + /* device local channel offset to offsets in driver, used by Jack2 */ + device->capture_channel_offset = driver->capture_nchannels; + device->playback_channel_offset = driver->playback_nchannels; - bitset_create (&device->channels_done, device->max_nchannels); - bitset_create (&device->channels_not_done, device->max_nchannels); + driver->capture_nchannels += device->capture_nchannels; + driver->playback_nchannels += device->playback_nchannels; - if (device->playback_handle) { - device->playback_addr = (char **) - malloc (sizeof (char *) * device->playback_nchannels); - memset (device->playback_addr, 0, - sizeof (char *) * device->playback_nchannels); - device->playback_interleave_skip = (unsigned long *) - malloc (sizeof (unsigned long *) * device->playback_nchannels); - memset (device->playback_interleave_skip, 0, - sizeof (unsigned long *) * device->playback_nchannels); - device->silent = (unsigned long *) - malloc (sizeof (unsigned long) - * device->playback_nchannels); + bitset_create (&device->channels_done, device->max_nchannels); + bitset_create (&device->channels_not_done, device->max_nchannels); - for (chn = 0; chn < device->playback_nchannels; chn++) { - device->silent[chn] = 0; - } + if (device->playback_handle) { + device->playback_addr = (char **) + malloc (sizeof (char *) * device->playback_nchannels); + memset (device->playback_addr, 0, + sizeof (char *) * device->playback_nchannels); + device->playback_interleave_skip = (unsigned long *) + malloc (sizeof (unsigned long *) * device->playback_nchannels); + memset (device->playback_interleave_skip, 0, + sizeof (unsigned long *) * device->playback_nchannels); + device->silent = (unsigned long *) + malloc (sizeof (unsigned long) + * device->playback_nchannels); + + for (chn = 0; chn < device->playback_nchannels; chn++) { + device->silent[chn] = 0; + } - for (chn = 0; chn < device->playback_nchannels; chn++) { - bitset_add (device->channels_done, chn); - } + for (chn = 0; chn < device->playback_nchannels; chn++) { + bitset_add (device->channels_done, chn); + } - driver->dither_state = (dither_state_t *) calloc (device->playback_nchannels, sizeof (dither_state_t)); - } + driver->dither_state = (dither_state_t *) calloc (device->playback_nchannels, sizeof (dither_state_t)); + } - if (device->capture_handle) { - device->capture_addr = (char **) - malloc (sizeof (char *) * device->capture_nchannels); - memset (device->capture_addr, 0, - sizeof (char *) * device->capture_nchannels); - device->capture_interleave_skip = (unsigned long *) - malloc (sizeof (unsigned long *) * device->capture_nchannels); - memset (device->capture_interleave_skip, 0, - sizeof (unsigned long *) * device->capture_nchannels); + if (device->capture_handle) { + device->capture_addr = (char **) + malloc (sizeof (char *) * device->capture_nchannels); + memset (device->capture_addr, 0, + sizeof (char *) * device->capture_nchannels); + device->capture_interleave_skip = (unsigned long *) + malloc (sizeof (unsigned long *) * device->capture_nchannels); + memset (device->capture_interleave_skip, 0, + sizeof (unsigned long *) * device->capture_nchannels); + } } driver->period_usecs = @@ -1213,11 +1247,13 @@ alsa_driver_reset_parameters (alsa_driver_t *driver, { int err = 0; + jack_info ("reset parameters"); + /* XXX unregister old ports ? */ for (int i = 0; i < driver->devices_count; ++i) { alsa_device_t *device = &driver->devices[i]; alsa_driver_release_channel_dependent_memory (driver, device); - if ((err = alsa_driver_set_parameters (driver, device, frames_per_cycle, user_nperiods, rate)) != 0) { + if ((err = alsa_driver_set_parameters (driver, device, 1, 1, frames_per_cycle, user_nperiods, rate)) != 0) { return err; } } @@ -1339,6 +1375,49 @@ alsa_driver_stream_start(snd_pcm_t *pcm, bool is_capture) } #endif +int +alsa_driver_open (alsa_driver_t *driver) +{ + int err = 0; + + driver->poll_last = 0; + driver->poll_next = 0; + + for (int i = 0; i < driver->devices_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + int do_capture = 0, do_playback = 0; + + if (!device->capture_handle && (i devices_c_count)) { + err = alsa_driver_open_device (driver, &driver->devices[i], SND_PCM_STREAM_CAPTURE); + if (err < 0) { + jack_error ("\n\nATTENTION: Opening of the capture device \"%s\" failed.", + driver->devices[i].capture_name); + return -1; + } + + do_capture = 1; + } + + if (!device->playback_handle && (i devices_p_count)) { + err = alsa_driver_open_device (driver, &driver->devices[i], SND_PCM_STREAM_PLAYBACK); + if (err < 0) { + jack_error ("\n\nATTENTION: Opening of the playback device \"%s\" failed.", + driver->devices[i].playback_name); + return -1; + } + + do_playback = 1; + } + + if (alsa_driver_set_parameters (driver, device, do_capture, do_playback, driver->frames_per_cycle, driver->user_nperiods, driver->frame_rate)) { + jack_error ("ALSA: failed to set parameters"); + return -1; + } + } + + return 0; +} + int alsa_driver_start (alsa_driver_t *driver) { @@ -1346,30 +1425,51 @@ alsa_driver_start (alsa_driver_t *driver) snd_pcm_uframes_t poffset, pavail; channel_t chn; - driver->poll_last = 0; - driver->poll_next = 0; + driver->capture_nfds = 0; + driver->playback_nfds = 0; + + int group_done = 0; for (int i = 0; i < driver->devices_c_count; ++i) { alsa_device_t *device = &driver->devices[i]; - if (device->capture_handle && !device->capture_linked) { - jack_log("prepare device %s", device->capture_name); - if ((err = alsa_driver_prepare (device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { - jack_error ("ALSA: failed to prepare device '%s' (%s)", device->capture_name, snd_strerror(err)); - return -1; - } + if (!device->capture_handle) { + continue; + } + + // TODO: amiartus, devices with target state PREPARED should also be prepared, however, + // this makes sense only if alsa_driver_stop alsa_driver_close do not close all devices + // as done in current implementation, once those functions are updated it makes sense to keep + // devices in PREPARED state so they can be started faster on Restart request + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + + driver->capture_nfds += snd_pcm_poll_descriptors_count (device->capture_handle); + } + + if ((err = alsa_driver_prepare (device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { + jack_error ("ALSA: failed to prepare device '%s' (%s)", device->capture_name, snd_strerror(err)); + return -1; } } for (int i = 0; i < driver->devices_p_count; ++i) { alsa_device_t *device = &driver->devices[i]; - if (device->playback_handle && !device->playback_linked) { - jack_log("prepare device %s", device->playback_name); - if ((err = alsa_driver_prepare (device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { - jack_error ("ALSA: failed to prepare device '%s' (%s)", device->playback_name, snd_strerror(err)); - return -1; - } + if (!device->playback_handle) { + continue; + } + + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + + driver->playback_nfds += snd_pcm_poll_descriptors_count (device->playback_handle); + + if ((err = alsa_driver_prepare (device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { + jack_error ("ALSA: failed to prepare device '%s' (%s)", device->playback_name, snd_strerror(err)); + return -1; } } @@ -1388,19 +1488,6 @@ alsa_driver_start (alsa_driver_t *driver) // } // } - driver->capture_nfds = 0; - driver->playback_nfds = 0; - - for (int i = 0; i < driver->devices_count; ++i) { - alsa_device_t *device = &driver->devices[i]; - if (device->capture_handle) { - driver->capture_nfds += snd_pcm_poll_descriptors_count (device->capture_handle); - } - if (device->playback_handle) { - driver->playback_nfds += snd_pcm_poll_descriptors_count (device->playback_handle); - } - } - if (driver->pfd) { free (driver->pfd); } @@ -1414,10 +1501,15 @@ alsa_driver_start (alsa_driver_t *driver) for (int i = 0; i < driver->devices_p_count; ++i) { alsa_device_t *device = &driver->devices[i]; + if (!device->playback_handle) { continue; } + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + const jack_nframes_t silence_frames = driver->frames_per_cycle * driver->playback_nperiods; /* fill playback buffer with zeroes, and mark @@ -1468,25 +1560,37 @@ alsa_driver_start (alsa_driver_t *driver) for (int i = 0; i < driver->devices_c_count; ++i) { alsa_device_t *device = &driver->devices[i]; - if (device->capture_handle && !device->capture_linked) { - jack_log("start device %s", device->capture_name); - if ((err = alsa_driver_stream_start (device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { - jack_error ("ALSA: failed to start device C: '%s' (%s)", device->capture_name, - snd_strerror(err)); - return -1; - } + + if (!device->capture_handle) { + continue; + } + + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + + if ((err = alsa_driver_stream_start (device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { + jack_error ("ALSA: failed to start device C: '%s' (%s)", device->capture_name, + snd_strerror(err)); + return -1; } } for (int i = 0; i < driver->devices_p_count; ++i) { alsa_device_t *device = &driver->devices[i]; - if (device->playback_handle && !device->playback_linked) { - jack_log("start device %s", device->playback_name); - if ((err = alsa_driver_stream_start (device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { - jack_error ("ALSA: failed to start device P: '%s' (%s)", device->playback_name, - snd_strerror(err)); - return -1; - } + + if (!device->playback_handle) { + continue; + } + + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + + if ((err = alsa_driver_stream_start (device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { + jack_error ("ALSA: failed to start device P: '%s' (%s)", device->playback_name, + snd_strerror(err)); + return -1; } } @@ -1522,35 +1626,40 @@ alsa_driver_stop (alsa_driver_t *driver) // JACK2 ClearOutput(); - for (int i = 0; i < driver->devices_count; ++i) { + + for (int i = 0; i < driver->devices_c_count; ++i) { alsa_device_t *device = &driver->devices[i]; + if (!device->capture_handle) { + continue; + } - if (device->playback_handle && !device->playback_linked) { #ifdef __QNXNTO__ - /* In case of playback: Drain discards the frames */ - err = snd_pcm_plugin_playback_drain(device->playback_handle); + /* In case of capture: Flush discards the frames */ + err = snd_pcm_plugin_flush(device->capture_handle, SND_PCM_CHANNEL_CAPTURE); #else - jack_info("flush device %s", device->playback_name); - err = snd_pcm_drop (device->playback_handle); + err = snd_pcm_drop (device->capture_handle); #endif - if (err < 0) { - jack_error ("ALSA: failed to flush device (%s)", snd_strerror (err)); - return -1; - } + if (err < 0) { + jack_error ("ALSA: failed to flush device (%s)", snd_strerror (err)); + return -1; + } + } + + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (!device->playback_handle) { + continue; } - if (device->capture_handle && !device->capture_linked) { #ifdef __QNXNTO__ - /* In case of capture: Flush discards the frames */ - err = snd_pcm_plugin_flush(device->capture_handle, SND_PCM_CHANNEL_CAPTURE); + /* In case of playback: Drain discards the frames */ + err = snd_pcm_plugin_playback_drain(device->playback_handle); #else - jack_info("flush device %s", device->capture_name); - err = snd_pcm_drop (device->capture_handle); + err = snd_pcm_drop (device->playback_handle); #endif - if (err < 0) { - jack_error ("ALSA: failed to flush device (%s)", snd_strerror (err)); - return -1; - } + if (err < 0) { + jack_error ("ALSA: failed to flush device (%s)", snd_strerror (err)); + return -1; } } @@ -1565,6 +1674,32 @@ alsa_driver_stop (alsa_driver_t *driver) return 0; } +int +alsa_driver_close (alsa_driver_t *driver) +{ + for (int i = 0; i < driver->devices_c_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (!device->capture_handle) { + continue; + } + + snd_pcm_close(device->capture_handle); + device->capture_handle = NULL; + } + + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (!device->playback_handle) { + continue; + } + + snd_pcm_close(device->playback_handle); + device->playback_handle = NULL; + } + + return 0; +} + static int alsa_driver_restart (alsa_driver_t *driver) { @@ -1576,7 +1711,7 @@ alsa_driver_restart (alsa_driver_t *driver) if ((res = driver->nt_stop((struct _jack_driver_nt *) driver))==0) res = driver->nt_start((struct _jack_driver_nt *) driver); */ - res = Restart(); + res = Restart(); driver->xrun_recovery = 0; if (res && driver->midi) @@ -1743,7 +1878,29 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float */ int pfd_count = driver->capture_nfds + driver->playback_nfds; - do { + /* special case where all devices are stopped */ + if (pfd_count == 0) { + + driver->poll_last = jack_get_microseconds (); + + if (driver->poll_next > driver->poll_last) { + struct timespec duration, remain; + duration.tv_sec = 0; + duration.tv_nsec = (int64_t) ((driver->poll_next - driver->poll_last) * 1000); + nanosleep(&duration, &remain); + + driver->poll_last = jack_get_microseconds (); + } + + SetTime(driver->poll_last); + driver->poll_next = driver->poll_last + driver->period_usecs; + + *status = 0; + + return INT_MAX; + } + + while (pfd_count > 0) { int poll_result; int pfd_index = 0; @@ -1753,7 +1910,16 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float if (cap_revents[i]) { continue; } + alsa_device_t *device = &driver->devices[i]; + if (!device->capture_handle) { + continue; + } + + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + pfd_cap_count[i] = alsa_driver_poll_descriptors (device->capture_handle, &driver->pfd[pfd_index], pfd_count - pfd_index, @@ -1778,7 +1944,16 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float if (play_revents[i]) { continue; } + alsa_device_t *device = &driver->devices[i]; + if (!device->playback_handle) { + continue; + } + + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + pfd_play_count[i] = alsa_driver_poll_descriptors (device->playback_handle, &driver->pfd[pfd_index], pfd_count - pfd_index, @@ -1895,7 +2070,15 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float if (cap_revents[i]) { continue; } + alsa_device_t *device = &driver->devices[i]; + if (!device->capture_handle) { + continue; + } + + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + continue; + } unsigned short collect_revs = 0; if (snd_pcm_poll_descriptors_revents (device->capture_handle, &driver->pfd[pfd_index], @@ -1904,11 +2087,12 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *status = -6; return 0; } + pfd_index += pfd_cap_count[i]; if (collect_revs & (POLLERR | POLLIN)) { if (collect_revs & POLLERR) { /* optimization, no point in polling more if we already have xrun on one device */ - jack_error ("ALSA: device reported xrun C: '%s'", device->capture_name); + jack_error ("xrun C: '%s'", device->capture_name); *status = alsa_driver_xrun_recovery (driver, delayed_usecs); return 0; } @@ -1925,7 +2109,15 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float if (play_revents[i]) { continue; } + alsa_device_t *device = &driver->devices[i]; + if (!device->playback_handle) { + continue; + } + + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + continue; + } unsigned short collect_revs = 0; if (snd_pcm_poll_descriptors_revents (device->playback_handle, &driver->pfd[pfd_index], @@ -1934,11 +2126,12 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *status = -6; return 0; } + pfd_index += pfd_play_count[i]; if (collect_revs & (POLLERR | POLLOUT)) { if (collect_revs & POLLERR) { /* optimization, no point in polling more if we already have xrun on one device */ - jack_error ("ALSA: device reported xrun P: '%s'", device->playback_name); + jack_error ("xrun P: '%s'", device->playback_name); *status = alsa_driver_xrun_recovery (driver, delayed_usecs); return 0; } @@ -1954,7 +2147,7 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float pfd_count -= pfd_play_count[i]; } } - } while (pfd_count > 0); + } /* TODO: amiartus; I assume all devices are snd_pcm_link-ed and running on the same clock source, * therefore should have the same avail frames, however in practice, this might have to be reworked, @@ -1965,8 +2158,16 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float for (int i = 0; i < driver->devices_c_count; ++i) { alsa_device_t *device = &driver->devices[i]; - snd_pcm_sframes_t avail = 0; + if (!device->capture_handle) { + continue; + } + + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + + snd_pcm_sframes_t avail = 0; if ((avail = alsa_driver_avail (driver, device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { if (avail == -EPIPE) { jack_error ("ALSA: avail_update xrun on capture dev '%s'", device->capture_name); @@ -1976,7 +2177,6 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float jack_error ("unknown ALSA avail_update return value (%u)", capture_avail); } } - jack_log("ALSA: avail frames C: %s %ld", device->capture_name, avail); capture_avail = capture_avail < avail ? capture_avail : avail; } @@ -1984,8 +2184,16 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float for (int i = 0; i < driver->devices_p_count; ++i) { alsa_device_t *device = &driver->devices[i]; - snd_pcm_sframes_t avail = 0; + if (!device->playback_handle) { + continue; + } + + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + + snd_pcm_sframes_t avail = 0; if ((avail = alsa_driver_avail (driver, device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { if (avail == -EPIPE) { jack_error ("ALSA: avail_update xrun on playback dev '%s'", device->playback_name); @@ -1995,12 +2203,25 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float jack_error ("unknown ALSA avail_update return value (%u)", playback_avail); } } - jack_log("ALSA: avail frames P: %s %ld", device->playback_name, avail); playback_avail = playback_avail < avail ? playback_avail : avail; } + /* mark all channels not done for now. read/write will change this */ + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + if (!device->playback_handle) { + continue; + } + + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + + bitset_copy (device->channels_not_done, device->channels_done); + } + *status = 0; - driver->last_wait_ust = poll_ret; avail = capture_avail < playback_avail ? capture_avail : playback_avail; @@ -2010,12 +2231,6 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float avail, playback_avail, capture_avail); #endif - /* mark all channels not done for now. read/write will change this */ - for (int i = 0; i < driver->devices_p_count; ++i) { - alsa_device_t *device = &driver->devices[i]; - bitset_copy (device->channels_not_done, device->channels_done); - } - /* constrain the available count to the nearest (round down) number of periods. */ @@ -2038,6 +2253,14 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) for (size_t i = 0; i < driver->devices_c_count; ++i) { alsa_device_t *device = &driver->devices[i]; + if (!device->capture_handle) { + continue; + } + + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + nread = 0; contiguous = 0; jack_nframes_t frames_remain = nframes; @@ -2117,6 +2340,13 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) for (size_t i = 0; i < driver->devices_p_count; ++i) { alsa_device_t *device = &driver->devices[i]; + if (!device->playback_handle) { + continue; + } + + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + continue; + } if (driver->midi) (driver->midi->write)(driver->midi, nframes); @@ -2433,7 +2663,7 @@ discover_alsa_using_apps () } static int -alsa_driver_open (alsa_driver_t *driver, alsa_device_t *device, bool is_capture) +alsa_driver_open_device (alsa_driver_t *driver, alsa_device_t *device, bool is_capture) { int err = 0; char* current_apps; @@ -2614,43 +2844,23 @@ alsa_driver_new (char *name, alsa_driver_info_t info, jack_client_t *client) driver->devices_count = info.devices_capture_size > info.devices_playback_size ? info.devices_capture_size : info.devices_playback_size; driver->devices = (alsa_device_t*) calloc(driver->devices_count, sizeof(*driver->devices)); - if (driver->devices_count == 1) { - driver->devices[0].capture_nchannels = info.devices[0].capture_channels; - driver->devices[0].playback_nchannels = info.devices[0].playback_channels; - } + driver->frame_rate = info.frame_rate; + driver->frames_per_cycle = info.frames_per_period; + driver->user_nperiods = info.periods_n; - for (int i = 0; i < driver->devices_c_count; ++i) { - driver->devices[i].capture_sample_bytes = (info.shorts_first ? 2:4); - driver->devices[i].capture_name = strdup(info.devices[i].capture_name); + driver->features = info.features; - err = alsa_driver_open(driver, &driver->devices[i], SND_PCM_STREAM_CAPTURE); - if (err < 0) { - jack_error ("\n\nATTENTION: Opening of the capture device \"%s\" failed.", - driver->devices[i].capture_name); - alsa_driver_delete (driver); - return NULL; - } - } - - for (int i = 0; i < driver->devices_p_count; ++i) { - driver->devices[i].playback_sample_bytes = (info.shorts_first ? 2:4); - driver->devices[i].playback_name = strdup(info.devices[i].playback_name); - -#ifndef __QNXNTO__ - if (alsa_driver_check_card_type (driver, &driver->devices[i])) { - alsa_driver_delete (driver); - return NULL; + for (int i = 0; i < driver->devices_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (i < driver->devices_c_count) { + device->capture_sample_bytes = (info.shorts_first ? 2:4); + device->capture_name = strdup(info.devices[i].capture_name); + device->capture_nchannels = info.devices[i].capture_channels; } - - alsa_driver_hw_specific (driver, &driver->devices[i], driver->hw_monitoring, driver->hw_metering); -#endif - - err = alsa_driver_open(driver, &driver->devices[i], SND_PCM_STREAM_PLAYBACK); - if (err < 0) { - jack_error ("\n\nATTENTION: Opening of the playback device \"%s\" failed.", - driver->devices[i].playback_name); - alsa_driver_delete (driver); - return NULL; + if (i < driver->devices_p_count) { + device->playback_sample_bytes = (info.shorts_first ? 2:4); + device->playback_name = strdup(info.devices[i].playback_name); + device->playback_nchannels = info.devices[i].playback_channels; } } @@ -2660,7 +2870,7 @@ alsa_driver_new (char *name, alsa_driver_info_t info, jack_client_t *client) driver->playback_sw_params = 0; driver->capture_sw_params = 0; - if (driver->devices[0].playback_handle) { + if (driver->devices_p_count) { if ((err = snd_pcm_hw_params_malloc ( &driver->playback_hw_params)) < 0) { jack_error ("ALSA: could not allocate playback hw" @@ -2678,7 +2888,7 @@ alsa_driver_new (char *name, alsa_driver_info_t info, jack_client_t *client) } } - if (driver->devices[0].capture_handle) { + if (driver->devices_c_count) { if ((err = snd_pcm_hw_params_malloc ( &driver->capture_hw_params)) < 0) { jack_error ("ALSA: could not allocate capture hw" @@ -2696,53 +2906,20 @@ alsa_driver_new (char *name, alsa_driver_info_t info, jack_client_t *client) } } #endif - for (int i = 0; i < driver->devices_count; ++i) { - alsa_device_t *device = &driver->devices[i]; - - if (alsa_driver_set_parameters (driver, device, info.frames_per_period, info.periods_n, info.frame_rate)) { - jack_error ("ALSA: failed to set parameters"); - alsa_driver_delete (driver); - return NULL; - } - } - for (int i = 0; i < driver->devices_c_count; ++i) { - alsa_device_t *device = &driver->devices[i]; - - device->capture_linked = 0; - - if (i == 0) { - continue; - } - - if (snd_pcm_link (driver->devices[0].capture_handle, device->capture_handle) != 0) { - jack_error ("failed to add device to link group C: '%s'", device->capture_name); - continue; - } - device->capture_linked = 1; - } - - for (int i = 0; i < driver->devices_p_count; ++i) { - alsa_device_t *device = &driver->devices[i]; + driver->client = client; - device->playback_linked = 0; - snd_pcm_t *handle = driver->devices[0].capture_handle; - if (!handle) { - if (i == 0) { - continue; - } - handle = driver->devices[0].playback_handle; +#ifndef __QNXNTO__ + for (int i = 0; i < driver->devices_count; ++i) { + if (alsa_driver_check_card_type (driver, &driver->devices[i])) { + alsa_driver_delete(driver); + return NULL; } - if (snd_pcm_link (handle, device->playback_handle) != 0) { - jack_error ("failed to add device to link group P: '%s'", device->playback_name); - continue; - } - device->playback_linked = 1; + alsa_driver_hw_specific (driver, &driver->devices[i], info.hw_monitoring, info.hw_metering); } - - driver->client = client; +#endif return (jack_driver_t *) driver; } diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h index 61b1e92e9..17aa31f43 100644 --- a/linux/alsa/alsa_driver.h +++ b/linux/alsa/alsa_driver.h @@ -54,8 +54,10 @@ #define SND_PCM_FORMAT_S32_LE SND_PCM_SFMT_S32_LE #define SND_PCM_FORMAT_S32_BE SND_PCM_SFMT_S32_BE #define SND_PCM_FORMAT_FLOAT_LE SND_PCM_SFMT_FLOAT_LE +#define SND_PCM_STATE_PREPARED SND_PCM_STATUS_PREPARED #define SND_PCM_STATE_SUSPENDED SND_PCM_STATUS_SUSPENDED #define SND_PCM_STATE_XRUN SND_PCM_STATUS_UNDERRUN +#define SND_PCM_STATE_RUNNING SND_PCM_STATUS_RUNNING #define SND_PCM_STREAM_PLAYBACK SND_PCM_CHANNEL_PLAYBACK #define SND_PCM_STREAM_CAPTURE SND_PCM_CHANNEL_CAPTURE @@ -128,13 +130,17 @@ typedef struct _alsa_device { unsigned long playback_sample_bytes; unsigned long capture_sample_bytes; - /* is this device linked to first device */ + /* device is 'snd_pcm_link' to a group, only 1 group of linked devices is allowed */ int capture_linked; int playback_linked; int capture_xrun_count; int playback_xrun_count; + /* desired state of device, decided by JackAlsaDriver */ + int capture_target_state; + int playback_target_state; + jack_hardware_t *hw; char *alsa_driver; } alsa_device_t; @@ -198,6 +204,8 @@ typedef struct _alsa_driver { int devices_count; int devices_c_count; int devices_p_count; + + int features; } alsa_driver_t; typedef struct _alsa_device_info { @@ -232,6 +240,8 @@ typedef struct _alsa_driver_info { int hw_metering; int monitor; int soft_mode; + + int features; } alsa_driver_info_t; static inline void @@ -311,12 +321,18 @@ alsa_driver_new (char *name, alsa_driver_info_t info, jack_client_t *client); void alsa_driver_delete (alsa_driver_t *driver); +int +alsa_driver_open (alsa_driver_t *driver); + int alsa_driver_start (alsa_driver_t *driver); int alsa_driver_stop (alsa_driver_t *driver); +int +alsa_driver_close (alsa_driver_t *driver); + jack_nframes_t alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delayed_usecs); @@ -334,6 +350,7 @@ void MonitorInput(); void ClearOutput(); void WriteOutput(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten); void SetTime(jack_time_t time); + int Restart(); #ifdef __cplusplus From b88a85ac8d8fa97e0053b7f50ca1fd8d16d2dc4f Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Tue, 1 Oct 2019 18:47:22 +0200 Subject: [PATCH 19/42] jack alsa: implement restart request [2/3] in case ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS is defined unused devices are closed on restart request, otherwise unused devices will be kept in prepared state Change-Id: I0cd91deb957a4839c5ea8c679bdabe1d0a3c6720 Signed-off-by: Adam Miartus (cherry picked from commit ee4fe3fde6902c41a3a086bba20a75f37ae96373) Signed-off-by: Timo Wischer --- linux/alsa/JackAlsaDriver.cpp | 10 ++++++++++ linux/alsa/alsa_driver.c | 4 ++-- linux/alsa/alsa_driver.h | 5 +++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index fb976f7af..7d812720a 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -564,6 +564,8 @@ int JackAlsaDriver::TargetState(int init, int connections_count) state = SND_PCM_STATE_RUNNING; } else if (init) { state = SND_PCM_STATE_RUNNING; + } else if (driver->features & ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS) { + state = SND_PCM_STATE_NOTREADY; } else { state = SND_PCM_STATE_PREPARED; } @@ -858,6 +860,9 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () "ALSA MIDI driver", NULL); + value.i = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "close-idle-devs", 'c', JackDriverParamBool, &value, NULL, "Close idle devices on alsa driver restart request", NULL); + return desc; } @@ -1043,6 +1048,11 @@ SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLocke free(info.midi_name); info.midi_name = strdup(param->value.str); break; + + case 'c': + info.features |= param->value.i ? ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS : 0; + break; + } } diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 371dc5870..738ee7ecc 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -1387,7 +1387,7 @@ alsa_driver_open (alsa_driver_t *driver) alsa_device_t *device = &driver->devices[i]; int do_capture = 0, do_playback = 0; - if (!device->capture_handle && (i devices_c_count)) { + if (!device->capture_handle && (i devices_c_count) && (device->capture_target_state != SND_PCM_STATE_NOTREADY)) { err = alsa_driver_open_device (driver, &driver->devices[i], SND_PCM_STREAM_CAPTURE); if (err < 0) { jack_error ("\n\nATTENTION: Opening of the capture device \"%s\" failed.", @@ -1398,7 +1398,7 @@ alsa_driver_open (alsa_driver_t *driver) do_capture = 1; } - if (!device->playback_handle && (i devices_p_count)) { + if (!device->playback_handle && (i devices_p_count) && (device->playback_target_state != SND_PCM_STATE_NOTREADY)) { err = alsa_driver_open_device (driver, &driver->devices[i], SND_PCM_STREAM_PLAYBACK); if (err < 0) { jack_error ("\n\nATTENTION: Opening of the playback device \"%s\" failed.", diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h index 17aa31f43..91022b257 100644 --- a/linux/alsa/alsa_driver.h +++ b/linux/alsa/alsa_driver.h @@ -58,14 +58,19 @@ #define SND_PCM_STATE_SUSPENDED SND_PCM_STATUS_SUSPENDED #define SND_PCM_STATE_XRUN SND_PCM_STATUS_UNDERRUN #define SND_PCM_STATE_RUNNING SND_PCM_STATUS_RUNNING +#define SND_PCM_STATE_NOTREADY SND_PCM_STATUS_NOTREADY #define SND_PCM_STREAM_PLAYBACK SND_PCM_CHANNEL_PLAYBACK #define SND_PCM_STREAM_CAPTURE SND_PCM_CHANNEL_CAPTURE typedef unsigned long snd_pcm_uframes_t; typedef signed long snd_pcm_sframes_t; typedef int32_t alsa_driver_default_format_t; +#else +#define SND_PCM_STATE_NOTREADY (SND_PCM_STATE_LAST + 1) #endif +#define ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS (1 << 1) + #ifdef __cplusplus extern "C" { From 418f4edab2fc9ce364eaf382d0b2d0f0ae0bc38b Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Tue, 1 Oct 2019 18:40:38 +0200 Subject: [PATCH 20/42] jack alsa: implement restart request [3/3] in case ALSA_DRIVER_FEAT_LINK_DEVS is defined, driver will attempt to snd_pcm_link all devices Change-Id: I7bd54b8414d0b614786bee1078667a9aa595b58f Signed-off-by: Adam Miartus (cherry picked from commit da45a136f556e1aeb3b12d3f10e4ba7687c389c8) Signed-off-by: Timo Wischer --- linux/alsa/JackAlsaDriver.cpp | 6 ++ linux/alsa/alsa_driver.c | 135 ++++++++++++++++++++++++++++++++++ linux/alsa/alsa_driver.h | 1 + 3 files changed, 142 insertions(+) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index 7d812720a..caa141a9f 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -863,6 +863,9 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () value.i = 0; jack_driver_descriptor_add_parameter(desc, &filler, "close-idle-devs", 'c', JackDriverParamBool, &value, NULL, "Close idle devices on alsa driver restart request", NULL); + value.i = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "unlinked-devs", 'u', JackDriverParamBool, &value, NULL, "Do not link devices", NULL); + return desc; } @@ -1053,6 +1056,9 @@ SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLocke info.features |= param->value.i ? ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS : 0; break; + case 'u': + info.features |= param->value.i ? ALSA_DRIVER_FEAT_UNLINKED_DEVS : 0; + break; } } diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 738ee7ecc..98dad1a29 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -1415,6 +1415,81 @@ alsa_driver_open (alsa_driver_t *driver) } } + if (driver->features & ALSA_DRIVER_FEAT_UNLINKED_DEVS) { + jack_info ("alsa driver linking disabled"); + return 0; + } else { + jack_info ("alsa driver linking enabled"); + } + + snd_pcm_t *group_handle = NULL; + + for (int i = 0; i < driver->devices_c_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + if (!device->capture_handle) { + continue; + } + + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + + if (group_handle == NULL) { + group_handle = device->capture_handle; + device->capture_linked = 1; + continue; + } + + if (device->capture_linked) { + continue; + } + + if (group_handle == device->capture_handle) { + device->capture_linked = 1; + continue; + } + + if (snd_pcm_link (group_handle, device->capture_handle) != 0) { + jack_error ("failed to add device to link group C: '%s'", device->capture_name); + continue; + } + device->capture_linked = 1; + } + + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + if (!device->playback_handle) { + continue; + } + + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + + if (group_handle == NULL) { + group_handle = device->playback_handle; + device->playback_linked = 1; + continue; + } + + if (device->playback_linked) { + continue; + } + + if (group_handle == device->playback_handle) { + device->playback_linked = 1; + continue; + } + + if (snd_pcm_link (group_handle, device->playback_handle) != 0) { + jack_error ("failed to add device to link group P: '%s'", device->playback_name); + continue; + } + device->playback_linked = 1; + } + return 0; } @@ -1446,6 +1521,13 @@ alsa_driver_start (alsa_driver_t *driver) } driver->capture_nfds += snd_pcm_poll_descriptors_count (device->capture_handle); + + if (group_done && device->capture_linked) { + continue; + } + + if (device->capture_linked) { + group_done = 1; } if ((err = alsa_driver_prepare (device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { @@ -1467,6 +1549,14 @@ alsa_driver_start (alsa_driver_t *driver) driver->playback_nfds += snd_pcm_poll_descriptors_count (device->playback_handle); + if (group_done && device->playback_linked) { + continue; + } + + if (device->playback_linked) { + group_done = 1; + } + if ((err = alsa_driver_prepare (device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { jack_error ("ALSA: failed to prepare device '%s' (%s)", device->playback_name, snd_strerror(err)); return -1; @@ -1558,6 +1648,8 @@ alsa_driver_start (alsa_driver_t *driver) #endif } + group_done = 0; + for (int i = 0; i < driver->devices_c_count; ++i) { alsa_device_t *device = &driver->devices[i]; @@ -1569,6 +1661,14 @@ alsa_driver_start (alsa_driver_t *driver) continue; } + if (group_done && device->capture_linked) { + continue; + } + + if (device->capture_linked) { + group_done = 1; + } + if ((err = alsa_driver_stream_start (device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { jack_error ("ALSA: failed to start device C: '%s' (%s)", device->capture_name, snd_strerror(err)); @@ -1587,6 +1687,14 @@ alsa_driver_start (alsa_driver_t *driver) continue; } + if (group_done && device->playback_linked) { + continue; + } + + if (device->playback_linked) { + group_done = 1; + } + if ((err = alsa_driver_stream_start (device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { jack_error ("ALSA: failed to start device P: '%s' (%s)", device->playback_name, snd_strerror(err)); @@ -1626,6 +1734,7 @@ alsa_driver_stop (alsa_driver_t *driver) // JACK2 ClearOutput(); + int group_done = 0; for (int i = 0; i < driver->devices_c_count; ++i) { alsa_device_t *device = &driver->devices[i]; @@ -1633,6 +1742,14 @@ alsa_driver_stop (alsa_driver_t *driver) continue; } + if (group_done && device->capture_linked) { + continue; + } + + if (device->capture_linked) { + group_done = 1; + } + #ifdef __QNXNTO__ /* In case of capture: Flush discards the frames */ err = snd_pcm_plugin_flush(device->capture_handle, SND_PCM_CHANNEL_CAPTURE); @@ -1651,6 +1768,14 @@ alsa_driver_stop (alsa_driver_t *driver) continue; } + if (group_done && device->playback_linked) { + continue; + } + + if (device->playback_linked) { + group_done = 1; + } + #ifdef __QNXNTO__ /* In case of playback: Drain discards the frames */ err = snd_pcm_plugin_playback_drain(device->playback_handle); @@ -1683,6 +1808,11 @@ alsa_driver_close (alsa_driver_t *driver) continue; } + if (device->capture_linked) { + snd_pcm_unlink(device->capture_handle); + device->capture_linked = 0; + } + snd_pcm_close(device->capture_handle); device->capture_handle = NULL; } @@ -1693,6 +1823,11 @@ alsa_driver_close (alsa_driver_t *driver) continue; } + if (device->playback_linked) { + snd_pcm_unlink(device->playback_handle); + device->playback_linked = 0; + } + snd_pcm_close(device->playback_handle); device->playback_handle = NULL; } diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h index 91022b257..1dca99d05 100644 --- a/linux/alsa/alsa_driver.h +++ b/linux/alsa/alsa_driver.h @@ -70,6 +70,7 @@ typedef int32_t alsa_driver_default_format_t; #endif #define ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS (1 << 1) +#define ALSA_DRIVER_FEAT_UNLINKED_DEVS (1 << 2) #ifdef __cplusplus extern "C" From f45f3cc62b171f3b9bc0ca9b339b0a517a147f60 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Wed, 16 Oct 2019 17:25:45 +0200 Subject: [PATCH 21/42] jack alsa: move linking functionality to separate function Change-Id: I607eba702288677f8401c396c93d9c2d790cd362 Signed-off-by: Adam Miartus (cherry picked from commit efe557426bb9d7fe88f37be27df0f8d681fc8d44) Signed-off-by: Timo Wischer --- linux/alsa/alsa_driver.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 98dad1a29..c93297145 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -59,6 +59,7 @@ char* strcasestr(const char* haystack, const char* needle); /* Max re-try count for Alsa poll timeout handling */ #define MAX_RETRY_COUNT 5 +static int alsa_driver_link (alsa_driver_t *driver); static int alsa_driver_open_device (alsa_driver_t *driver, alsa_device_t *device, bool is_capture); void @@ -1422,6 +1423,14 @@ alsa_driver_open (alsa_driver_t *driver) jack_info ("alsa driver linking enabled"); } + alsa_driver_link(driver); + + return 0; +} + +static int +alsa_driver_link (alsa_driver_t *driver) +{ snd_pcm_t *group_handle = NULL; for (int i = 0; i < driver->devices_c_count; ++i) { From 316aee4a8e3e5849d60b251aabae7f93ca415bb5 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Thu, 17 Oct 2019 14:43:07 +0200 Subject: [PATCH 22/42] jack alsa: do not return early allows additional logic to be added to function later Change-Id: I30a8de53faa66883a7976f69ccc2b312d9f7ecce Signed-off-by: Adam Miartus (cherry picked from commit 2fb38bca8d19f777a20e6124a41bdfffc9e6a1a2) Signed-off-by: Timo Wischer --- linux/alsa/alsa_driver.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index c93297145..431917553 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -1416,15 +1416,13 @@ alsa_driver_open (alsa_driver_t *driver) } } - if (driver->features & ALSA_DRIVER_FEAT_UNLINKED_DEVS) { - jack_info ("alsa driver linking disabled"); - return 0; - } else { + if (!(driver->features & ALSA_DRIVER_FEAT_UNLINKED_DEVS)) { jack_info ("alsa driver linking enabled"); + alsa_driver_link(driver); + } else { + jack_info ("alsa driver linking disabled"); } - alsa_driver_link(driver); - return 0; } From 3de4858c5b75126a80985069cdd21c64bbc755b1 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Mon, 30 Sep 2019 14:46:32 +0200 Subject: [PATCH 23/42] jack alsa: add array_string_t flags allow discard duplicate array members as a configurable feature Change-Id: Ibbbfed9ec5633d2bdd23cc516f726ff4ad98c4ed Signed-off-by: Adam Miartus (cherry picked from commit 65c157936ae94024d9bcd29e82080f53ccee6613) Signed-off-by: Timo Wischer --- linux/alsa/JackAlsaDriver.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index caa141a9f..fa643f3f0 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -871,6 +871,11 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () struct array_string_t { + enum flags { + none, + discard_duplicate, + }; + uint64_t size; char **data; }; @@ -891,7 +896,7 @@ void array_string_free(struct array_string_t *obj) obj->size = 0; } -struct array_string_t array_string_split(const char *str, const char sep) +struct array_string_t array_string_split(const char *str, const char sep, array_string_t::flags flags = array_string_t::none) { struct array_string_t result; result.size = 0; @@ -911,7 +916,7 @@ struct array_string_t array_string_split(const char *str, const char sep) std::vector drivers; while (std::getline(stream, driver, sep)) { driver.erase(std::remove_if(driver.begin(), driver.end(), isspace), driver.end()); - if (std::find(drivers.begin(), drivers.end(), driver) != drivers.end()) + if (std::find(drivers.begin(), drivers.end(), driver) != drivers.end() && (flags & array_string_t::discard_duplicate)) continue; char *str = (char*) calloc(JACK_CLIENT_NAME_SIZE + 1, sizeof(char)); strncpy(str, driver.c_str(), JACK_CLIENT_NAME_SIZE); @@ -1078,13 +1083,13 @@ SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLocke struct array_string_t capture_names = {}; if (capture_names_param) { - capture_names = array_string_split(capture_names_param, ' '); + capture_names = array_string_split(capture_names_param, ' ', array_string_t::discard_duplicate); free(capture_names_param); } struct array_string_t playback_names = {}; if (playback_names_param) { - playback_names = array_string_split(playback_names_param, ' '); + playback_names = array_string_split(playback_names_param, ' ', array_string_t::discard_duplicate); free(playback_names_param); } From 9a25d224a35d63815b426bcc3829aa9b279d62be Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Mon, 30 Sep 2019 14:57:52 +0200 Subject: [PATCH 24/42] jack alsa: reoganize code for readability Change-Id: I3d79e5f2516d8709b70dc700e5c9a04534619661 Signed-off-by: Adam Miartus (cherry picked from commit 7a7d3b3524877533c0d5a9d3490d862d4323d280) Signed-off-by: Timo Wischer --- linux/alsa/JackAlsaDriver.cpp | 42 ++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index fa643f3f0..35dbb2ef9 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -347,31 +347,33 @@ int JackAlsaDriver::Open(alsa_driver_info_t info) #endif fDriver = alsa_driver_new ((char*)"alsa_pcm", info, NULL); + if (!fDriver) { + Close(); + return -1; + } - if (fDriver) { - /* we need to initialize variables for all devices, mainly channels count since this is required by Jack to setup ports */ - UpdateDriverTargetState(1); - if (alsa_driver_open((alsa_driver_t *)fDriver) < 0) { - Close(); - return -1; - } - // ALSA driver may have changed the in/out values - fCaptureChannels = ((alsa_driver_t *)fDriver)->capture_nchannels; - fPlaybackChannels = ((alsa_driver_t *)fDriver)->playback_nchannels; -#ifndef __QNXNTO__ - if (JackServerGlobals::on_device_reservation_loop != NULL) { - device_reservation_loop_running = true; - if (JackPosixThread::StartImp(&fReservationLoopThread, 0, 0, on_device_reservation_loop, NULL) != 0) { - device_reservation_loop_running = false; - } - } -#endif + /* we need to initialize variables for all devices, mainly channels count since this is required by Jack to setup ports */ + UpdateDriverTargetState(1); - return 0; - } else { + if (alsa_driver_open((alsa_driver_t *)fDriver) < 0) { Close(); return -1; } + + // ALSA driver may have changed the in/out values + fCaptureChannels = ((alsa_driver_t *)fDriver)->capture_nchannels; + fPlaybackChannels = ((alsa_driver_t *)fDriver)->playback_nchannels; + +#ifndef __QNXNTO__ + if (JackServerGlobals::on_device_reservation_loop != NULL) { + device_reservation_loop_running = true; + if (JackPosixThread::StartImp(&fReservationLoopThread, 0, 0, on_device_reservation_loop, NULL) != 0) { + device_reservation_loop_running = false; + } + } +#endif + + return 0; } int JackAlsaDriver::Close() From 99bb68b8a200f189aac192394e04370fb6b1eefd Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Wed, 2 Oct 2019 13:25:10 +0200 Subject: [PATCH 25/42] jack alsa: implement ALSA_DRIVER_FEAT_START_CLOSED if enabled with '-x' option starts jackd with all devices closed, when configured, user must provide number of channels for all devices, otherwise we would not be able to create any jack ports. we need to create all jack ports, since the only way to start any device in this condition is to connect a jack port and issue a call jack_client_reload_master Change-Id: I63736eff20a2f2db7405ad512fec11e7bd0a9860 Signed-off-by: Adam Miartus (cherry picked from commit 55d03f05d05e438f89f8e57658c7593d3ec56a41) Signed-off-by: Timo Wischer --- linux/alsa/JackAlsaDriver.cpp | 80 +++++++++++++++++++++-------------- linux/alsa/alsa_driver.c | 11 ++--- linux/alsa/alsa_driver.h | 1 + 3 files changed, 55 insertions(+), 37 deletions(-) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index 35dbb2ef9..35bf7814d 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -360,9 +360,29 @@ int JackAlsaDriver::Open(alsa_driver_info_t info) return -1; } - // ALSA driver may have changed the in/out values - fCaptureChannels = ((alsa_driver_t *)fDriver)->capture_nchannels; - fPlaybackChannels = ((alsa_driver_t *)fDriver)->playback_nchannels; + /* this feature caused driver to skip opening alsa devices, therefore populate channels from user input */ + if (info.features & ALSA_DRIVER_FEAT_START_CLOSED) { + for (size_t i = 0; i < std::max(info.devices_capture_size, info.devices_playback_size); ++i) { + if (i < info.devices_capture_size && info.devices[i].capture_channels < 1) { + jack_error ("invalid or missing channels parameter with '-x' option 'start-closed' for device C: '%s'", info.devices[i].capture_name); + Close(); + return -1; + } + + if (i < info.devices_playback_size && info.devices[i].playback_channels < 1) { + jack_error ("invalid or missing channels parameter with '-x' option 'start-closed' for device P: '%s'", info.devices[i].playback_name); + Close(); + return -1; + } + + fCaptureChannels += info.devices[i].capture_channels; + fPlaybackChannels += info.devices[i].playback_channels; + } + /* in case we really opened alsa devices, channel information is generated by driver */ + } else { + fCaptureChannels = ((alsa_driver_t *)fDriver)->capture_nchannels; + fPlaybackChannels = ((alsa_driver_t *)fDriver)->playback_nchannels; + } #ifndef __QNXNTO__ if (JackServerGlobals::on_device_reservation_loop != NULL) { @@ -565,7 +585,11 @@ int JackAlsaDriver::TargetState(int init, int connections_count) if (connections_count > 0) { state = SND_PCM_STATE_RUNNING; } else if (init) { - state = SND_PCM_STATE_RUNNING; + if (driver->features & ALSA_DRIVER_FEAT_START_CLOSED) { + state = SND_PCM_STATE_NOTREADY; + } else { + state = SND_PCM_STATE_RUNNING; + } } else if (driver->features & ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS) { state = SND_PCM_STATE_NOTREADY; } else { @@ -838,8 +862,8 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () NULL); strcpy(value.str, "none"); - jack_driver_descriptor_add_parameter(desc, &filler, "inchannels", 'i', JackDriverParamString, &value, NULL, "List of device capture channels (defaults to hardware max)", NULL); - jack_driver_descriptor_add_parameter(desc, &filler, "outchannels", 'o', JackDriverParamString, &value, NULL, "List of device playback channels (defaults to hardware max)", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "inchannels", 'i', JackDriverParamString, &value, NULL, "List of device capture channels (defaults to hw max)", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "outchannels", 'o', JackDriverParamString, &value, NULL, "List of device playback channels (defaults to hw max)", NULL); value.i = FALSE; jack_driver_descriptor_add_parameter(desc, &filler, "shorts", 'S', JackDriverParamBool, &value, NULL, "Try 16-bit samples before 32-bit", NULL); @@ -862,6 +886,9 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () "ALSA MIDI driver", NULL); + value.i = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "start-closed", 'x', JackDriverParamBool, &value, NULL, "Start with all devices closed", NULL); + value.i = 0; jack_driver_descriptor_add_parameter(desc, &filler, "close-idle-devs", 'c', JackDriverParamBool, &value, NULL, "Close idle devices on alsa driver restart request", NULL); @@ -878,8 +905,7 @@ struct array_string_t discard_duplicate, }; - uint64_t size; - char **data; + std::vector data; }; void array_string_free(struct array_string_t *obj) @@ -887,49 +913,35 @@ void array_string_free(struct array_string_t *obj) if (obj == NULL) { return; } - if (obj->data == NULL) { - return; - } - for (size_t i = 0; i < obj->size; ++i) { + for (size_t i = 0; i < obj->data.size(); ++i) { free(obj->data[i]); } - free(obj->data); - obj->data = NULL; - obj->size = 0; } struct array_string_t array_string_split(const char *str, const char sep, array_string_t::flags flags = array_string_t::none) { struct array_string_t result; - result.size = 0; std::stringstream stream; stream << std::string(str); if (stream.str().find(sep) == std::string::npos) { - result.data = (char**) calloc(1, sizeof(char*)); - result.data[0] = (char*) calloc(JACK_CLIENT_NAME_SIZE + 1, sizeof(char)); - result.size = 1; + result.data.push_back((char*) calloc(JACK_CLIENT_NAME_SIZE + 1, sizeof(char))); strncpy(result.data[0], str, JACK_CLIENT_NAME_SIZE); result.data[0][JACK_CLIENT_NAME_SIZE] = '\0'; return result; } std::string driver; - std::vector drivers; while (std::getline(stream, driver, sep)) { driver.erase(std::remove_if(driver.begin(), driver.end(), isspace), driver.end()); - if (std::find(drivers.begin(), drivers.end(), driver) != drivers.end() && (flags & array_string_t::discard_duplicate)) + if (std::find(result.data.begin(), result.data.end(), driver) != result.data.end() && (flags & array_string_t::discard_duplicate)) continue; char *str = (char*) calloc(JACK_CLIENT_NAME_SIZE + 1, sizeof(char)); strncpy(str, driver.c_str(), JACK_CLIENT_NAME_SIZE); str[JACK_CLIENT_NAME_SIZE] = '\0'; - drivers.push_back(str); + result.data.push_back(str); } - result.data = (char**) calloc(driver.size(), sizeof(char*)); - result.size = drivers.size(); - memcpy(result.data, drivers.data(), result.size * sizeof(char*)); - return result; } @@ -1059,6 +1071,10 @@ SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLocke info.midi_name = strdup(param->value.str); break; + case 'x': + info.features |= param->value.i ? ALSA_DRIVER_FEAT_START_CLOSED : 0; + break; + case 'c': info.features |= param->value.i ? ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS : 0; break; @@ -1107,20 +1123,20 @@ SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLocke free(playback_channels_param); } - info.devices_capture_size = capture_names.size; - info.devices_playback_size = playback_names.size; + info.devices_capture_size = capture_names.data.size(); + info.devices_playback_size = playback_names.data.size(); info.devices = (alsa_device_info_t*) calloc(std::max(info.devices_capture_size, info.devices_playback_size), sizeof(alsa_device_info_t)); for (size_t i = 0; i < std::max(info.devices_capture_size, info.devices_playback_size); ++i) { - if (i < capture_names.size) { + if (i < capture_names.data.size()) { info.devices[i].capture_name = strdup(capture_names.data[i]); } - if (i < capture_channels.size) { + if (i < capture_channels.data.size()) { info.devices[i].capture_channels = atoi(capture_channels.data[i]); } - if (i < playback_names.size) { + if (i < playback_names.data.size()) { info.devices[i].playback_name = strdup(playback_names.data[i]); } - if (i < playback_channels.size) { + if (i < playback_channels.data.size()) { info.devices[i].playback_channels = atoi(playback_channels.data[i]); } } diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 431917553..080d9dbb7 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -1156,9 +1156,10 @@ alsa_driver_set_parameters (alsa_driver_t *driver, } #endif + alsa_driver_setup_io_function_pointers (driver, device); + /* do only on first start */ if (device->max_nchannels == 0) { - alsa_driver_setup_io_function_pointers (driver, device); /* Allocate and initialize structures that rely on the channels counts. @@ -1182,7 +1183,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, bitset_create (&device->channels_done, device->max_nchannels); bitset_create (&device->channels_not_done, device->max_nchannels); - if (device->playback_handle) { + if (device->playback_name) { device->playback_addr = (char **) malloc (sizeof (char *) * device->playback_nchannels); memset (device->playback_addr, 0, @@ -1206,7 +1207,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->dither_state = (dither_state_t *) calloc (device->playback_nchannels, sizeof (dither_state_t)); } - if (device->capture_handle) { + if (device->capture_name) { device->capture_addr = (char **) malloc (sizeof (char *) * device->capture_nchannels); memset (device->capture_addr, 0, @@ -1388,7 +1389,7 @@ alsa_driver_open (alsa_driver_t *driver) alsa_device_t *device = &driver->devices[i]; int do_capture = 0, do_playback = 0; - if (!device->capture_handle && (i devices_c_count) && (device->capture_target_state != SND_PCM_STATE_NOTREADY)) { + if (!device->capture_handle && (i < driver->devices_c_count) && (device->capture_target_state != SND_PCM_STATE_NOTREADY)) { err = alsa_driver_open_device (driver, &driver->devices[i], SND_PCM_STREAM_CAPTURE); if (err < 0) { jack_error ("\n\nATTENTION: Opening of the capture device \"%s\" failed.", @@ -1399,7 +1400,7 @@ alsa_driver_open (alsa_driver_t *driver) do_capture = 1; } - if (!device->playback_handle && (i devices_p_count) && (device->playback_target_state != SND_PCM_STATE_NOTREADY)) { + if (!device->playback_handle && (i < driver->devices_p_count) && (device->playback_target_state != SND_PCM_STATE_NOTREADY)) { err = alsa_driver_open_device (driver, &driver->devices[i], SND_PCM_STREAM_PLAYBACK); if (err < 0) { jack_error ("\n\nATTENTION: Opening of the playback device \"%s\" failed.", diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h index 1dca99d05..d5bf24f54 100644 --- a/linux/alsa/alsa_driver.h +++ b/linux/alsa/alsa_driver.h @@ -69,6 +69,7 @@ typedef int32_t alsa_driver_default_format_t; #define SND_PCM_STATE_NOTREADY (SND_PCM_STATE_LAST + 1) #endif +#define ALSA_DRIVER_FEAT_START_CLOSED (1 << 0) #define ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS (1 << 1) #define ALSA_DRIVER_FEAT_UNLINKED_DEVS (1 << 2) From 9a3bdd6142a4a9a55b9b64c7d6412be048447a96 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Wed, 2 Oct 2019 15:35:31 +0200 Subject: [PATCH 26/42] jack alsa: reintroduce special hardware handling Change-Id: I9c4724ca8b0b464dc3b38bac9f1fa6d8b05c186e Signed-off-by: Adam Miartus (cherry picked from commit 73a34ffc49a9ca7c6fb6f291c365ac80859d44c7) Signed-off-by: Timo Wischer --- linux/alsa/alsa_driver.c | 98 ++++++++++++++++++++++++++-------------- linux/alsa/alsa_driver.h | 8 ++-- linux/alsa/generic.h | 2 +- linux/alsa/generic_hw.c | 2 +- linux/alsa/hammerfall.c | 20 ++++---- linux/alsa/hammerfall.h | 4 +- linux/alsa/hdsp.c | 6 +-- linux/alsa/hdsp.h | 4 +- linux/alsa/ice1712.c | 8 ++-- linux/alsa/ice1712.h | 4 +- 10 files changed, 94 insertions(+), 62 deletions(-) diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 080d9dbb7..5e2912893 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -198,13 +198,13 @@ alsa_driver_check_card_type (alsa_driver_t *driver, alsa_device_t *device) // XXX: I don't know the "right" way to do this. Which to use // driver->alsa_name_playback or driver->alsa_name_capture. - if ((err = snd_ctl_open (&driver->ctl_handle, ctl_name, 0)) < 0) { + if ((err = snd_ctl_open (&device->ctl_handle, ctl_name, 0)) < 0) { jack_error ("control open \"%s\" (%s)", ctl_name, snd_strerror(err)); - } else if ((err = snd_ctl_card_info(driver->ctl_handle, card_info)) < 0) { + } else if ((err = snd_ctl_card_info(device->ctl_handle, card_info)) < 0) { jack_error ("control hardware info \"%s\" (%s)", device->playback_name, snd_strerror (err)); - snd_ctl_close (driver->ctl_handle); + snd_ctl_close (device->ctl_handle); } device->alsa_driver = strdup(snd_ctl_card_info_get_driver (card_info)); @@ -217,30 +217,30 @@ alsa_driver_check_card_type (alsa_driver_t *driver, alsa_device_t *device) static int alsa_driver_hammerfall_hardware (alsa_driver_t *driver, alsa_device_t *device) { - device->hw = jack_alsa_hammerfall_hw_new (driver); + device->hw = jack_alsa_hammerfall_hw_new (device); return 0; } static int alsa_driver_hdsp_hardware (alsa_driver_t *driver, alsa_device_t *device) { - device->hw = jack_alsa_hdsp_hw_new (driver); + device->hw = jack_alsa_hdsp_hw_new (device); return 0; } static int alsa_driver_ice1712_hardware (alsa_driver_t *driver, alsa_device_t *device) { - device->hw = jack_alsa_ice1712_hw_new (driver); + device->hw = jack_alsa_ice1712_hw_new (device); return 0; } // JACK2 /* static int -alsa_driver_usx2y_hardware (alsa_driver_t *driver) +alsa_driver_usx2y_hardware (alsa_driver_t *driver, alsa_device_t *device) { - driver->hw = jack_alsa_usx2y_hw_new (driver); + driver->hw = jack_alsa_usx2y_hw_new (device); return 0; } */ @@ -248,7 +248,7 @@ alsa_driver_usx2y_hardware (alsa_driver_t *driver) static int alsa_driver_generic_hardware (alsa_driver_t *driver, alsa_device_t *device) { - device->hw = jack_alsa_generic_hw_new (driver); + device->hw = jack_alsa_generic_hw_new (device); return 0; } @@ -2635,22 +2635,9 @@ alsa_driver_clock_sync_status (channel_t chn) void alsa_driver_delete (alsa_driver_t *driver) { - JSList *node; - if (driver->midi) (driver->midi->destroy)(driver->midi); - for (node = driver->clock_sync_listeners; node; - node = jack_slist_next (node)) { - free (node->data); - } - jack_slist_free (driver->clock_sync_listeners); - - if (driver->ctl_handle) { - snd_ctl_close (driver->ctl_handle); - driver->ctl_handle = 0; - } - for (int i = 0; i < driver->devices_count; ++i) { if (driver->devices[i].capture_handle) { snd_pcm_close (driver->devices[i].capture_handle); @@ -2660,6 +2647,12 @@ alsa_driver_delete (alsa_driver_t *driver) if (driver->devices[i].playback_handle) { snd_pcm_close (driver->devices[i].playback_handle); driver->devices[i].playback_handle = 0; +#ifndef __QNXNTO__ + for (JSList *node = driver->devices[i].clock_sync_listeners; node; node = jack_slist_next (node)) { + free (node->data); + } + jack_slist_free (driver->devices[i].clock_sync_listeners); +#endif } free(driver->devices[i].capture_name); @@ -2672,6 +2665,11 @@ alsa_driver_delete (alsa_driver_t *driver) driver->devices[i].hw->release (driver->devices[i].hw); driver->devices[i].hw = 0; } + + if (driver->devices[i].ctl_handle) { + snd_ctl_close (driver->devices[i].ctl_handle); + driver->devices[i].ctl_handle = 0; + } } #ifndef __QNXNTO__ @@ -2955,7 +2953,6 @@ alsa_driver_new (char *name, alsa_driver_info_t info, jack_client_t *client) driver->nt_run_cycle = (JackDriverNTRunCycleFunction) alsa_driver_run_cycle; */ - driver->ctl_handle = 0; driver->capture_frame_latency = info.capture_latency; driver->playback_frame_latency = info.playback_latency; @@ -2972,9 +2969,6 @@ alsa_driver_new (char *name, alsa_driver_info_t info, jack_client_t *client) driver->dither = info.dither; driver->soft_mode = info.soft_mode; - pthread_mutex_init (&driver->clock_sync_lock, 0); - driver->clock_sync_listeners = 0; - driver->poll_late = 0; driver->xrun_count = 0; driver->process_count = 0; @@ -3054,7 +3048,10 @@ alsa_driver_new (char *name, alsa_driver_info_t info, jack_client_t *client) #ifndef __QNXNTO__ - for (int i = 0; i < driver->devices_count; ++i) { + for (int i = 0; i < driver->devices_p_count; ++i) { + pthread_mutex_init (&driver->devices[i].clock_sync_lock, 0); + driver->devices[i].clock_sync_listeners = 0; + if (alsa_driver_check_card_type (driver, &driver->devices[i])) { alsa_driver_delete(driver); return NULL; @@ -3068,29 +3065,62 @@ alsa_driver_new (char *name, alsa_driver_info_t info, jack_client_t *client) } int -alsa_driver_stop_listening_to_clock_sync_status (alsa_driver_t *driver, - unsigned int which) +alsa_driver_listen_for_clock_sync_status (alsa_device_t *device, + ClockSyncListenerFunction func, + void *arg) +{ + ClockSyncListener *csl; + + csl = (ClockSyncListener *) malloc (sizeof (ClockSyncListener)); + csl->function = func; + csl->arg = arg; + csl->id = device->next_clock_sync_listener_id++; + + pthread_mutex_lock (&device->clock_sync_lock); + device->clock_sync_listeners = + jack_slist_prepend (device->clock_sync_listeners, csl); + pthread_mutex_unlock (&device->clock_sync_lock); + return csl->id; +} +int +alsa_driver_stop_listening_to_clock_sync_status (alsa_device_t *device, + unsigned int which) { JSList *node; int ret = -1; - pthread_mutex_lock (&driver->clock_sync_lock); - for (node = driver->clock_sync_listeners; node; + pthread_mutex_lock (&device->clock_sync_lock); + for (node = device->clock_sync_listeners; node; node = jack_slist_next (node)) { if (((ClockSyncListener *) node->data)->id == which) { - driver->clock_sync_listeners = + device->clock_sync_listeners = jack_slist_remove_link ( - driver->clock_sync_listeners, node); + device->clock_sync_listeners, node); free (node->data); jack_slist_free_1 (node); ret = 0; break; } } - pthread_mutex_unlock (&driver->clock_sync_lock); + pthread_mutex_unlock (&device->clock_sync_lock); return ret; } +void +alsa_device_clock_sync_notify (alsa_device_t *device, channel_t chn, + ClockSyncStatus status) +{ + JSList *node; + + pthread_mutex_lock (&device->clock_sync_lock); + for (node = device->clock_sync_listeners; node; + node = jack_slist_next (node)) { + ClockSyncListener *csl = (ClockSyncListener *) node->data; + csl->function (chn, status, csl->arg); + } + pthread_mutex_unlock (&device->clock_sync_lock); +} + /* DRIVER "PLUGIN" INTERFACE */ const char driver_client_name[] = "alsa_pcm"; diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h index d5bf24f54..d62ddb8d7 100644 --- a/linux/alsa/alsa_driver.h +++ b/linux/alsa/alsa_driver.h @@ -149,7 +149,12 @@ typedef struct _alsa_device { int playback_target_state; jack_hardware_t *hw; + snd_ctl_t *ctl_handle; char *alsa_driver; + + JSList *clock_sync_listeners; + pthread_mutex_t clock_sync_lock; + unsigned long next_clock_sync_listener_id; } alsa_device_t; typedef struct _alsa_driver { @@ -179,7 +184,6 @@ typedef struct _alsa_driver { unsigned long user_nperiods; unsigned int playback_nperiods; unsigned int capture_nperiods; - snd_ctl_t *ctl_handle; jack_client_t *client; unsigned long input_monitor_mask; @@ -197,8 +201,6 @@ typedef struct _alsa_driver { dither_state_t *dither_state; SampleClockMode clock_mode; - JSList *clock_sync_listeners; - pthread_mutex_t clock_sync_lock; int poll_late; int xrun_count; diff --git a/linux/alsa/generic.h b/linux/alsa/generic.h index 4458aea42..d228d86f8 100644 --- a/linux/alsa/generic.h +++ b/linux/alsa/generic.h @@ -27,7 +27,7 @@ extern "C" #endif jack_hardware_t * - jack_alsa_generic_hw_new (alsa_driver_t *driver); + jack_alsa_generic_hw_new (alsa_device_t *device); #ifdef __cplusplus } diff --git a/linux/alsa/generic_hw.c b/linux/alsa/generic_hw.c index 30e7f3400..ccf246889 100644 --- a/linux/alsa/generic_hw.c +++ b/linux/alsa/generic_hw.c @@ -38,7 +38,7 @@ generic_release (jack_hardware_t *hw) } jack_hardware_t * -jack_alsa_generic_hw_new (alsa_driver_t *driver) +jack_alsa_generic_hw_new (alsa_device_t *device) { jack_hardware_t *hw; diff --git a/linux/alsa/hammerfall.c b/linux/alsa/hammerfall.c index 523540708..fe862d183 100644 --- a/linux/alsa/hammerfall.c +++ b/linux/alsa/hammerfall.c @@ -62,7 +62,7 @@ hammerfall_broadcast_channel_status_change (hammerfall_t *h, int lock, int sync, } for (chn = lowchn; chn < highchn; chn++) { - alsa_driver_set_clock_sync_status (h->driver, chn, status); + alsa_driver_set_clock_sync_status (h->device, chn, status); } } @@ -87,8 +87,8 @@ hammerfall_check_sync_state (hammerfall_t *h, int val, int adat_id) /* XXX broken! fix for hammerfall light ! */ - alsa_driver_set_clock_sync_status (h->driver, 24, status); - alsa_driver_set_clock_sync_status (h->driver, 25, status); + alsa_driver_set_clock_sync_status (h->device, 24, status); + alsa_driver_set_clock_sync_status (h->device, 25, status); h->said_that_spdif_is_fine = TRUE; } @@ -153,7 +153,7 @@ hammerfall_set_input_monitor_mask (jack_hardware_t *hw, unsigned long mask) snd_ctl_elem_value_set_integer (ctl, i, (mask & (1<driver->ctl_handle, ctl)) != 0) { + if ((err = snd_ctl_elem_write (h->device->ctl_handle, ctl)) != 0) { jack_error ("ALSA/Hammerfall: cannot set input monitoring (%s)", snd_strerror (err)); return -1; } @@ -188,7 +188,7 @@ hammerfall_change_sample_clock (jack_hardware_t *hw, SampleClockMode mode) break; } - if ((err = snd_ctl_elem_write (h->driver->ctl_handle, ctl)) < 0) { + if ((err = snd_ctl_elem_write (h->device->ctl_handle, ctl)) < 0) { jack_error ("ALSA-Hammerfall: cannot set clock mode"); } @@ -244,17 +244,17 @@ hammerfall_monitor_controls (void *arg) snd_ctl_elem_value_set_id (sw[2], switch_id[2]); while (1) { - if (snd_ctl_elem_read (h->driver->ctl_handle, sw[0])) { + if (snd_ctl_elem_read (h->device->ctl_handle, sw[0])) { jack_error ("cannot read control switch 0 ..."); } hammerfall_check_sync (h, sw[0]); - if (snd_ctl_elem_read (h->driver->ctl_handle, sw[1])) { + if (snd_ctl_elem_read (h->device->ctl_handle, sw[1])) { jack_error ("cannot read control switch 0 ..."); } hammerfall_check_sync (h, sw[1]); - if (snd_ctl_elem_read (h->driver->ctl_handle, sw[2])) { + if (snd_ctl_elem_read (h->device->ctl_handle, sw[2])) { jack_error ("cannot read control switch 0 ..."); } hammerfall_check_sync (h, sw[2]); @@ -269,7 +269,7 @@ hammerfall_monitor_controls (void *arg) #endif /* HAMMERFALL_MONITOR_CONTROLS */ jack_hardware_t * -jack_alsa_hammerfall_hw_new (alsa_driver_t *driver) +jack_alsa_hammerfall_hw_new (alsa_device_t *device) { jack_hardware_t *hw; hammerfall_t *h; @@ -293,7 +293,7 @@ jack_alsa_hammerfall_hw_new (alsa_driver_t *driver) h->lock_status[2] = FALSE; h->sync_status[2] = FALSE; h->said_that_spdif_is_fine = FALSE; - h->driver = driver; + h->device = device; h->monitor_interval.tv_sec = 1; h->monitor_interval.tv_nsec = 0; diff --git a/linux/alsa/hammerfall.h b/linux/alsa/hammerfall.h index aeb6eb76e..8b8b4eabf 100644 --- a/linux/alsa/hammerfall.h +++ b/linux/alsa/hammerfall.h @@ -29,7 +29,7 @@ typedef struct int sync_status[3]; int said_that_spdif_is_fine; pthread_t monitor_thread; - alsa_driver_t *driver; + alsa_device_t *device; struct timespec monitor_interval; } hammerfall_t; @@ -39,7 +39,7 @@ extern "C" { #endif - jack_hardware_t *jack_alsa_hammerfall_hw_new (alsa_driver_t *driver); + jack_hardware_t *jack_alsa_hammerfall_hw_new (alsa_device_t *device); #ifdef __cplusplus } diff --git a/linux/alsa/hdsp.c b/linux/alsa/hdsp.c index b399e17f8..eb222d295 100644 --- a/linux/alsa/hdsp.c +++ b/linux/alsa/hdsp.c @@ -115,7 +115,7 @@ static int hdsp_set_mixer_gain(jack_hardware_t *hw, int input_channel, snd_ctl_elem_value_set_integer (ctl, 2, gain); /* Commit the mixer value and check for errors */ - if ((err = snd_ctl_elem_write (h->driver->ctl_handle, ctl)) != 0) { + if ((err = snd_ctl_elem_write (h->device->ctl_handle, ctl)) != 0) { jack_error ("ALSA/HDSP: cannot set mixer gain (%s)", snd_strerror (err)); return -1; } @@ -206,7 +206,7 @@ hdsp_release (jack_hardware_t *hw) /* Mostly copied directly from hammerfall.c */ jack_hardware_t * -jack_alsa_hdsp_hw_new (alsa_driver_t *driver) +jack_alsa_hdsp_hw_new (alsa_device_t *device) { jack_hardware_t *hw; hdsp_t *h; @@ -227,7 +227,7 @@ jack_alsa_hdsp_hw_new (alsa_driver_t *driver) hw->get_hardware_power = hdsp_get_hardware_power; h = (hdsp_t *) malloc (sizeof (hdsp_t)); - h->driver = driver; + h->device = device; hw->private_hw = h; return hw; diff --git a/linux/alsa/hdsp.h b/linux/alsa/hdsp.h index fe5df84a6..c083fca09 100644 --- a/linux/alsa/hdsp.h +++ b/linux/alsa/hdsp.h @@ -25,7 +25,7 @@ typedef struct { - alsa_driver_t *driver; + alsa_device_t *device; } hdsp_t; @@ -35,7 +35,7 @@ extern "C" #endif jack_hardware_t * - jack_alsa_hdsp_hw_new (alsa_driver_t *driver); + jack_alsa_hdsp_hw_new (alsa_device_t *device); #ifdef __cplusplus } diff --git a/linux/alsa/ice1712.c b/linux/alsa/ice1712.c index 968922100..297da6599 100644 --- a/linux/alsa/ice1712.c +++ b/linux/alsa/ice1712.c @@ -47,7 +47,7 @@ ice1712_hw_monitor_toggle(jack_hardware_t *hw, int idx, int onoff) } else { snd_ctl_elem_value_set_enumerated (val, 0, 0); } - if ((err = snd_ctl_elem_write (h->driver->ctl_handle, val)) != 0) { + if ((err = snd_ctl_elem_write (h->device->ctl_handle, val)) != 0) { jack_error ("ALSA/ICE1712: (%d) cannot set input monitoring (%s)", idx,snd_strerror (err)); return -1; @@ -94,7 +94,7 @@ ice1712_release (jack_hardware_t *hw) jack_hardware_t * -jack_alsa_ice1712_hw_new (alsa_driver_t *driver) +jack_alsa_ice1712_hw_new (alsa_device_t *device) { jack_hardware_t *hw; ice1712_t *h; @@ -113,14 +113,14 @@ jack_alsa_ice1712_hw_new (alsa_driver_t *driver) h = (ice1712_t *) malloc (sizeof (ice1712_t)); - h->driver = driver; + h->device = device; /* Get the EEPROM (adopted from envy24control) */ h->eeprom = (ice1712_eeprom_t *) malloc (sizeof (ice1712_eeprom_t)); snd_ctl_elem_value_alloca (&val); snd_ctl_elem_value_set_interface (val, SND_CTL_ELEM_IFACE_CARD); snd_ctl_elem_value_set_name (val, "ICE1712 EEPROM"); - if ((err = snd_ctl_elem_read (driver->ctl_handle, val)) < 0) { + if ((err = snd_ctl_elem_read (device->ctl_handle, val)) < 0) { jack_error( "ALSA/ICE1712: Unable to read EEPROM contents (%s)\n", snd_strerror (err)); /* Recover? */ } diff --git a/linux/alsa/ice1712.h b/linux/alsa/ice1712.h index fb4220121..7b67e490e 100644 --- a/linux/alsa/ice1712.h +++ b/linux/alsa/ice1712.h @@ -59,7 +59,7 @@ ice1712_eeprom_t; typedef struct { - alsa_driver_t *driver; + alsa_device_t *device; ice1712_eeprom_t *eeprom; unsigned long active_channels; } @@ -70,7 +70,7 @@ extern "C" { #endif - jack_hardware_t *jack_alsa_ice1712_hw_new (alsa_driver_t *driver); + jack_hardware_t *jack_alsa_ice1712_hw_new (alsa_device_t *device); #ifdef __cplusplus } From 27a676c2dfb74a917c1d40664ce77bf56db7d739 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Wed, 16 Oct 2019 17:22:13 +0200 Subject: [PATCH 27/42] jack alsa: reformat to allow easier managing of priorities Change-Id: I68228ae98b4b074847abf547810e35e8e9267f31 Signed-off-by: Adam Miartus (cherry picked from commit d890538d322acbdd4c1b09b37a86b2bdb0f31d75) Signed-off-by: Timo Wischer --- linux/alsa/JackAlsaDriver.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index 35bf7814d..f5400eeb6 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -584,16 +584,21 @@ int JackAlsaDriver::TargetState(int init, int connections_count) if (connections_count > 0) { state = SND_PCM_STATE_RUNNING; - } else if (init) { + return state; + } + + if (init) { if (driver->features & ALSA_DRIVER_FEAT_START_CLOSED) { state = SND_PCM_STATE_NOTREADY; } else { state = SND_PCM_STATE_RUNNING; } - } else if (driver->features & ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS) { + return state; + } + + if (driver->features & ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS) { state = SND_PCM_STATE_NOTREADY; - } else { - state = SND_PCM_STATE_PREPARED; + return state; } return state; From e994144b2cd7569d9e777da6584169b748b16ae7 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Thu, 17 Oct 2019 14:36:26 +0200 Subject: [PATCH 28/42] jack alsa: add DriverMode as enum changes signature of function to allow extending the possible modes of TargetState() Change-Id: I394d85f4aac9fabb669fb03eedbfc3c6587a2a8e Signed-off-by: Adam Miartus (cherry picked from commit 9d215ce938556aead96db81126aa5c20be1aa5e3) Signed-off-by: Timo Wischer --- linux/alsa/JackAlsaDriver.cpp | 28 ++++++++++++---------------- linux/alsa/JackAlsaDriver.h | 9 +++++++-- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index f5400eeb6..4ea77ac39 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -353,7 +353,7 @@ int JackAlsaDriver::Open(alsa_driver_info_t info) } /* we need to initialize variables for all devices, mainly channels count since this is required by Jack to setup ports */ - UpdateDriverTargetState(1); + UpdateDriverTargetState(DriverMode::Init); if (alsa_driver_open((alsa_driver_t *)fDriver) < 0) { Close(); @@ -457,7 +457,7 @@ int JackAlsaDriver::Stop() int JackAlsaDriver::Reload() { - UpdateDriverTargetState(); + UpdateDriverTargetState(DriverMode::Runtime); alsa_driver_t* driver = (alsa_driver_t*) fDriver; if (alsa_driver_close (driver) < 0) { @@ -551,7 +551,7 @@ int JackAlsaDriver::PortSetDefaultMetadata(jack_port_id_t port_id, const char* p return fEngine->PortSetDefaultMetadata(fClientControl.fRefNum, port_id, pretty_name); } -int JackAlsaDriver::UpdateDriverTargetState(int init) +int JackAlsaDriver::UpdateDriverTargetState(DriverMode mode) { int c_list_index = 0, p_list_index = 0; alsa_driver_t* driver = (alsa_driver_t*) fDriver; @@ -564,44 +564,40 @@ int JackAlsaDriver::UpdateDriverTargetState(int init) capture_connections_count += fGraphManager->GetConnectionsNum(fCapturePortList[c_list_index]); c_list_index++; } - device->capture_target_state = TargetState(init, capture_connections_count); + device->capture_target_state = TargetState(mode, capture_connections_count); int playback_connections_count = 0; for (int j = 0; j < device->playback_nchannels; ++j) { playback_connections_count += fGraphManager->GetConnectionsNum(fPlaybackPortList[p_list_index]); p_list_index++; } - device->playback_target_state = TargetState(init, playback_connections_count); + device->playback_target_state = TargetState(mode, playback_connections_count); } return 0; } -int JackAlsaDriver::TargetState(int init, int connections_count) +int JackAlsaDriver::TargetState(DriverMode mode, int connections_count) { alsa_driver_t* driver = (alsa_driver_t*) fDriver; - int state = SND_PCM_STATE_PREPARED; if (connections_count > 0) { - state = SND_PCM_STATE_RUNNING; - return state; + return SND_PCM_STATE_RUNNING; } - if (init) { + if (mode == DriverMode::Init) { if (driver->features & ALSA_DRIVER_FEAT_START_CLOSED) { - state = SND_PCM_STATE_NOTREADY; + return SND_PCM_STATE_NOTREADY; } else { - state = SND_PCM_STATE_RUNNING; + return SND_PCM_STATE_RUNNING; } - return state; } if (driver->features & ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS) { - state = SND_PCM_STATE_NOTREADY; - return state; + return SND_PCM_STATE_NOTREADY; } - return state; + return SND_PCM_STATE_PREPARED; } void JackAlsaDriver::WriteOutputAux(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten) diff --git a/linux/alsa/JackAlsaDriver.h b/linux/alsa/JackAlsaDriver.h index bf9e679a9..110dbcde5 100644 --- a/linux/alsa/JackAlsaDriver.h +++ b/linux/alsa/JackAlsaDriver.h @@ -38,6 +38,11 @@ class JackAlsaDriver : public JackAudioDriver private: + enum DriverMode { + Init, + Runtime, + }; + jack_driver_t* fDriver; jack_native_thread_t fReservationLoopThread; @@ -80,9 +85,9 @@ class JackAlsaDriver : public JackAudioDriver int PortSetDefaultMetadata(jack_port_id_t port_id, const char* pretty_name); - int UpdateDriverTargetState(int init = 0); + int UpdateDriverTargetState(DriverMode mode); - int TargetState(int init, int connections_count); + int TargetState(DriverMode mode, int connections_count); // JACK API emulation for the midi driver int is_realtime() const; From eb1713719912ae632f3443524c9f988319d0cb0a Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Wed, 16 Oct 2019 17:23:59 +0200 Subject: [PATCH 29/42] jack alsa: add shutdown mode of operation for cases when we need to shutdown jack server or disconnect all devices Change-Id: Idb3bede50e9f5e86d5997d5f675724846226b0c5 Signed-off-by: Adam Miartus (cherry picked from commit 2d7d8b93d0268bc243a6ad83aa788b57ea6d480c) Signed-off-by: Timo Wischer --- linux/alsa/JackAlsaDriver.cpp | 5 +++++ linux/alsa/JackAlsaDriver.h | 1 + 2 files changed, 6 insertions(+) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index 4ea77ac39..afb75b26a 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -401,6 +401,7 @@ int JackAlsaDriver::Close() // Generic audio driver close int res = JackAudioDriver::Close(); + UpdateDriverTargetState(DriverMode::Shutdown); alsa_driver_close((alsa_driver_t *)fDriver); if (fDriver) { @@ -581,6 +582,10 @@ int JackAlsaDriver::TargetState(DriverMode mode, int connections_count) { alsa_driver_t* driver = (alsa_driver_t*) fDriver; + if (mode == DriverMode::Shutdown) { + return SND_PCM_STATE_NOTREADY; + } + if (connections_count > 0) { return SND_PCM_STATE_RUNNING; } diff --git a/linux/alsa/JackAlsaDriver.h b/linux/alsa/JackAlsaDriver.h index 110dbcde5..52fc47a87 100644 --- a/linux/alsa/JackAlsaDriver.h +++ b/linux/alsa/JackAlsaDriver.h @@ -41,6 +41,7 @@ class JackAlsaDriver : public JackAudioDriver enum DriverMode { Init, Runtime, + Shutdown, }; jack_driver_t* fDriver; From 950497d8096b1579d40eadda72a810fc14a17cbb Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Wed, 16 Oct 2019 17:25:20 +0200 Subject: [PATCH 30/42] jack alsa: optimize operation of reload request do not close alsa devices that should stay open do not prepare already prepared device do not drop frames on device that is not running Change-Id: Ibb6463bf6604a9e5e0038266bc99d2f8d838e135 Signed-off-by: Adam Miartus (cherry picked from commit f5f8d29ef0d6b934b0a2022e924dafc8894382a5) Signed-off-by: Timo Wischer --- linux/alsa/alsa_driver.c | 41 ++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 5e2912893..07729c6c1 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -61,6 +61,7 @@ char* strcasestr(const char* haystack, const char* needle); static int alsa_driver_link (alsa_driver_t *driver); static int alsa_driver_open_device (alsa_driver_t *driver, alsa_device_t *device, bool is_capture); +static int alsa_driver_get_state (snd_pcm_t *handle, int is_capture); void jack_driver_init (jack_driver_t *driver) @@ -1520,20 +1521,22 @@ alsa_driver_start (alsa_driver_t *driver) continue; } - // TODO: amiartus, devices with target state PREPARED should also be prepared, however, - // this makes sense only if alsa_driver_stop alsa_driver_close do not close all devices - // as done in current implementation, once those functions are updated it makes sense to keep - // devices in PREPARED state so they can be started faster on Restart request - if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + if (device->capture_target_state == SND_PCM_STATE_NOTREADY) { continue; } - driver->capture_nfds += snd_pcm_poll_descriptors_count (device->capture_handle); + if (device->capture_target_state == SND_PCM_STATE_RUNNING) { + driver->capture_nfds += snd_pcm_poll_descriptors_count (device->capture_handle); + } if (group_done && device->capture_linked) { continue; } + if (alsa_driver_get_state(device->capture_handle, 1) == SND_PCM_STATE_PREPARED) { + continue; + } + if (device->capture_linked) { group_done = 1; } @@ -1551,16 +1554,22 @@ alsa_driver_start (alsa_driver_t *driver) continue; } - if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + if (device->playback_target_state == SND_PCM_STATE_NOTREADY) { continue; } - driver->playback_nfds += snd_pcm_poll_descriptors_count (device->playback_handle); + if (device->playback_target_state == SND_PCM_STATE_RUNNING) { + driver->playback_nfds += snd_pcm_poll_descriptors_count (device->playback_handle); + } if (group_done && device->playback_linked) { continue; } + if (alsa_driver_get_state(device->playback_handle, 0) == SND_PCM_STATE_PREPARED) { + continue; + } + if (device->playback_linked) { group_done = 1; } @@ -1754,6 +1763,10 @@ alsa_driver_stop (alsa_driver_t *driver) continue; } + if (alsa_driver_get_state(device->capture_handle, 1) != SND_PCM_STATE_RUNNING) { + continue; + } + if (device->capture_linked) { group_done = 1; } @@ -1780,6 +1793,10 @@ alsa_driver_stop (alsa_driver_t *driver) continue; } + if (alsa_driver_get_state(device->playback_handle, 0) != SND_PCM_STATE_RUNNING) { + continue; + } + if (device->playback_linked) { group_done = 1; } @@ -1821,6 +1838,10 @@ alsa_driver_close (alsa_driver_t *driver) device->capture_linked = 0; } + if (device->capture_target_state != SND_PCM_STATE_NOTREADY) { + continue; + } + snd_pcm_close(device->capture_handle); device->capture_handle = NULL; } @@ -1836,6 +1857,10 @@ alsa_driver_close (alsa_driver_t *driver) device->playback_linked = 0; } + if (device->playback_target_state != SND_PCM_STATE_NOTREADY) { + continue; + } + snd_pcm_close(device->playback_handle); device->playback_handle = NULL; } From 4d60f5b9a298f0c7df6214cbaa4a94ab1c7e7e43 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Thu, 2 Jan 2020 15:18:24 +0100 Subject: [PATCH 31/42] jack alsa: rework 'x' and 'c' options 'c' decides what state idle device should be put to during reload api call, when this option is present logic is to 'close' the idle device, otherwise leave it 'prepared' 'x' enables the idle devices check for jackd init phase, the check will behave the same way reload api call check call behaves, therefore the state of the device will depend on 'c' option, if 'x' option is not provided, devices will be configured to 'running' state during jack init. Change-Id: I3f8461884ef0569229f689412d0ec6ff8aeb39e9 Signed-off-by: Adam Miartus Signed-off-by: Adam Miartus (cherry picked from commit ce2146ef1b2dd1f7fd5c12b288c6b044f9628cc9) Signed-off-by: Timo Wischer --- linux/alsa/JackAlsaDriver.cpp | 23 ++++++++++------------- linux/alsa/alsa_driver.h | 4 ++-- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index afb75b26a..e503752cc 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -360,8 +360,8 @@ int JackAlsaDriver::Open(alsa_driver_info_t info) return -1; } - /* this feature caused driver to skip opening alsa devices, therefore populate channels from user input */ - if (info.features & ALSA_DRIVER_FEAT_START_CLOSED) { + /* we are starting with all alsa devices closed, therfore populate jack channels based on user hint */ + if (info.features & ALSA_DRIVER_FEAT_BACKEND_EVAL_ON_INIT && info.features & ALSA_DRIVER_FEAT_BACKEND_CLOSE_IDLE) { for (size_t i = 0; i < std::max(info.devices_capture_size, info.devices_playback_size); ++i) { if (i < info.devices_capture_size && info.devices[i].capture_channels < 1) { jack_error ("invalid or missing channels parameter with '-x' option 'start-closed' for device C: '%s'", info.devices[i].capture_name); @@ -590,15 +590,12 @@ int JackAlsaDriver::TargetState(DriverMode mode, int connections_count) return SND_PCM_STATE_RUNNING; } - if (mode == DriverMode::Init) { - if (driver->features & ALSA_DRIVER_FEAT_START_CLOSED) { - return SND_PCM_STATE_NOTREADY; - } else { - return SND_PCM_STATE_RUNNING; - } + // evaluation during init is disabled by user option + if (mode == DriverMode::Init && !(driver->features & ALSA_DRIVER_FEAT_BACKEND_EVAL_ON_INIT)) { + return SND_PCM_STATE_RUNNING; } - if (driver->features & ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS) { + if (driver->features & ALSA_DRIVER_FEAT_BACKEND_CLOSE_IDLE) { return SND_PCM_STATE_NOTREADY; } @@ -893,10 +890,10 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () NULL); value.i = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "start-closed", 'x', JackDriverParamBool, &value, NULL, "Start with all devices closed", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "eval-on-init", 'x', JackDriverParamBool, &value, NULL, "Do not start ALSA devices on jack startup", NULL); value.i = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "close-idle-devs", 'c', JackDriverParamBool, &value, NULL, "Close idle devices on alsa driver restart request", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "close-idle", 'c', JackDriverParamBool, &value, NULL, "Close idle devices on alsa driver restart request", NULL); value.i = 0; jack_driver_descriptor_add_parameter(desc, &filler, "unlinked-devs", 'u', JackDriverParamBool, &value, NULL, "Do not link devices", NULL); @@ -1078,11 +1075,11 @@ SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLocke break; case 'x': - info.features |= param->value.i ? ALSA_DRIVER_FEAT_START_CLOSED : 0; + info.features |= param->value.i ? ALSA_DRIVER_FEAT_BACKEND_EVAL_ON_INIT : 0; break; case 'c': - info.features |= param->value.i ? ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS : 0; + info.features |= param->value.i ? ALSA_DRIVER_FEAT_BACKEND_CLOSE_IDLE : 0; break; case 'u': diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h index d62ddb8d7..78c2ac017 100644 --- a/linux/alsa/alsa_driver.h +++ b/linux/alsa/alsa_driver.h @@ -69,8 +69,8 @@ typedef int32_t alsa_driver_default_format_t; #define SND_PCM_STATE_NOTREADY (SND_PCM_STATE_LAST + 1) #endif -#define ALSA_DRIVER_FEAT_START_CLOSED (1 << 0) -#define ALSA_DRIVER_FEAT_CLOSE_IDLE_DEVS (1 << 1) +#define ALSA_DRIVER_FEAT_BACKEND_EVAL_ON_INIT (1 << 0) +#define ALSA_DRIVER_FEAT_BACKEND_CLOSE_IDLE (1 << 1) #define ALSA_DRIVER_FEAT_UNLINKED_DEVS (1 << 2) #ifdef __cplusplus From cdddc81f10852b5dfa2256c5c501572c2a7d73ed Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Tue, 14 Jan 2020 14:07:58 +0100 Subject: [PATCH 32/42] alsa_driver: remove additional delete to avoid double free the upper layer is responsible for deleting the driver in case of failed initialization Change-Id: I2fa0850375b1397d0b6907f210ca68d7a73acd03 Signed-off-by: Adam Miartus (cherry picked from commit 8e6734a2b038c502d323154bcc306e70a95c6dd5) Signed-off-by: Timo Wischer --- linux/alsa/alsa_driver.c | 1 - 1 file changed, 1 deletion(-) diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 07729c6c1..43d6d484e 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -500,7 +500,6 @@ alsa_driver_configure_stream (alsa_driver_t *driver, alsa_device_t *device, char /*Set the configurable parameters for a PCM channel*/ if ((err = snd_pcm_plugin_params(handle, &ch_params)) < 0) { jack_error("snd_pcm_plugin_params failed for %s %s with err = (%s)", snd_strerror(err), stream_name, device_name); - alsa_driver_delete(driver); return -1; } From 104ca04438957ca5524786654464697273ef30e1 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Thu, 20 Feb 2020 14:17:59 +0100 Subject: [PATCH 33/42] QTISA-195 alsa: qnx call plugin flush for every device separately snd_pcm_plugin_flush will not flush all linked devices on qnx, each device has to be flushed separately Change-Id: Ie2921ecc9d9f7cdd88e6b80f6a51e7d7bfd3d954 Signed-off-by: Adam Miartus (cherry picked from commit 8ff259557563bbc9098c5d5435228a51e2b0c4bf) Signed-off-by: Timo Wischer --- linux/alsa/alsa_driver.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 43d6d484e..7aa198999 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -1766,14 +1766,14 @@ alsa_driver_stop (alsa_driver_t *driver) continue; } - if (device->capture_linked) { - group_done = 1; - } - #ifdef __QNXNTO__ /* In case of capture: Flush discards the frames */ err = snd_pcm_plugin_flush(device->capture_handle, SND_PCM_CHANNEL_CAPTURE); #else + if (device->capture_linked) { + group_done = 1; + } + err = snd_pcm_drop (device->capture_handle); #endif if (err < 0) { @@ -1796,14 +1796,14 @@ alsa_driver_stop (alsa_driver_t *driver) continue; } - if (device->playback_linked) { - group_done = 1; - } - #ifdef __QNXNTO__ /* In case of playback: Drain discards the frames */ err = snd_pcm_plugin_playback_drain(device->playback_handle); #else + if (device->playback_linked) { + group_done = 1; + } + err = snd_pcm_drop (device->playback_handle); #endif if (err < 0) { From 894ca0ccf9bd8259d11d3d604020d3f589168563 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Fri, 7 Feb 2020 12:59:39 +0100 Subject: [PATCH 34/42] alsa: rework error reporting from alsa_driver_wait error code can now be checked making error handling more explicit this change also fixes confusing log in case of idle mode of operation the specific error code numbers are not significant because they were never used in handling errors and in general there is error log printed each time error occurs Change-Id: Ieef6c2d72301ba582bfbd101bba7f3a5e228e980 Signed-off-by: Adam Miartus (cherry picked from commit 2ec88d7054e8a985875e33ee49b1241bc55e88f2) Signed-off-by: Timo Wischer --- linux/alsa/JackAlsaDriver.cpp | 31 ++++++++++++------------ linux/alsa/alsa_driver.c | 44 +++++++++++++++++------------------ linux/alsa/alsa_driver.h | 8 ++++++- 3 files changed, 45 insertions(+), 38 deletions(-) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index e503752cc..c63fb9f6d 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -476,25 +476,26 @@ int JackAlsaDriver::Reload() int JackAlsaDriver::Read() { /* Taken from alsa_driver_run_cycle */ - int wait_status; - jack_nframes_t nframes; + alsa_driver_wait_status_t wait_status; + jack_nframes_t nframes = 0; fDelayedUsecs = 0.f; -retry: + /* wait until all devices have some data available */ + do { + nframes = alsa_driver_wait((alsa_driver_t *)fDriver, -1, &wait_status, &fDelayedUsecs); - nframes = alsa_driver_wait((alsa_driver_t *)fDriver, -1, &wait_status, &fDelayedUsecs); + if (wait_status == ALSA_DRIVER_WAIT_ERROR) + return -1; /* driver failed */ - if (wait_status < 0) - return -1; /* driver failed */ - - if (nframes == 0) { - /* we detected an xrun and restarted: notify - * clients about the delay. - */ - jack_log("ALSA XRun wait_status = %d", wait_status); - NotifyXRun(fBeginDateUst, fDelayedUsecs); - goto retry; /* recoverable error*/ - } + if (wait_status == ALSA_DRIVER_WAIT_XRUN) { + /* we detected an xrun and restarted: notify + * clients about the delay. + */ + jack_log("ALSA XRun wait_status = %d", wait_status); + NotifyXRun(fBeginDateUst, fDelayedUsecs); + continue; + } + } while (nframes == 0); if (nframes != fEngineControl->fBufferSize) jack_log("JackAlsaDriver::Read warning fBufferSize = %ld nframes = %ld", fEngineControl->fBufferSize, nframes); diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 7aa198999..5a1092d04 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -2020,7 +2020,7 @@ alsa_driver_avail(alsa_driver_t *driver, snd_pcm_t *pcm, bool is_capture) static int under_gdb = FALSE; jack_nframes_t -alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float +alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t *status, float *delayed_usecs) { snd_pcm_sframes_t avail = 0; @@ -2062,9 +2062,9 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float SetTime(driver->poll_last); driver->poll_next = driver->poll_last + driver->period_usecs; - *status = 0; + *status = ALSA_DRIVER_WAIT_OK; - return INT_MAX; + return driver->frames_per_cycle; } while (pfd_count > 0) { @@ -2174,13 +2174,13 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float continue; } jack_error(poll_log); - *status = -2; + *status = ALSA_DRIVER_WAIT_ERROR; return 0; } jack_error ("ALSA: poll call failed (%s)", strerror (errno)); - *status = -3; + *status = ALSA_DRIVER_WAIT_ERROR; return 0; } @@ -2193,14 +2193,14 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float jack_error ("ALSA: poll time out, polled for %" PRIu64 " usecs, Reached max retry cnt = %d, Exiting", poll_ret - poll_enter, MAX_RETRY_COUNT); - *status = -5; + *status = ALSA_DRIVER_WAIT_ERROR; return 0; } + jack_error ("ALSA: poll time out, polled for %" PRIu64 " usecs, Retrying with a recovery, retry cnt = %d", poll_ret - poll_enter, retry_cnt); - *status = alsa_driver_xrun_recovery (driver, delayed_usecs); - return 0; + goto xrun; } // JACK2 @@ -2217,11 +2217,11 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float * to return from poll */ if (driver->pfd[pfd_index-1].revents == 0) { /* we timed out on the extra fd */ - *status = -4; + *status = ALSA_DRIVER_WAIT_ERROR; return -1; } /* if POLLIN was the only bit set, we're OK */ - *status = 0; + *status = ALSA_DRIVER_WAIT_OK; return (driver->pfd[pfd_index-1].revents == POLLIN) ? 0 : -1; } @@ -2251,7 +2251,7 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float if (snd_pcm_poll_descriptors_revents (device->capture_handle, &driver->pfd[pfd_index], pfd_cap_count[i], &collect_revs) != 0) { jack_error ("ALSA: capture revents failed"); - *status = -6; + *status = ALSA_DRIVER_WAIT_ERROR; return 0; } @@ -2260,8 +2260,7 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float if (collect_revs & POLLERR) { /* optimization, no point in polling more if we already have xrun on one device */ jack_error ("xrun C: '%s'", device->capture_name); - *status = alsa_driver_xrun_recovery (driver, delayed_usecs); - return 0; + goto xrun; } if (collect_revs & POLLIN) { } @@ -2290,7 +2289,7 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float if (snd_pcm_poll_descriptors_revents (device->playback_handle, &driver->pfd[pfd_index], pfd_play_count[i], &collect_revs) != 0) { jack_error ("ALSA: playback revents failed"); - *status = -6; + *status = ALSA_DRIVER_WAIT_ERROR; return 0; } @@ -2299,12 +2298,11 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float if (collect_revs & POLLERR) { /* optimization, no point in polling more if we already have xrun on one device */ jack_error ("xrun P: '%s'", device->playback_name); - *status = alsa_driver_xrun_recovery (driver, delayed_usecs); - return 0; + goto xrun; } if (collect_revs & POLLNVAL) { jack_error ("ALSA: playback device disconnected"); - *status = -7; + *status = ALSA_DRIVER_WAIT_ERROR; return 0; } if (collect_revs & POLLOUT) { @@ -2338,8 +2336,7 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float if ((avail = alsa_driver_avail (driver, device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { if (avail == -EPIPE) { jack_error ("ALSA: avail_update xrun on capture dev '%s'", device->capture_name); - *status = alsa_driver_xrun_recovery (driver, delayed_usecs); - return 0; + goto xrun; } else { jack_error ("unknown ALSA avail_update return value (%u)", capture_avail); } @@ -2364,8 +2361,7 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float if ((avail = alsa_driver_avail (driver, device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { if (avail == -EPIPE) { jack_error ("ALSA: avail_update xrun on playback dev '%s'", device->playback_name); - *status = alsa_driver_xrun_recovery (driver, delayed_usecs); - return 0; + goto xrun; } else { jack_error ("unknown ALSA avail_update return value (%u)", playback_avail); } @@ -2388,7 +2384,7 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float bitset_copy (device->channels_not_done, device->channels_done); } - *status = 0; + *status = ALSA_DRIVER_WAIT_OK; avail = capture_avail < playback_avail ? capture_avail : playback_avail; @@ -2403,6 +2399,10 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float */ return avail - (avail % driver->frames_per_cycle); + +xrun: + *status = (alsa_driver_xrun_recovery (driver, delayed_usecs) == 0) ? ALSA_DRIVER_WAIT_XRUN : ALSA_DRIVER_WAIT_ERROR; + return 0; } int diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h index 78c2ac017..55c59abf1 100644 --- a/linux/alsa/alsa_driver.h +++ b/linux/alsa/alsa_driver.h @@ -342,8 +342,14 @@ alsa_driver_stop (alsa_driver_t *driver); int alsa_driver_close (alsa_driver_t *driver); +typedef enum { + ALSA_DRIVER_WAIT_OK = 0, + ALSA_DRIVER_WAIT_ERROR = -1, + ALSA_DRIVER_WAIT_XRUN = -2, +} alsa_driver_wait_status_t; + jack_nframes_t -alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float +alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t *status, float *delayed_usecs); int From 0187cf6782e733efc97cb6c9bc96c32ddf095f22 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Thu, 20 Feb 2020 16:09:32 +0100 Subject: [PATCH 35/42] QTISA-183 alsa: add more logs improve debugging add more logs to error cases Change-Id: Ie369972a177b869ea1ca8375556f608304d52a44 Signed-off-by: Adam Miartus (cherry picked from commit 9abd7adb57abdbc1596f47ee9ef0b1a92421f9d4) Signed-off-by: Timo Wischer --- linux/alsa/alsa_driver.c | 132 ++++++++++++++++++++++++++++++++++----- 1 file changed, 117 insertions(+), 15 deletions(-) diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 5a1092d04..69045905f 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -59,6 +59,44 @@ char* strcasestr(const char* haystack, const char* needle); /* Max re-try count for Alsa poll timeout handling */ #define MAX_RETRY_COUNT 5 +#ifdef __QNXNTO__ + +char qnx_channel_error_str[9][35] = { + "SND_PCM_STATUS_NOTREADY", + "SND_PCM_STATUS_READY", + "SND_PCM_STATUS_PREPARED", + "SND_PCM_STATUS_RUNNING", + "SND_PCM_STATUS_PAUSED", + "SND_PCM_STATUS_SUSPENDED", + "SND_PCM_STATUS_UNDERRUN", + "SND_PCM_STATUS_OVERRUN", + "SND_PCM_STATUS_UNKNOWN", +}; + +static char* alsa_channel_status_error_str(int code) +{ + switch(code) { + case SND_PCM_STATUS_NOTREADY: + return qnx_channel_error_str[0]; + case SND_PCM_STATUS_READY: + return qnx_channel_error_str[1]; + case SND_PCM_STATUS_PREPARED: + return qnx_channel_error_str[2]; + case SND_PCM_STATUS_RUNNING: + return qnx_channel_error_str[3]; + case SND_PCM_STATUS_PAUSED: + return qnx_channel_error_str[4]; + case SND_PCM_STATUS_SUSPENDED: + return qnx_channel_error_str[5]; + case SND_PCM_STATUS_UNDERRUN: + return qnx_channel_error_str[6]; + case SND_PCM_STATUS_OVERRUN: + return qnx_channel_error_str[7]; + } + return qnx_channel_error_str[8]; +} +#endif + static int alsa_driver_link (alsa_driver_t *driver); static int alsa_driver_open_device (alsa_driver_t *driver, alsa_device_t *device, bool is_capture); static int alsa_driver_get_state (snd_pcm_t *handle, int is_capture); @@ -1390,6 +1428,8 @@ alsa_driver_open (alsa_driver_t *driver) int do_capture = 0, do_playback = 0; if (!device->capture_handle && (i < driver->devices_c_count) && (device->capture_target_state != SND_PCM_STATE_NOTREADY)) { + jack_info("open C: %s", device->capture_name); + err = alsa_driver_open_device (driver, &driver->devices[i], SND_PCM_STREAM_CAPTURE); if (err < 0) { jack_error ("\n\nATTENTION: Opening of the capture device \"%s\" failed.", @@ -1401,6 +1441,8 @@ alsa_driver_open (alsa_driver_t *driver) } if (!device->playback_handle && (i < driver->devices_p_count) && (device->playback_target_state != SND_PCM_STATE_NOTREADY)) { + jack_info("open P: %s", device->playback_name); + err = alsa_driver_open_device (driver, &driver->devices[i], SND_PCM_STREAM_PLAYBACK); if (err < 0) { jack_error ("\n\nATTENTION: Opening of the playback device \"%s\" failed.", @@ -1439,21 +1481,27 @@ alsa_driver_link (alsa_driver_t *driver) continue; } + jack_info("link C: %s", device->capture_name); + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + jack_info("link skipped, device unused"); continue; } if (group_handle == NULL) { + jack_info("link, device is group master"); group_handle = device->capture_handle; device->capture_linked = 1; continue; } if (device->capture_linked) { + jack_info("link skipped, already done"); continue; } if (group_handle == device->capture_handle) { + jack_info("link skipped, master already done"); device->capture_linked = 1; continue; } @@ -1472,21 +1520,27 @@ alsa_driver_link (alsa_driver_t *driver) continue; } + jack_info("link P: %s", device->playback_name); + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + jack_info("link skipped, device unused"); continue; } if (group_handle == NULL) { + jack_info("link, device is group master"); group_handle = device->playback_handle; device->playback_linked = 1; continue; } if (device->playback_linked) { + jack_info("link skipped, already done"); continue; } if (group_handle == device->playback_handle) { + jack_info("link skipped, master already done"); device->playback_linked = 1; continue; } @@ -1520,7 +1574,10 @@ alsa_driver_start (alsa_driver_t *driver) continue; } + jack_info("prepare C: %s", device->capture_name); + if (device->capture_target_state == SND_PCM_STATE_NOTREADY) { + jack_info("prepare skipped, device unused"); continue; } @@ -1529,10 +1586,12 @@ alsa_driver_start (alsa_driver_t *driver) } if (group_done && device->capture_linked) { + jack_info("prepare skipped, already done by link group"); continue; } if (alsa_driver_get_state(device->capture_handle, 1) == SND_PCM_STATE_PREPARED) { + jack_info("prepare skipped, already prepared"); continue; } @@ -1553,7 +1612,10 @@ alsa_driver_start (alsa_driver_t *driver) continue; } + jack_info("prepare P: %s", device->playback_name); + if (device->playback_target_state == SND_PCM_STATE_NOTREADY) { + jack_info("prepare skipped, device unused"); continue; } @@ -1562,10 +1624,12 @@ alsa_driver_start (alsa_driver_t *driver) } if (group_done && device->playback_linked) { + jack_info("prepare skipped, already done by link group"); continue; } if (alsa_driver_get_state(device->playback_handle, 0) == SND_PCM_STATE_PREPARED) { + jack_info("prepare skipped, already prepared"); continue; } @@ -1612,7 +1676,10 @@ alsa_driver_start (alsa_driver_t *driver) continue; } + jack_info("silence P: %s", device->playback_name); + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + jack_info("silence skipped, device unused"); continue; } @@ -1655,8 +1722,8 @@ alsa_driver_start (alsa_driver_t *driver) device->playback_sample_bytes; if ((err = snd_pcm_plugin_write(device->playback_handle, device->playback_areas, bytes)) < bytes) { - jack_error ("ALSA: could not complete write of %" - PRIu32 " frames: error = %d", silence_frames, err); + jack_error ("ALSA: could not complete silence %s of %" + PRIu32 " frames: %u, error = %d", device->playback_name, silence_frames, err, errno); return -1; } #else @@ -1673,11 +1740,15 @@ alsa_driver_start (alsa_driver_t *driver) continue; } + jack_info("start C: %s", device->capture_name); + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + jack_info("start skipped, device unused"); continue; } if (group_done && device->capture_linked) { + jack_info("start skipped, already done by link group"); continue; } @@ -1699,11 +1770,15 @@ alsa_driver_start (alsa_driver_t *driver) continue; } + jack_info("start P: %s", device->playback_name); + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + jack_info("start skipped, device unused"); continue; } if (group_done && device->playback_linked) { + jack_info("start skipped, already done by link group"); continue; } @@ -1758,11 +1833,15 @@ alsa_driver_stop (alsa_driver_t *driver) continue; } + jack_info("stop C: %s", device->capture_name); + if (group_done && device->capture_linked) { + jack_info("stop skipped, already done by link group"); continue; } if (alsa_driver_get_state(device->capture_handle, 1) != SND_PCM_STATE_RUNNING) { + jack_info("stop skipped, device not running"); continue; } @@ -1788,11 +1867,15 @@ alsa_driver_stop (alsa_driver_t *driver) continue; } + jack_info("stop P: %s", device->playback_name); + if (group_done && device->playback_linked) { + jack_info("stop skipped, already done by link group"); continue; } if (alsa_driver_get_state(device->playback_handle, 0) != SND_PCM_STATE_RUNNING) { + jack_info("stop skipped, device not running"); continue; } @@ -1938,7 +2021,6 @@ alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs) if (device->playback_handle) { state = alsa_driver_get_state(device->playback_handle, SND_PCM_STREAM_PLAYBACK); - // TODO overrun if (state == SND_PCM_STATE_XRUN) { driver->xrun_count++; @@ -2092,12 +2174,13 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t pfd_count - pfd_index, SND_PCM_STREAM_CAPTURE); if (pfd_cap_count[i] < 0) { - jack_log ("alsa_driver_poll_descriptors failed pfd_cap_count[%d]=%d", i ,pfd_cap_count[i] ); /* In case of xrun -EPIPE is returned perform xrun recovery*/ if (pfd_cap_count[i] == -EPIPE) { + jack_error("poll descriptors xrun C: %s pfd_cap_count[%d]=%d", device->capture_name, i, pfd_cap_count[i]); goto xrun; } /* for any other error return negative wait status to caller */ + jack_error("poll descriptors error C: %s pfd_cap_count[%d]=%d", device->capture_name, i, pfd_cap_count[i]); *status = ALSA_DRIVER_WAIT_ERROR; return 0; } else { @@ -2126,12 +2209,13 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t pfd_count - pfd_index, SND_PCM_STREAM_PLAYBACK); if (pfd_play_count[i] < 0) { - jack_log ("alsa_driver_poll_descriptors failed pfd_play_count[%d]=%d", i ,pfd_play_count[i] ); /* In case of xrun -EPIPE is returned perform xrun recovery*/ if (pfd_cap_count[i] == -EPIPE) { + jack_error("poll descriptors xrun P: %s pfd_cap_count[%d]=%d", device->playback_name, i, pfd_play_count[i]); goto xrun; } /* for any other error return negative wait status to caller */ + jack_error("poll descriptors error P: %s pfd_cap_count[%d]=%d", device->playback_name, i, pfd_play_count[i]); *status = ALSA_DRIVER_WAIT_ERROR; return 0; } else { @@ -2200,6 +2284,14 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t jack_error ("ALSA: poll time out, polled for %" PRIu64 " usecs, Retrying with a recovery, retry cnt = %d", poll_ret - poll_enter, retry_cnt); + for (int i = 0; i < driver->devices_count; ++i) { + if (driver->devices[i].capture_handle && i < driver->devices_c_count && cap_revents[i] == 0) { + jack_log("device C: %s poll was requested", driver->devices[i].capture_name); + } + if (driver->devices[i].playback_handle && i < driver->devices_p_count && play_revents[i] == 0) { + jack_log("device P: %s poll was requested", driver->devices[i].playback_name); + } + } goto xrun; } @@ -2217,6 +2309,7 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t * to return from poll */ if (driver->pfd[pfd_index-1].revents == 0) { /* we timed out on the extra fd */ + jack_error("extra fd error"); *status = ALSA_DRIVER_WAIT_ERROR; return -1; } @@ -2449,8 +2542,12 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) const size_t bytes = contiguous * device->capture_nchannels * device->capture_sample_bytes; if ((err = snd_pcm_plugin_read(device->capture_handle, device->capture_areas, bytes)) < bytes) { - jack_error ("ALSA: could not complete read of %" - PRIu32 " frames: error = %d", contiguous, err); + jack_error("read C: %s, requested %d, got %d, snd error %s, errno %d", + device->capture_name, + bytes, + err, + alsa_channel_status_error_str(alsa_driver_get_state(device->capture_handle, 1)), + errno); return -1; } #endif @@ -2476,8 +2573,8 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) #ifndef __QNXNTO__ if ((err = snd_pcm_mmap_commit (device->capture_handle, offset, contiguous)) < 0) { - jack_error ("ALSA: could not complete read of %" - PRIu32 " frames: error = %d", contiguous, err); + jack_error ("ALSA: could not complete read commit %s of %" + PRIu32 " frames: error = %d", device->capture_name, contiguous, err); return -1; } #endif @@ -2586,15 +2683,19 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) const size_t bytes = contiguous * device->playback_nchannels * device->playback_sample_bytes; if ((err = snd_pcm_plugin_write(device->playback_handle, device->playback_areas, bytes)) < bytes) { - jack_error ("ALSA: could not complete write of %" - PRIu32 " frames: error = %d", contiguous, err); + jack_error("write P: %s, requested %d, got %d, snd error %s, errno %d", + device->playback_name, + bytes, + err, + alsa_channel_status_error_str(alsa_driver_get_state(device->playback_handle, 0)), + errno); return -1; } #else if ((err = snd_pcm_mmap_commit (device->playback_handle, offset, contiguous)) < 0) { - jack_error ("ALSA: could not complete playback of %" - PRIu32 " frames: error = %d", contiguous, err); + jack_error ("ALSA: could not complete playback commit %s of %" + PRIu32 " frames: error = %d", device->playback_name, contiguous, err); if (err != -EPIPE && err != -ESTRPIPE) return -1; } @@ -2888,8 +2989,9 @@ alsa_driver_open_device (alsa_driver_t *driver, alsa_device_t *device, bool is_c case EPERM: jack_error ("you do not have permission to open " - "the audio device \"%s\" for playback", - is_capture ? device->capture_name : device->playback_name); + "the audio device \"%s\" for %s", + is_capture ? device->capture_name : device->playback_name, + is_capture ? "capture" : "playback"); break; case EINVAL: From 3fb9fec2d9def0364fb38b0df28a4383c72d1da1 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Fri, 21 Feb 2020 18:57:31 +0100 Subject: [PATCH 36/42] QTISA-183 alsa: make alsa_driver_xrun_recovery global to be used in JackAlsaDriver.cpp Change-Id: Ica92fe1a1366664e5a9f5eff54dc8d1c8a0405e1 Signed-off-by: Adam Miartus (cherry picked from commit a88639d3e9dade9f2100c93376f28c694786b45b) Signed-off-by: Timo Wischer --- linux/alsa/alsa_driver.c | 2 +- linux/alsa/alsa_driver.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 69045905f..69f416963 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -1990,7 +1990,7 @@ alsa_driver_get_state (snd_pcm_t *handle, int is_capture) #endif } -static int +int alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs) { int state; diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h index 55c59abf1..aa3a81300 100644 --- a/linux/alsa/alsa_driver.h +++ b/linux/alsa/alsa_driver.h @@ -358,6 +358,9 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes); int alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes); +int +alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs); + // Code implemented in JackAlsaDriver.cpp void ReadInput(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread); From 604c4859efb745d6575a0abefe6e177ff93aeeb8 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Fri, 21 Feb 2020 18:59:51 +0100 Subject: [PATCH 37/42] QTISA-183 alsa: remove recovery from alsa_driver_wait upper layer will be responsible for handling recovery Change-Id: I507df2976ae569b4953a16e6e334ae5f9c78b59f Signed-off-by: Adam Miartus (cherry picked from commit aff8dd6e215df7023918dc47264da70b3252ad91) Signed-off-by: Timo Wischer --- linux/alsa/alsa_driver.c | 41 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 69f416963..937034651 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -56,8 +56,6 @@ char* strcasestr(const char* haystack, const char* needle); /* Delay (in process calls) before jackd will report an xrun */ #define XRUN_REPORT_DELAY 0 -/* Max re-try count for Alsa poll timeout handling */ -#define MAX_RETRY_COUNT 5 #ifdef __QNXNTO__ @@ -2108,7 +2106,6 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t snd_pcm_sframes_t avail = 0; snd_pcm_sframes_t capture_avail = 0; snd_pcm_sframes_t playback_avail = 0; - int retry_cnt = 0; jack_time_t poll_enter; jack_time_t poll_ret = 0; @@ -2177,7 +2174,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t /* In case of xrun -EPIPE is returned perform xrun recovery*/ if (pfd_cap_count[i] == -EPIPE) { jack_error("poll descriptors xrun C: %s pfd_cap_count[%d]=%d", device->capture_name, i, pfd_cap_count[i]); - goto xrun; + *status = ALSA_DRIVER_WAIT_XRUN; + return 0; } /* for any other error return negative wait status to caller */ jack_error("poll descriptors error C: %s pfd_cap_count[%d]=%d", device->capture_name, i, pfd_cap_count[i]); @@ -2212,7 +2210,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t /* In case of xrun -EPIPE is returned perform xrun recovery*/ if (pfd_cap_count[i] == -EPIPE) { jack_error("poll descriptors xrun P: %s pfd_cap_count[%d]=%d", device->playback_name, i, pfd_play_count[i]); - goto xrun; + *status = ALSA_DRIVER_WAIT_XRUN; + return 0; } /* for any other error return negative wait status to caller */ jack_error("poll descriptors error P: %s pfd_cap_count[%d]=%d", device->playback_name, i, pfd_play_count[i]); @@ -2272,18 +2271,9 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t poll_ret = jack_get_microseconds (); if (poll_result == 0) { - retry_cnt++; - if(retry_cnt > MAX_RETRY_COUNT) { - jack_error ("ALSA: poll time out, polled for %" PRIu64 - " usecs, Reached max retry cnt = %d, Exiting", - poll_ret - poll_enter, MAX_RETRY_COUNT); - *status = ALSA_DRIVER_WAIT_ERROR; - return 0; - } - jack_error ("ALSA: poll time out, polled for %" PRIu64 - " usecs, Retrying with a recovery, retry cnt = %d", - poll_ret - poll_enter, retry_cnt); + " usecs, Retrying with a recovery", + poll_ret - poll_enter); for (int i = 0; i < driver->devices_count; ++i) { if (driver->devices[i].capture_handle && i < driver->devices_c_count && cap_revents[i] == 0) { jack_log("device C: %s poll was requested", driver->devices[i].capture_name); @@ -2292,7 +2282,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t jack_log("device P: %s poll was requested", driver->devices[i].playback_name); } } - goto xrun; + *status = ALSA_DRIVER_WAIT_XRUN; + return 0; } // JACK2 @@ -2353,7 +2344,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t if (collect_revs & POLLERR) { /* optimization, no point in polling more if we already have xrun on one device */ jack_error ("xrun C: '%s'", device->capture_name); - goto xrun; + *status = ALSA_DRIVER_WAIT_XRUN; + return 0; } if (collect_revs & POLLIN) { } @@ -2391,7 +2383,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t if (collect_revs & POLLERR) { /* optimization, no point in polling more if we already have xrun on one device */ jack_error ("xrun P: '%s'", device->playback_name); - goto xrun; + *status = ALSA_DRIVER_WAIT_XRUN; + return 0; } if (collect_revs & POLLNVAL) { jack_error ("ALSA: playback device disconnected"); @@ -2429,7 +2422,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t if ((avail = alsa_driver_avail (driver, device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { if (avail == -EPIPE) { jack_error ("ALSA: avail_update xrun on capture dev '%s'", device->capture_name); - goto xrun; + *status = ALSA_DRIVER_WAIT_XRUN; + return 0; } else { jack_error ("unknown ALSA avail_update return value (%u)", capture_avail); } @@ -2454,7 +2448,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t if ((avail = alsa_driver_avail (driver, device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { if (avail == -EPIPE) { jack_error ("ALSA: avail_update xrun on playback dev '%s'", device->playback_name); - goto xrun; + *status = ALSA_DRIVER_WAIT_XRUN; + return 0; } else { jack_error ("unknown ALSA avail_update return value (%u)", playback_avail); } @@ -2492,10 +2487,6 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t */ return avail - (avail % driver->frames_per_cycle); - -xrun: - *status = (alsa_driver_xrun_recovery (driver, delayed_usecs) == 0) ? ALSA_DRIVER_WAIT_XRUN : ALSA_DRIVER_WAIT_ERROR; - return 0; } int From ee274e7c1743b464a2c8832ed5ce74a0ad4b1222 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Tue, 25 Feb 2020 19:03:39 +0100 Subject: [PATCH 38/42] QTISA-183 audio: implement retry for failed read write read and write can fail, allow retry in process method Change-Id: I6d29b5cb1a6b849b44c9e0128dd3de7269a35873 Signed-off-by: Adam Miartus (cherry picked from commit e24bd05411b61f6914a601bbd8861882b50065fe) Signed-off-by: Timo Wischer --- common/JackAudioDriver.cpp | 22 +++++++++++++++++----- common/JackAudioDriver.h | 2 ++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/common/JackAudioDriver.cpp b/common/JackAudioDriver.cpp index ac686e3a3..5ca297052 100644 --- a/common/JackAudioDriver.cpp +++ b/common/JackAudioDriver.cpp @@ -199,7 +199,19 @@ int JackAudioDriver::Write() int JackAudioDriver::Process() { - return (fEngineControl->fSyncMode) ? ProcessSync() : ProcessAsync(); + int err = 0; + int retries = -1; + + while (retries++ < fMaxRetryCount) { + err = (fEngineControl->fSyncMode) ? ProcessSync() : ProcessAsync(); + if (err == 0) { + break; + } + + jack_error("JackAudioDriver::Process failed, retry %d", retries); + } + + return err; } /* @@ -211,13 +223,13 @@ int JackAudioDriver::ProcessAsync() { // Read input buffers for the current cycle if (Read() < 0) { - jack_error("JackAudioDriver::ProcessAsync: read error, stopping..."); + jack_error("JackAudioDriver::ProcessAsync: read error"); return -1; } // Write output buffers from the previous cycle if (Write() < 0) { - jack_error("JackAudioDriver::ProcessAsync: write error, stopping..."); + jack_error("JackAudioDriver::ProcessAsync: write error"); return -1; } @@ -285,7 +297,7 @@ int JackAudioDriver::ProcessSync() { // Read input buffers for the current cycle if (Read() < 0) { - jack_error("JackAudioDriver::ProcessSync: read error, stopping..."); + jack_error("JackAudioDriver::ProcessSync: read error"); return -1; } @@ -294,7 +306,7 @@ int JackAudioDriver::ProcessSync() // Write output buffers from the current cycle if (Write() < 0) { - jack_error("JackAudioDriver::ProcessSync: write error, stopping..."); + jack_error("JackAudioDriver::ProcessSync: write error"); return -1; } diff --git a/common/JackAudioDriver.h b/common/JackAudioDriver.h index e6f332f0a..76b503cbf 100644 --- a/common/JackAudioDriver.h +++ b/common/JackAudioDriver.h @@ -61,6 +61,8 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver void ProcessGraphSyncMaster(); void ProcessGraphSyncSlave(); + static const int fMaxRetryCount = 5; + public: JackAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); From b5ede1c04a040d2598a89d09a8559139999583ea Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Tue, 25 Feb 2020 19:04:06 +0100 Subject: [PATCH 39/42] QTISA-183 alsa: allow recovery for read and write both read and write can fail, therefore allow recovery Change-Id: I32462c68aaa2395da11d2969a73ab5b5f28af879 Signed-off-by: Adam Miartus (cherry picked from commit 1996a103ac2b14062d81ef222cf902c1c45b8812) Signed-off-by: Timo Wischer --- linux/alsa/JackAlsaDriver.cpp | 39 ++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index c63fb9f6d..e848e2ac0 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -484,16 +484,9 @@ int JackAlsaDriver::Read() do { nframes = alsa_driver_wait((alsa_driver_t *)fDriver, -1, &wait_status, &fDelayedUsecs); - if (wait_status == ALSA_DRIVER_WAIT_ERROR) - return -1; /* driver failed */ - - if (wait_status == ALSA_DRIVER_WAIT_XRUN) { - /* we detected an xrun and restarted: notify - * clients about the delay. - */ - jack_log("ALSA XRun wait_status = %d", wait_status); - NotifyXRun(fBeginDateUst, fDelayedUsecs); - continue; + if (wait_status != ALSA_DRIVER_WAIT_OK) { + jack_error("JackAlsaDriver::Read wait failed, xrun recovery"); + goto retry; } } while (nframes == 0); @@ -503,12 +496,34 @@ int JackAlsaDriver::Read() // Has to be done before read JackDriver::CycleIncTime(); - return alsa_driver_read((alsa_driver_t *)fDriver, fEngineControl->fBufferSize); + if (alsa_driver_read((alsa_driver_t *)fDriver, fEngineControl->fBufferSize) != 0) { + jack_error("JackAlsaDriver::Read read failed, xrun recovery"); + goto retry; + } + + return 0; + +retry: + /* we detected an xrun and restarted: notify + * clients about the delay. + */ + jack_error("JackAlsaDriver::Read failed, xrun recovery"); + alsa_driver_xrun_recovery((alsa_driver_t *)fDriver, &fDelayedUsecs); + NotifyXRun(fBeginDateUst, fDelayedUsecs); + + return -1; } int JackAlsaDriver::Write() { - return alsa_driver_write((alsa_driver_t *)fDriver, fEngineControl->fBufferSize); + if (alsa_driver_write((alsa_driver_t *)fDriver, fEngineControl->fBufferSize) != 0) { + jack_error("JackAlsaDriver::Write failed, xrun recovery"); + alsa_driver_xrun_recovery((alsa_driver_t *)fDriver, &fDelayedUsecs); + NotifyXRun(fBeginDateUst, fDelayedUsecs); + return -1; + } + + return 0; } void JackAlsaDriver::ReadInputAux(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread) From eaca20daad05e75f93b8e82a56090400e6539660 Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Wed, 26 Feb 2020 10:56:16 +0100 Subject: [PATCH 40/42] QTISA-188 alsa: fix implementation for shorts_first previously some state variables were reused for this purpose which caused hidden bugs in multi device implementation, this would cause issues with reloading the jack server in certain conditions now separate variable is used and the driver correctly evaluates sample bytes and format Change-Id: Ic82ba475c4eaa7baffea032106b62bc0b7e86535 Signed-off-by: Adam Miartus (cherry picked from commit b0dfe85ef1e29fd6d6ff9d69992985a22b2d098f) Signed-off-by: Timo Wischer --- linux/alsa/alsa_driver.c | 68 ++++++++++++++++++---------------------- linux/alsa/alsa_driver.h | 3 ++ 2 files changed, 33 insertions(+), 38 deletions(-) diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 937034651..14bcde0c8 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -492,7 +492,7 @@ alsa_driver_configure_stream (alsa_driver_t *driver, alsa_device_t *device, char snd_pcm_t *handle, unsigned int *nperiodsp, channel_t *nchns, - unsigned long sample_width, + unsigned long preferred_sample_bytes, bool is_capture) { int err = 0; @@ -516,10 +516,12 @@ alsa_driver_configure_stream (alsa_driver_t *driver, alsa_device_t *device, char *nchns = ch_info.max_voices; } + ch_params.format.format = (preferred_sample_bytes == 4) ? SND_PCM_SFMT_S32_LE : SND_PCM_SFMT_S16_LE; + ch_params.mode = SND_PCM_MODE_BLOCK; ch_params.start_mode = SND_PCM_START_GO; ch_params.stop_mode = SND_PCM_STOP_STOP; - ch_params.buf.block.frag_size = driver->frames_per_cycle * *nchns * sample_size; + ch_params.buf.block.frag_size = driver->frames_per_cycle * *nchns * snd_pcm_format_width(ch_params.format.format) / 8; *nperiodsp = driver->user_nperiods; ch_params.buf.block.frags_min = 1; @@ -532,7 +534,6 @@ alsa_driver_configure_stream (alsa_driver_t *driver, alsa_device_t *device, char ch_params.format.voices = *nchns; ch_params.channel = is_capture; - ch_params.format.format = (sample_width == 4) ? SND_PCM_SFMT_S32_LE : SND_PCM_SFMT_S16_LE; /*Set the configurable parameters for a PCM channel*/ if ((err = snd_pcm_plugin_params(handle, &ch_params)) < 0) { jack_error("snd_pcm_plugin_params failed for %s %s with err = (%s)", snd_strerror(err), stream_name, device_name); @@ -555,7 +556,7 @@ alsa_driver_configure_stream (alsa_driver_t *driver, alsa_device_t *device, char snd_pcm_sw_params_t *sw_params, unsigned int *nperiodsp, channel_t *nchns, - unsigned long sample_width) + unsigned long preferred_sample_bytes) { int err, format; unsigned int frame_rate; @@ -605,13 +606,13 @@ alsa_driver_configure_stream (alsa_driver_t *driver, alsa_device_t *device, char } } - format = (sample_width == 4) ? 0 : NUMFORMATS - 1; + format = (preferred_sample_bytes == 4) ? 0 : NUMFORMATS - 1; while (1) { if ((err = snd_pcm_hw_params_set_format ( handle, hw_params, formats[format].format)) < 0) { - if ((sample_width == 4 + if ((preferred_sample_bytes == 4 ? format++ >= NUMFORMATS - 1 : format-- <= 0)) { jack_error ("Sorry. The audio interface \"%s\"" @@ -841,26 +842,6 @@ alsa_driver_check_format (snd_pcm_format_t format) return 0; } -static void -alsa_driver_set_sample_bytes (alsa_driver_t *driver, alsa_device_t *device) -{ -#ifdef __QNXNTO__ - device->playback_sample_bytes = - snd_pcm_format_width (device->playback_sample_format) - / 8; - device->capture_sample_bytes = - snd_pcm_format_width (device->capture_sample_format) - / 8; -#else - device->playback_sample_bytes = - snd_pcm_format_physical_width (device->playback_sample_format) - / 8; - device->capture_sample_bytes = - snd_pcm_format_physical_width (device->capture_sample_format) - / 8; -#endif -} - static int alsa_driver_set_parameters (alsa_driver_t *driver, alsa_device_t *device, @@ -908,7 +889,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, device->capture_handle, &driver->capture_nperiods, &device->capture_nchannels, - device->capture_sample_bytes, + driver->preferred_sample_bytes, SND_PCM_CHANNEL_CAPTURE); if (err) { @@ -923,11 +904,11 @@ alsa_driver_set_parameters (alsa_driver_t *driver, } cr = c_setup.format.rate; - c_period_size = c_setup.buf.block.frag_size / device->capture_nchannels - / device->capture_sample_bytes; c_periods = c_setup.buf.block.frags; device->capture_sample_format = c_setup.format.format; device->capture_interleaved = c_setup.format.interleave; + device->capture_sample_bytes = snd_pcm_format_width (c_setup.format.format) / 8; + c_period_size = c_setup.buf.block.frag_size / device->capture_nchannels / device->capture_sample_bytes; #else err = alsa_driver_configure_stream ( driver, @@ -939,7 +920,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->capture_sw_params, &driver->capture_nperiods, &device->capture_nchannels, - device->capture_sample_bytes); + driver->preferred_sample_bytes); if (err) { jack_error ("ALSA: cannot configure capture channel"); @@ -961,6 +942,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, device->capture_interleaved = (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) || (access == SND_PCM_ACCESS_MMAP_COMPLEX); + device->capture_sample_bytes = snd_pcm_format_physical_width (device->capture_sample_format) / 8; #endif if (err) { jack_error ("ALSA: cannot configure capture channel"); @@ -982,7 +964,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, device->playback_handle, &driver->playback_nperiods, &device->playback_nchannels, - device->playback_sample_bytes, + driver->preferred_sample_bytes, SND_PCM_CHANNEL_PLAYBACK); if (err) { @@ -997,11 +979,11 @@ alsa_driver_set_parameters (alsa_driver_t *driver, } pr = p_setup.format.rate; - p_period_size = p_setup.buf.block.frag_size / device->playback_nchannels - / device->playback_sample_bytes; p_periods = p_setup.buf.block.frags; device->playback_sample_format = p_setup.format.format; device->playback_interleaved = p_setup.format.interleave; + device->playback_sample_bytes = snd_pcm_format_width (p_setup.format.format) / 8; + p_period_size = p_setup.buf.block.frag_size / device->playback_nchannels / device->playback_sample_bytes; #else err = alsa_driver_configure_stream ( @@ -1014,7 +996,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->playback_sw_params, &driver->playback_nperiods, &device->playback_nchannels, - device->playback_sample_bytes); + driver->preferred_sample_bytes); if (err) { jack_error ("ALSA: cannot configure playback channel"); @@ -1037,6 +1019,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, device->playback_interleaved = (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) || (access == SND_PCM_ACCESS_MMAP_COMPLEX); + device->playback_sample_bytes = snd_pcm_format_physical_width (device->playback_sample_format) / 8; #endif } @@ -1136,8 +1119,6 @@ alsa_driver_set_parameters (alsa_driver_t *driver, #endif } - alsa_driver_set_sample_bytes(driver, device); - if (do_playback) { err = alsa_driver_check_format(device->playback_sample_format); if(err < 0) { @@ -1922,6 +1903,9 @@ alsa_driver_close (alsa_driver_t *driver) continue; } + device->capture_sample_bytes = 0; + device->capture_sample_format = SND_PCM_FORMAT_UNKNOWN; + snd_pcm_close(device->capture_handle); device->capture_handle = NULL; } @@ -1932,6 +1916,7 @@ alsa_driver_close (alsa_driver_t *driver) continue; } + if (device->playback_linked) { snd_pcm_unlink(device->playback_handle); device->playback_linked = 0; @@ -1941,6 +1926,9 @@ alsa_driver_close (alsa_driver_t *driver) continue; } + device->playback_sample_bytes = 0; + device->playback_sample_format = SND_PCM_FORMAT_UNKNOWN; + snd_pcm_close(device->playback_handle); device->playback_handle = NULL; } @@ -3102,17 +3090,21 @@ alsa_driver_new (char *name, alsa_driver_info_t info, jack_client_t *client) driver->frames_per_cycle = info.frames_per_period; driver->user_nperiods = info.periods_n; + driver->preferred_sample_bytes = info.shorts_first ? 2 : 4; + driver->features = info.features; for (int i = 0; i < driver->devices_count; ++i) { alsa_device_t *device = &driver->devices[i]; if (i < driver->devices_c_count) { - device->capture_sample_bytes = (info.shorts_first ? 2:4); + device->capture_sample_bytes = 0; + device->capture_sample_format = SND_PCM_FORMAT_UNKNOWN; device->capture_name = strdup(info.devices[i].capture_name); device->capture_nchannels = info.devices[i].capture_channels; } if (i < driver->devices_p_count) { - device->playback_sample_bytes = (info.shorts_first ? 2:4); + device->playback_sample_bytes = 0; + device->playback_sample_format = SND_PCM_FORMAT_UNKNOWN; device->playback_name = strdup(info.devices[i].playback_name); device->playback_nchannels = info.devices[i].playback_channels; } diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h index aa3a81300..b9c1fe64c 100644 --- a/linux/alsa/alsa_driver.h +++ b/linux/alsa/alsa_driver.h @@ -54,6 +54,7 @@ #define SND_PCM_FORMAT_S32_LE SND_PCM_SFMT_S32_LE #define SND_PCM_FORMAT_S32_BE SND_PCM_SFMT_S32_BE #define SND_PCM_FORMAT_FLOAT_LE SND_PCM_SFMT_FLOAT_LE +#define SND_PCM_FORMAT_UNKNOWN SND_PCM_SFMT_SPECIAL #define SND_PCM_STATE_PREPARED SND_PCM_STATUS_PREPARED #define SND_PCM_STATE_SUSPENDED SND_PCM_STATUS_SUSPENDED #define SND_PCM_STATE_XRUN SND_PCM_STATUS_UNDERRUN @@ -197,6 +198,8 @@ typedef struct _alsa_driver { char has_hw_monitoring; char has_hw_metering; + int preferred_sample_bytes; + int dither; dither_state_t *dither_state; From 0e957382c656c2e6473ece4c999b2ef5e0c2b8fa Mon Sep 17 00:00:00 2001 From: Adam Miartus Date: Wed, 26 Feb 2020 10:56:58 +0100 Subject: [PATCH 41/42] QTISA-188 alsa: modify sample rate only when device was opened otherwise incorrect sample rate (0) would be used for comparisons Change-Id: I93b733f820171fe3a169a5e9c92b416348e90c5e Signed-off-by: Adam Miartus (cherry picked from commit 94eaf8b0232c0bcda444e9201f36723adbcaeb48) Signed-off-by: Timo Wischer --- linux/alsa/alsa_driver.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 14bcde0c8..d8a419632 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -1025,7 +1025,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, /* original checks done for single device mode */ if (driver->devices_count == 1) { - if (device->capture_handle && device->playback_handle) { + if (do_capture && do_playback) { if (cr != pr) { jack_error ("playback and capture sample rates do " "not match (%d vs. %d)", pr, cr); @@ -1042,13 +1042,13 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->frame_rate = cr; } } - else if (device->capture_handle && cr != driver->frame_rate) { + else if (do_capture && cr != driver->frame_rate) { jack_error ("capture sample rate in use (%d Hz) does not " "match requested rate (%d Hz)", cr, driver->frame_rate); driver->frame_rate = cr; } - else if (device->playback_handle && pr != driver->frame_rate) { + else if (do_playback && pr != driver->frame_rate) { jack_error ("playback sample rate in use (%d Hz) does not " "match requested rate (%d Hz)", pr, driver->frame_rate); From a154717cea28f35a53bba74d87bd27ba1b4ca113 Mon Sep 17 00:00:00 2001 From: Timo Wischer Date: Thu, 5 Mar 2020 08:14:45 +0100 Subject: [PATCH 42/42] example: Add client for reloading the master backend of JACK. Currently this is only supported for the ALSA backend. Signed-off-by: Timo Wischer Change-Id: I4b634056ea1774db0a264fcff121d2854f6b1a60 --- example-clients/reload.c | 32 ++++++++++++++++++++++++++++++++ example-clients/wscript | 1 + 2 files changed, 33 insertions(+) create mode 100644 example-clients/reload.c diff --git a/example-clients/reload.c b/example-clients/reload.c new file mode 100644 index 000000000..7619efd93 --- /dev/null +++ b/example-clients/reload.c @@ -0,0 +1,32 @@ +#include +#include +#include + + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + printf("usage: %s [server_name [server_name ...]]", argv[0]); + return 1; + } + + const int client_count = argc - 1; + char** server_names = &argv[1]; + + jack_client_t* clients[client_count]; + + for (int i = 0; i < client_count; ++i) { + const jack_options_t options = (jack_options_t) (JackNoStartServer | JackServerName); + jack_status_t status; + + printf("Connecting to JACK server %s\n", server_names[i]); + clients[i] = jack_client_open("reload", options, &status, server_names[i]); + jack_client_reload_master(clients[i]); + } + + for (int i = 0; i < client_count; ++i) { + jack_client_close(clients[i]); + } + + return 0; +} diff --git a/example-clients/wscript b/example-clients/wscript index 065fcf157..542e61b06 100644 --- a/example-clients/wscript +++ b/example-clients/wscript @@ -31,6 +31,7 @@ example_programs = { 'jack_midi_latency_test' : 'midi_latency_test.c', 'jack_simdtests' : 'simdtests.cpp', 'jack_property' : 'property.c', + 'jack_reload' : 'reload.c', } example_libs = {