From d4baa88b37b418c2f8207f3c14dbefd2d238fde6 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Tue, 2 Jun 2015 09:22:14 -0500 Subject: [PATCH 01/10] Fix potential buffer overflow in ringbuffer resize jack_ringbuffer_reset_size changed the tracked size of the internal buffer but didn't actually resize the buffer itself, potentially leading to buffer overflows. --- common/ringbuffer.c | 46 ++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/common/ringbuffer.c b/common/ringbuffer.c index 9de844f6..aad38b42 100644 --- a/common/ringbuffer.c +++ b/common/ringbuffer.c @@ -67,21 +67,16 @@ size_t jack_ringbuffer_write_space(const jack_ringbuffer_t *rb); LIB_EXPORT jack_ringbuffer_t * jack_ringbuffer_create (size_t sz) { - int power_of_two; jack_ringbuffer_t *rb; if ((rb = (jack_ringbuffer_t *) malloc (sizeof (jack_ringbuffer_t))) == NULL) { return NULL; } - for (power_of_two = 1; 1 << power_of_two < sz; power_of_two++); + rb->buf = NULL; + jack_ringbuffer_reset_size( rb, sz ); - rb->size = 1 << power_of_two; - rb->size_mask = rb->size; - rb->size_mask -= 1; - rb->write_ptr = 0; - rb->read_ptr = 0; - if ((rb->buf = (char *) malloc (rb->size)) == NULL) { + if (rb->buf == NULL) { free (rb); return NULL; } @@ -129,17 +124,38 @@ jack_ringbuffer_reset (jack_ringbuffer_t * rb) memset(rb->buf, 0, rb->size); } -/* Reset the read and write pointers to zero. This is not thread - safe. */ +/* Reset the size of the ringbuffer. + + Reallocates the internal buffer using the next power-of-two size up from + the requested size. + + If the reallocation fails, the previous buffer is left intact. + */ LIB_EXPORT void jack_ringbuffer_reset_size (jack_ringbuffer_t * rb, size_t sz) { - rb->size = sz; - rb->size_mask = rb->size; - rb->size_mask -= 1; - rb->read_ptr = 0; - rb->write_ptr = 0; + /* Attempt to reallocate buffer to size sz */ + int power_of_two; + for (power_of_two = 1; 1 << power_of_two < sz; power_of_two++); + size_t newsz = 1 << power_of_two; + + void* newbuf = realloc (rb->buf, newsz); + if (NULL != newbuf) + { + rb->buf = newbuf; + rb->size = newsz; + rb->size_mask = rb->size; + rb->size_mask -= 1; + rb->read_ptr = 0; + rb->write_ptr = 0; + } + else + { + /* Reallocation failed. Ringbuffer is left in initial state + * so application must check ringbuffer size to see if + * the resize succeeded. */ + } } /* Return the number of bytes available for reading. This is the From dbdb8216a1c15c9a2464bcca64b07510b0e6259d Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Wed, 3 Jun 2015 13:42:54 -0500 Subject: [PATCH 02/10] Begin JackPosixSemaphore names with slash According to the posix standard, a named semaphore needs to begin with a slash in order to guarantee the same semaphore will be opened by that name by multiple processes. This is an issue on QNX where a named semaphore that does not begin with a slash has the current working directory prepended to the name as a kind of namespacing. --- posix/JackPosixSemaphore.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/posix/JackPosixSemaphore.cpp b/posix/JackPosixSemaphore.cpp index e9503536..96e946b3 100644 --- a/posix/JackPosixSemaphore.cpp +++ b/posix/JackPosixSemaphore.cpp @@ -33,9 +33,9 @@ void JackPosixSemaphore::BuildName(const char* client_name, const char* server_n char ext_client_name[SYNC_MAX_NAME_SIZE + 1]; JackTools::RewriteName(client_name, ext_client_name); if (getenv("JACK_PROMISCUOUS_SERVER")) { - snprintf(res, size, "jack_sem.%s_%s", server_name, ext_client_name); + snprintf(res, size, "/jack_sem.%s_%s", server_name, ext_client_name); } else { - snprintf(res, size, "jack_sem.%d_%s_%s", JackTools::GetUID(), server_name, ext_client_name); + snprintf(res, size, "/jack_sem.%d_%s_%s", JackTools::GetUID(), server_name, ext_client_name); } } From e9a96be098dd39bc97a56cbf8413fc155b792237 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Mon, 17 Aug 2015 17:14:14 -0500 Subject: [PATCH 03/10] Fix c++ identity crisis in external_metro The program is C++ but includes C headers and uses extern "C" clauses around the ExternalMetro class. --- tests/external_metro.cpp | 2 +- tests/external_metro.h | 19 +++++-------------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/tests/external_metro.cpp b/tests/external_metro.cpp index 6ca8bc90..2d5ff022 100644 --- a/tests/external_metro.cpp +++ b/tests/external_metro.cpp @@ -17,7 +17,7 @@ */ #include "external_metro.h" -#include +#include typedef jack_default_audio_sample_t sample_t; diff --git a/tests/external_metro.h b/tests/external_metro.h index 8c618410..317dcb60 100644 --- a/tests/external_metro.h +++ b/tests/external_metro.h @@ -21,18 +21,13 @@ #ifndef __external_metro__ #define __external_metro__ -#ifdef __cplusplus -extern "C" -{ -#endif - -#include -#include -#include +#include +#include +#include #include -#include +#include #include -#include +#include #include #include @@ -63,8 +58,4 @@ extern "C" static void shutdown (void* arg); }; -#ifdef __cplusplus -} -#endif - #endif From ca440c46505098929ade1f01f607cb5959a54e48 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Thu, 27 Aug 2015 11:19:01 -0500 Subject: [PATCH 04/10] Add missing include struct timeval is defined in . Including is explicitly here fixes a compile error on QNX. --- posix/JackNetUnixSocket.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/posix/JackNetUnixSocket.cpp b/posix/JackNetUnixSocket.cpp index d3aeb4ce..d34f3a37 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 b09e046f4e52825538fb58ee40463b13e8c172d2 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Thu, 27 Aug 2015 11:22:11 -0500 Subject: [PATCH 05/10] Fix ambiguous abs(jack_nframes_t) call The expression tries to take the absolute value of an unsigned (jack_nframes_t) which is the difference between two unsigned. Cast the unsigned to signed before taking the difference so a cur_buffer_size larger than delta_time actually produces a meaningful result. --- tests/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test.cpp b/tests/test.cpp index 8a8a8117..f995ea32 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -479,7 +479,7 @@ int process4(jack_nframes_t nframes, void *arg) jack_nframes_t delta_time = cur_time - last_time; Log("calling process4 callback : jack_frame_time = %ld delta_time = %ld\n", cur_time, delta_time); - if (delta_time > 0 && (jack_nframes_t)abs(delta_time - cur_buffer_size) > tolerance) { + if (delta_time > 0 && (jack_nframes_t)abs((long int)delta_time - (long int)cur_buffer_size) > tolerance) { printf("!!! ERROR !!! jack_frame_time seems to return incorrect values cur_buffer_size = %d, delta_time = %d tolerance %d\n", cur_buffer_size, delta_time, tolerance); } From 7978a9e18ac09a0e384a748b5f3c63e2dbe76060 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Thu, 27 Aug 2015 11:17:14 -0500 Subject: [PATCH 06/10] Add Basic QNX support Adds enough support and platform-specific files to get JACK building on QNX SDP 6.6.0. This does not include any integration with the QNX audio system, io-audio. --- common/JackControlAPI.cpp | 5 + common/jack/systemdeps.h | 2 +- common/shm.c | 45 ++++++++ common/wscript | 44 ++++++++ dbus/wscript | 4 + example-clients/wscript | 9 +- qnx/JackAtomic_os.h | 86 +++++++++++++++ qnx/JackPlatformPlug_os.h | 90 ++++++++++++++++ qnx/JackQnxTime.c | 216 ++++++++++++++++++++++++++++++++++++++ qnx/cycles.h | 31 ++++++ qnx/wscript | 38 +++++++ wscript | 25 ++++- 12 files changed, 592 insertions(+), 3 deletions(-) create mode 100644 qnx/JackAtomic_os.h create mode 100644 qnx/JackPlatformPlug_os.h create mode 100644 qnx/JackQnxTime.c create mode 100644 qnx/cycles.h create mode 100644 qnx/wscript diff --git a/common/JackControlAPI.cpp b/common/JackControlAPI.cpp index a733f5d9..fe3b873c 100644 --- a/common/JackControlAPI.cpp +++ b/common/JackControlAPI.cpp @@ -661,7 +661,12 @@ jackctl_setup_signals( sigfillset(&allsignals); action.sa_handler = signal_handler; action.sa_mask = allsignals; +#ifdef __QNX__ + // SA_RESTART is not supported in QNX 6.6.0 + action.sa_flags = SA_RESETHAND; +#else action.sa_flags = SA_RESTART|SA_RESETHAND; +#endif for (i = 1; i < NSIG; i++) { diff --git a/common/jack/systemdeps.h b/common/jack/systemdeps.h index 1f62cb64..1f2d6014 100644 --- a/common/jack/systemdeps.h +++ b/common/jack/systemdeps.h @@ -103,7 +103,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(__QNX__) || defined(__sun__) || defined(sun) || defined(__unix__) || defined(__CYGWIN__) || defined(GNU_WIN32) #if defined(__CYGWIN__) || defined(GNU_WIN32) #include diff --git a/common/shm.c b/common/shm.c index c52dfa50..36839425 100644 --- a/common/shm.c +++ b/common/shm.c @@ -49,7 +49,9 @@ #include #include #include +#ifndef USE_POSIX_SHM #include +#endif #include #include @@ -176,6 +178,49 @@ semaphore_init () {return 0;} static int semaphore_add (int value) {return 0;} +#elif defined(__QNX__) +#include + +static sem_t sem; + +/* all semaphore errors are fatal -- issue message, but do not return */ +static void +semaphore_error (char *msg) +{ + jack_error ("JACK semaphore error: %s (%s)", + msg, strerror (errno)); +} + +static int +semaphore_init () +{ + semid = sem_init(&sem, JACK_SEMAPHORE_KEY, 1); + + if( semid == -1) + { + semaphore_error("semaphore_init()"); + } + + + return 0; +} + +static inline int +semaphore_add (int value) +{ + while( value > 0 ) + { + sem_post( &sem ); + --value; + } + while( value < 0 ) + { + sem_wait( &sem ); + ++value; + } + return 0; +} + #else /* all semaphore errors are fatal -- issue message, but do not return */ static void diff --git a/common/wscript b/common/wscript index fcf1d495..8356b6a7 100644 --- a/common/wscript +++ b/common/wscript @@ -26,6 +26,8 @@ def create_jack_process_obj(bld, target, sources, uselib = None): 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', '../qnx/ioaudio'] if bld.env['IS_SUN']: env_includes = ['../solaris', '../posix', '../solaris/oss'] if bld.env['IS_WINDOWS']: @@ -36,6 +38,8 @@ def create_jack_process_obj(bld, target, sources, uselib = None): process.source = sources if bld.env['IS_LINUX']: process.env.append_value("CPPFLAGS", "-fvisibility=hidden") + if bld.env['IS_QNX']: + process.env.append_value("CPPFLAGS", "-fvisibility=hidden") if bld.env['IS_MACOSX']: process.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64") #process.env.append_value("LINKFLAGS", "-arch i386 -arch ppc -arch x86_64") @@ -93,6 +97,22 @@ def build(bld): uselib.append('RT') uselib.append('DL') + if bld.env['IS_QNX']: + common_libsources += [ + 'JackDebugClient.cpp', + 'timestamps.c', + '../posix/JackPosixThread.cpp', + '../posix/JackPosixSemaphore.cpp', + '../posix/JackPosixProcessSync.cpp', + '../posix/JackPosixMutex.cpp', + '../posix/JackSocket.cpp', + '../qnx/JackQnxTime.c', + ] + includes = ['../qnx', '../posix'] + includes + uselib.append('RT') + uselib.append('DL') + uselib.append('SOCKET') + if bld.env['IS_SUN']: common_libsources += [ 'JackDebugClient.cpp', @@ -158,6 +178,12 @@ def build(bld): '../posix/JackPosixServerLaunch.cpp', ] + if bld.env['IS_QNX']: + clientlib.source += [ + '../posix/JackSocketClientChannel.cpp', + '../posix/JackPosixServerLaunch.cpp', + ] + if bld.env['IS_SUN']: clientlib.source += [ '../posix/JackSocketClientChannel.cpp', @@ -183,6 +209,9 @@ def build(bld): if bld.env['IS_LINUX']: clientlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") + if bld.env['IS_QNX']: + clientlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") + if bld.env['IS_MACOSX']: clientlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") clientlib.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64") @@ -252,6 +281,14 @@ def build(bld): '../posix/JackNetUnixSocket.cpp', ] + if bld.env['IS_QNX']: + serverlib.source += [ + '../posix/JackSocketServerChannel.cpp', + '../posix/JackSocketNotifyChannel.cpp', + '../posix/JackSocketServerNotifyChannel.cpp', + '../posix/JackNetUnixSocket.cpp', + ] + if bld.env['IS_SUN']: serverlib.source += [ '../posix/JackSocketServerChannel.cpp', @@ -283,6 +320,9 @@ def build(bld): if bld.env['IS_LINUX']: serverlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") + if bld.env['IS_QNX']: + serverlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") + if bld.env['IS_MACOSX']: serverlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") serverlib.env.append_value("CPPFLAGS", "-mmacosx-version-min=10.4 -arch i386 -arch ppc -arch x86_64") @@ -321,6 +361,10 @@ def build(bld): netlib.source += ['../posix/JackNetUnixSocket.cpp','../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', '../linux/JackLinuxTime.c'] netlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") + if bld.env['IS_QNX']: + netlib.source += ['../posix/JackNetUnixSocket.cpp','../posix/JackPosixThread.cpp','../posix/JackPosixMutex.cpp','../qnx/JackQnxTime.c'] + netlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") + if bld.env['IS_SUN']: netlib.source += ['../posix/JackNetUnixSocket.cpp','../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', '../solaris/JackSolarisTime.c'] netlib.env.append_value("CPPFLAGS", "-fvisibility=hidden") diff --git a/dbus/wscript b/dbus/wscript index ece9198f..d9b7da5f 100644 --- a/dbus/wscript +++ b/dbus/wscript @@ -41,6 +41,8 @@ def build(bld): sysdeps_dbus_include = ['../linux', '../posix'] if bld.env['IS_MACOSX']: sysdeps_dbus_include = ['../macosx', '../posix'] + if bld.env['IS_QNX']: + sysdeps_dbus_include = ['../qnx', '../posix'] obj.includes = sysdeps_dbus_include + ['.', '../', '../common', '../common/jack'] obj.defines = ['HAVE_CONFIG_H','SERVER_SIDE'] @@ -66,6 +68,8 @@ def build(bld): obj.use += ['PTHREAD', 'DL', 'RT', 'DBUS-1', 'EXPAT', 'STDC++'] if bld.env['IS_MACOSX']: obj.use += ['PTHREAD', 'DL', 'DBUS-1', 'EXPAT'] + if bld.env['IS_QNX']: + obj.use += ['PTHREAD', 'DL', 'DBUS-1', 'EXPAT', 'STDC++'] obj.target = 'jackdbus' # process org.jackaudio.service.in -> org.jackaudio.service diff --git a/example-clients/wscript b/example-clients/wscript index ba67614e..33875011 100644 --- a/example-clients/wscript +++ b/example-clients/wscript @@ -47,6 +47,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']: @@ -75,11 +77,12 @@ def build(bld): prog.env.append_value("LINKFLAGS", "") if bld.env['IS_LINUX']: prog.use += ['RT', 'M'] + if bld.env['IS_QNX']: + prog.use += ['M','SOCKET'] if bld.env['IS_SUN']: prog.use += ['M'] prog.target = example_program - if bld.env['BUILD_EXAMPLE_CLIENT_TRANSPORT']: prog = bld(features = 'c cprogram') prog.includes = os_incdir + ['../common/jack', '../common'] @@ -95,6 +98,8 @@ def build(bld): prog.use += ['READLINE'] if bld.env['IS_WINDOWS']: prog.use += ['READLINE'] + if bld.env['IS_QNX']: + prog.use += ['READLINE'] prog.target = 'jack_transport' if bld.env['BUILD_EXAMPLE_CLIENT_REC']: @@ -109,6 +114,8 @@ def build(bld): prog.use += ['SNDFILE'] if bld.env['IS_LINUX']: prog.use += ['RT', 'SNDFILE'] + if bld.env['IS_QNX']: + prog.use += ['SNDFILE'] if bld.env['IS_SUN']: prog.use += ['RT', 'SNDFILE'] if bld.env['IS_WINDOWS']: diff --git a/qnx/JackAtomic_os.h b/qnx/JackAtomic_os.h new file mode 100644 index 00000000..61ffc7e0 --- /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_qnx__ +#define __JackAtomic_qnx__ + +#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/JackPlatformPlug_os.h b/qnx/JackPlatformPlug_os.h new file mode 100644 index 00000000..0f401451 --- /dev/null +++ b/qnx/JackPlatformPlug_os.h @@ -0,0 +1,90 @@ +/* +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_qnx__ +#define __JackPlatformPlug_qnx__ + +#define jack_server_dir "/dev/shmem" +#define jack_client_dir "/dev/shmem" +#define JACK_DEFAULT_DRIVER "ioaudio" + +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; } + +/* __JackPlatformSynchro__ client activation */ +/* +#include "JackFifo.h" +namespace Jack { typedef JackFifo JackSynchro; } +*/ + +#include "JackPosixSemaphore.h" +namespace Jack { typedef JackPosixSemaphore 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/JackQnxTime.c b/qnx/JackQnxTime.c new file mode 100644 index 00000000..371e9f0d --- /dev/null +++ b/qnx/JackQnxTime.c @@ -0,0 +1,216 @@ +/* +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 "cycles.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/cycles.h b/qnx/cycles.h new file mode 100644 index 00000000..d0d9df2e --- /dev/null +++ b/qnx/cycles.h @@ -0,0 +1,31 @@ +/* + Copyright (C) 2001 Paul Davis + Code derived from various headers from the Linux kernel + + 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: cycles.h,v 1.4.2.1 2006/06/20 14:44:00 letz Exp $ +*/ + +#ifndef __jack_cycles_h__ +#define __jack_cycles_h__ + +#include +//#include + +typedef uint64_t cycles_t; +#define get_cycles() ClockCycles() + +#endif /* __jack_cycles_h__ */ diff --git a/qnx/wscript b/qnx/wscript new file mode 100644 index 00000000..31148878 --- /dev/null +++ b/qnx/wscript @@ -0,0 +1,38 @@ +def configure(conf): + conf.check_cc(lib='asound', header_name='audio_driver.h', uselib_store='IOAUDIO', define_name="HAVE_IOAUDIO", mandatory=False) + if conf.is_defined('HAVE_IOAUDIO'): + conf.env['BUILD_DRIVER_IOAUDIO'] = True + +def create_jack_driver_obj(bld, target, sources, uselib=None): + driver = bld(features=['c', 'cxx', 'cxxshlib', 'cshlib']) + driver.env['cxxshlib_PATTERN'] = 'jack_%s.so' + + driver.defines = ['HAVE_CONFIG_H', 'SERVER_SIDE', 'HAVE_PPOLL', 'HAVE_TIMERFD'] + + driver.includes = ['.', '../qnx', '../posix', '../common', '../common/jack', '../dbus', '../'] + driver.target = target + driver.source = sources + driver.install_path = '${ADDON_DIR}/' + driver.use = [] + driver.cflags = [] + if uselib: + driver.use += uselib + return driver + +def build(bld): + if bld.env['BUILD_JACKD'] == True: + jackd = bld(features=['cxx', 'cxxprogram']) + jackd.includes = ['../qnx', '../posix', '../common/jack', '../common', '../dbus', '../'] + jackd.defines = ['HAVE_CONFIG_H', 'SERVER_SIDE'] + jackd.source = ['../common/Jackdmp.cpp'] + jackd.use = ['serverlib'] + if bld.env['IS_QNX'] and bld.env['BUILD_JACKDBUS']: + jackd.source += ['../dbus/reserve.c', '../dbus/audio_reserve.c'] + jackd.use += ['PTHREAD', 'DL', 'RT', 'M', 'STDC++', 'DBUS-1'] + else: + jackd.use += ['PTHREAD', 'DL', 'RT', 'M', 'STDC++'] + jackd.target = 'jackd' + + create_jack_driver_obj(bld, 'dummy', '../common/JackDummyDriver.cpp') + + create_jack_driver_obj(bld, 'loopback', '../common/JackLoopbackDriver.cpp') diff --git a/wscript b/wscript index 561373df..a3fe2ae3 100644 --- a/wscript +++ b/wscript @@ -395,7 +395,7 @@ def options(opt): opt.add_option('--mandir', type='string', help="Manpage directory [Default: /share/man/man1]") # options affecting binaries - opt.add_option('--dist-target', type='string', default='auto', help='Specify the target for cross-compiling [auto,mingw]') + opt.add_option('--dist-target', type='string', default='auto', help='Specify the target for cross-compiling [auto,mingw,qnx]') opt.add_option('--mixed', action='store_true', default=False, help='Build with 32/64 bits mixed mode') opt.add_option('--debug', action='store_true', default=False, dest='debug', help='Build debuggable binaries') @@ -457,6 +457,8 @@ def configure(conf): conf.env['IS_LINUX'] = True elif Options.options.dist_target == 'mingw': conf.env['IS_WINDOWS'] = True + elif Options.options.dist_target == 'qnx': + conf.env['IS_QNX'] = True if conf.env['IS_LINUX']: Logs.pprint('CYAN', "Linux detected") @@ -464,6 +466,9 @@ def configure(conf): if conf.env['IS_MACOSX']: Logs.pprint('CYAN', "MacOS X detected") + if conf.env['IS_QNX']: + Logs.pprint('CYAN', "Cross compile to QNX") + if conf.env['IS_SUN']: Logs.pprint('CYAN', "SunOS detected") @@ -483,6 +488,8 @@ def configure(conf): conf.recurse('common') if conf.env['IS_LINUX']: conf.recurse('linux') + if conf.env['IS_QNX']: + conf.recurse('qnx') if Options.options.dbus: conf.recurse('dbus') if conf.env['BUILD_JACKDBUS'] != True: @@ -495,6 +502,14 @@ def configure(conf): conf.env['LIB_RT'] = ['rt'] conf.env['LIB_M'] = ['m'] conf.env['LIB_STDC++'] = ['stdc++'] + + if conf.env['IS_QNX']: + conf.env['LIB_PTHREAD'] = ['c'] + conf.env['LIB_DL'] = ['c'] + conf.env['LIB_RT'] = [] + conf.check_cxx( lib=['rt'], uselib_store='RT', mandatory=False ) + conf.check_cxx( lib=['socket'], uselib_store='SOCKET' ) + conf.env['JACK_API_VERSION'] = JACK_API_VERSION conf.env['JACK_VERSION'] = VERSION @@ -716,6 +731,14 @@ def build(bld): if bld.env['BUILD_JACKDBUS'] == True: bld.recurse('dbus') + if bld.env['IS_QNX']: + bld.recurse('qnx') + bld.recurse('example-clients') + bld.recurse('tests') + bld.recurse('man') + if bld.env['BUILD_JACKDBUS'] == True: + bld.recurse('dbus') + if bld.env['IS_SUN']: bld.recurse('solaris') bld.recurse('example-clients') From 05dd5c43d31a407e80aef08f1edbec232a1aefdc Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Mon, 17 Aug 2015 17:11:00 -0500 Subject: [PATCH 07/10] Add QNX io-audio adapter Adds a JACK internal client to interact with QNX io-audio system. --- common/JackAudioAdapterFactory.cpp | 5 + common/wscript | 5 + qnx/ioaudio/JackIoAudioAdapter.cpp | 1227 ++++++++++++++++++++++++++++ qnx/ioaudio/JackIoAudioAdapter.h | 295 +++++++ tests/qnx/TestIoAudioAdapter.cpp | 56 ++ tests/qnx/TestIoAudioInterface.cpp | 44 + 6 files changed, 1632 insertions(+) create mode 100644 qnx/ioaudio/JackIoAudioAdapter.cpp create mode 100644 qnx/ioaudio/JackIoAudioAdapter.h create mode 100644 tests/qnx/TestIoAudioAdapter.cpp create mode 100644 tests/qnx/TestIoAudioInterface.cpp diff --git a/common/JackAudioAdapterFactory.cpp b/common/JackAudioAdapterFactory.cpp index 9ceeee04..f94dc84c 100644 --- a/common/JackAudioAdapterFactory.cpp +++ b/common/JackAudioAdapterFactory.cpp @@ -35,6 +35,11 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #define JackPlatformAdapter JackAlsaAdapter #endif +#ifdef __QNX__ +#include "JackIoAudioAdapter.h" +#define JackPlatformAdapter JackIoAudioAdapter +#endif + #if defined(__sun__) || defined(sun) #include "JackOSSAdapter.h" #define JackPlatformAdapter JackOSSAdapter diff --git a/common/wscript b/common/wscript index 8356b6a7..3d5390db 100644 --- a/common/wscript +++ b/common/wscript @@ -414,6 +414,11 @@ def build(bld): process = create_jack_process_obj(bld, 'audioadapter', audio_adapter_sources, serverlib) process.use = ['ALSA', 'SAMPLERATE'] + if bld.env['BUILD_ADAPTER'] and bld.env['IS_QNX']: + audio_adapter_sources += ['../qnx/ioaudio/JackIoAudioAdapter.cpp'] + process = create_jack_process_obj(bld, 'audioadapter', audio_adapter_sources, serverlib) + process.use = ['IOAUDIO', 'SAMPLERATE'] + if bld.env['BUILD_ADAPTER'] and bld.env['IS_SUN']: audio_adapter_sources += ['../solaris/oss/JackOSSAdapter.cpp', 'memops.c'] process = create_jack_process_obj(bld, 'audioadapter', audio_adapter_sources, serverlib) diff --git a/qnx/ioaudio/JackIoAudioAdapter.cpp b/qnx/ioaudio/JackIoAudioAdapter.cpp new file mode 100644 index 00000000..b285e335 --- /dev/null +++ b/qnx/ioaudio/JackIoAudioAdapter.cpp @@ -0,0 +1,1227 @@ +/* + Copyright (C) 2008 Grame + + 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. + + */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include "JackIoAudioAdapter.h" +#include "JackGlobals.h" +#include "JackEngineControl.h" + +#define check_error(err) if (err) { jack_error("%s:%d, io-audio error %d : %s", __FILE__, __LINE__, err, snd_strerror(err)); return err; } +#define check_error_msg(err,msg) if (err) { jack_error("%s:%d, %s : %s(%d)", __FILE__, __LINE__, msg, snd_strerror(err), err); return err; } +#define display_error_msg(err,msg) if (err) { jack_error("%s:%d, %s : %s(%d)", __FILE__, __LINE__, msg, snd_strerror(err), err); } + +namespace Jack +{ + + template + int snd_pcm_malloc_struct( + T** ptr ) + { + T* tmp = (T*)malloc( sizeof(T) ); + if( NULL == tmp ) + { + *ptr = NULL; + return -1; + } + *ptr = tmp; + memset( tmp, + 0, + sizeof(T) ); + return 0; + } + + template + int snd_pcm_free_struct( + T** ptr ) + { + if( NULL != *ptr ) + { + free( *ptr ); + *ptr = NULL; + } + + return 0; + } + + inline void* aligned_calloc( + size_t nmemb, + size_t size ) + { + return (void*)calloc( nmemb, + size ); + } + + AudioInterface::AudioInterface( + const AudioParam& ap ) : + fParams( ap ) + { + AudioInterface_common(); + } + + AudioInterface::AudioInterface( + jack_nframes_t buffer_size, + jack_nframes_t sample_rate ) : + fParams( buffer_size, + sample_rate ) + { + AudioInterface_common(); + } + + int AudioInterface::AudioInterface_common() + { + fInputDevice = NULL; + check_error( snd_pcm_malloc_struct( &fInputParams ) ); + check_error( snd_pcm_malloc_struct( &fInputSetup ) ); + fInputCardBuffer = NULL; + fJackInputBuffers = + (jack_default_audio_sample_t**)calloc( NUM_BUFFERS, + sizeof(jack_default_audio_sample_t*) ); + + fOutputDevice = NULL; + check_error( snd_pcm_malloc_struct( &fOutputParams ) ); + check_error( snd_pcm_malloc_struct( &fOutputSetup ) ); + fOutputCardBuffer = NULL; + fJackOutputBuffers = + (jack_default_audio_sample_t**)calloc( NUM_BUFFERS, + sizeof(jack_default_audio_sample_t*) ); + + return EOK; + } + + AudioInterface::~AudioInterface() + { + snd_pcm_free_struct( &fInputParams ); + snd_pcm_free_struct( &fInputSetup ); + free( fJackInputBuffers ); + + snd_pcm_free_struct( &fOutputParams ); + snd_pcm_free_struct( &fOutputSetup ); + free( fJackOutputBuffers ); + } + + /** + * Open the audio interface + */ + int AudioInterface::open() + { + /////////////////////////////////////////////////////////////////////// + // Declare Local Variables + /////////////////////////////////////////////////////////////////////// + int err; + int card; + int device; + + /////////////////////////////////////////////////////////////////////// + // Input Device Setup + /////////////////////////////////////////////////////////////////////// + + if( strncmp( fParams.fInputCardName, + "none", + 4 ) != 0 ) + { + + check_error( + snd_pcm_open_name( &fInputDevice, + fParams.fInputCardName, + SND_PCM_OPEN_CAPTURE ) ); + + // Disable plugin conversions for this device + err = snd_pcm_plugin_set_disable( fInputDevice, + PLUGIN_BUFFER_PARTIAL_BLOCKS ); + err = snd_pcm_plugin_set_disable( fInputDevice, + PLUGIN_MMAP ); + err = snd_pcm_plugin_set_disable( fInputDevice, + PLUGIN_ROUTING ); + err = snd_pcm_plugin_set_disable( fInputDevice, + PLUGIN_CONVERSION ); + + // get channel parameters + snd_pcm_channel_info_t inputInfo; + memset( &inputInfo, + 0, + sizeof( inputInfo ) ); + inputInfo.channel = SND_PCM_CHANNEL_CAPTURE; + check_error( snd_pcm_channel_info( fInputDevice, + &inputInfo ) ); + + //get hardware input parameters + fInputParams->mode = SND_PCM_MODE_BLOCK; + fInputParams->channel = SND_PCM_CHANNEL_CAPTURE; + //fInputParams->sync = Not Supported + + // Check supported formats, preferring in order: float, sint32, sint16, plugin converted + if( inputInfo.formats & SND_PCM_FMT_FLOAT ) + { + fInputParams->format.format = SND_PCM_SFMT_FLOAT; + } + else if( inputInfo.formats & SND_PCM_FMT_S32_LE ) + { + fInputParams->format.format = SND_PCM_SFMT_S32_LE; + } + else if( inputInfo.formats & SND_PCM_FMT_S16_LE ) + { + fInputParams->format.format = SND_PCM_SFMT_S16_LE; + } + else + { + // Re-enable format conversion plugin if device can't accept float, sint32 or sint16 + snd_pcm_plugin_set_enable( fInputDevice, + PLUGIN_CONVERSION ); + fInputParams->format.format = SND_PCM_SFMT_FLOAT; + } + + fInputParams->format.interleave = 1; + fInputParams->format.rate = fParams.fFrequency; + fInputParams->format.voices = fNumInputPorts; + //fInputParams->digital = Not Implemented + fInputParams->start_mode = SND_PCM_START_DATA; + fInputParams->stop_mode = SND_PCM_STOP_STOP; + //fInputParams->time = 1; + //fInputParams->ust_time + fInputParams->buf.block.frag_size = + snd_pcm_format_size( fInputParams->format.format, + fParams.fBuffering * fNumInputPorts ); + ; + fInputParams->buf.block.frags_max = fParams.fPeriod; + fInputParams->buf.block.frags_min = 1; + strcpy( fInputParams->sw_mixer_subchn_name, + "Jack Capture Channel" ); + + //set params record with initial values + if( ( err = snd_pcm_channel_params( fInputDevice, + fInputParams ) ) != 0 ) + { + jack_error( "%s:%d, io-audio error %d : %s, why_failed = %d", + __FILE__, + __LINE__, + err, + snd_strerror( err ), + fInputParams->why_failed ); + return err; + } + + //set nonblocking mode + //check_error(snd_pcm_nonblock_mode(fInputDevice, 1)); + + //get params record with actual values + fInputSetup->channel = SND_PCM_CHANNEL_CAPTURE; + check_error( snd_pcm_channel_setup( fInputDevice, + fInputSetup ) ); + + //set hardware buffers + fInputFormat = fInputSetup->format; + fNumInputPorts = fInputFormat.voices; + ssize_t frameSize = snd_pcm_format_size( fInputFormat.format, + fInputFormat.voices ); + fInputBufferFrames = fInputSetup->buf.block.frag_size + / ( frameSize ); + + size_t nfrags = fInputSetup->buf.block.frags_max; + size_t nframes = fInputBufferFrames; + size_t framesize = frameSize; + + fInputCardBuffer = aligned_calloc( nfrags, + nframes * framesize ); + + //create floating point buffers needed by the JACK + for( unsigned int i = 0; i < fNumInputPorts; i++ ) + { + fJackInputBuffers[i] = + (jack_default_audio_sample_t*)aligned_calloc( + fInputBufferFrames, + sizeof(jack_default_audio_sample_t) ); + for( unsigned int j = 0; j < fInputBufferFrames; j++ ) + fJackInputBuffers[i][j] = 0.0; + } + } + /////////////////////////////////////////////////////////////////////// + // Output Device Setup + /////////////////////////////////////////////////////////////////////// + + if( strncmp( fParams.fOutputCardName, + "none", + 4 ) != 0 ) + { + + check_error( + snd_pcm_open_name( &fOutputDevice, + fParams.fOutputCardName, + SND_PCM_OPEN_PLAYBACK ) ); + + // Disable plugin conversions for this device + err = snd_pcm_plugin_set_disable( fOutputDevice, + PLUGIN_BUFFER_PARTIAL_BLOCKS ); + err = snd_pcm_plugin_set_disable( fOutputDevice, + PLUGIN_MMAP ); + err = snd_pcm_plugin_set_disable( fOutputDevice, + PLUGIN_ROUTING ); + err = snd_pcm_plugin_set_disable( fOutputDevice, + PLUGIN_CONVERSION ); + + snd_pcm_channel_info_t outputInfo; + memset( &outputInfo, + 0, + sizeof( outputInfo ) ); + outputInfo.channel = SND_PCM_CHANNEL_PLAYBACK; + check_error( snd_pcm_channel_info( fOutputDevice, + &outputInfo ) ); + + //get hardware output parameters + check_error( snd_pcm_malloc_struct( &fOutputParams ) ); + fOutputParams->channel = SND_PCM_CHANNEL_PLAYBACK; + fOutputParams->mode = SND_PCM_MODE_BLOCK; + //fOutputParams->sync = Not Supported + + // Check supported formats, preferring in order: float, sint32, sint16, plugin converted + if( outputInfo.formats & SND_PCM_FMT_FLOAT ) + { + fOutputParams->format.format = SND_PCM_SFMT_FLOAT; + } + else if( outputInfo.formats & SND_PCM_FMT_S32_LE ) + { + fOutputParams->format.format = SND_PCM_SFMT_S32_LE; + } + else if( outputInfo.formats & SND_PCM_FMT_S16_LE ) + { + fOutputParams->format.format = SND_PCM_SFMT_S16_LE; + } + else + { + // Re-enable format conversion plugin if device can't accept float, sint32 or sint16 + snd_pcm_plugin_set_enable( fOutputDevice, + PLUGIN_CONVERSION ); + fOutputParams->format.format = SND_PCM_SFMT_FLOAT; + } + + fOutputParams->format.interleave = 1; + fOutputParams->format.rate = fParams.fFrequency; + fOutputParams->format.voices = fNumOutputPorts; + //fOutputParams->digital = Not Implemented + fOutputParams->start_mode = SND_PCM_START_DATA; + fOutputParams->stop_mode = SND_PCM_STOP_STOP; + fOutputParams->time = 1; + //fOutputParams->ust_time + fOutputParams->buf.block.frag_size = + snd_pcm_format_size( fOutputParams->format.format, + fParams.fBuffering * fNumOutputPorts ); + fOutputParams->buf.block.frags_max = fParams.fPeriod; + fOutputParams->buf.block.frags_min = 1; + strcpy( fOutputParams->sw_mixer_subchn_name, + "Jack Playback Channel" ); + + //set params record with initial values + if( ( err = snd_pcm_channel_params( fOutputDevice, + fOutputParams ) ) != 0 ) + { + jack_error( "%s:%d, io-audio error %d : %s, why_failed = %d", + __FILE__, + __LINE__, + err, + snd_strerror( err ), + fOutputParams->why_failed ); + return err; + } + +// //set nonblocking mode +// check_error(snd_pcm_nonblock_mode(fOutputDevice, 1)); + + //get params record with actual values + check_error( snd_pcm_channel_setup( fOutputDevice, + fOutputSetup ) ); + + //set hardware buffers + fOutputFormat = fOutputSetup->format; + + fNumOutputPorts = fOutputFormat.voices; + ssize_t frameSize = snd_pcm_format_size( fOutputFormat.format, + fOutputFormat.voices ); + fOutputBufferFrames = fOutputSetup->buf.block.frag_size + / ( frameSize ); + + size_t nfrags = fOutputSetup->buf.block.frags_max; + size_t nframes = fOutputBufferFrames; + size_t framesize = frameSize; + + fOutputCardBuffer = aligned_calloc( nfrags, + nframes * framesize ); + + //create floating point buffers needed by the JACK + for( unsigned int i = 0; i < fNumOutputPorts; i++ ) + { + fJackOutputBuffers[i] = + (jack_default_audio_sample_t*)aligned_calloc( + fOutputBufferFrames, + sizeof(jack_default_audio_sample_t) ); + for( unsigned int j = 0; j < fOutputBufferFrames; j++ ) + fJackOutputBuffers[i][j] = 0.0; + } + } + + return 0; + } + + int AudioInterface::close() + { + snd_pcm_close( fInputDevice ); + snd_pcm_close( fOutputDevice ); + + for( unsigned int i = 0; i < fNumInputPorts; i++ ) + if( fJackInputBuffers[i] ) + free( fJackInputBuffers[i] ); + + for( unsigned int i = 0; i < fNumOutputPorts; i++ ) + if( fJackOutputBuffers[i] ) + free( fJackOutputBuffers[i] ); + +// for (unsigned int i = 0; i < fNumInputPorts; i++) +// if (fInputCardBuffer[i]) +// free(fInputCardBuffer[i]); +// +// for (unsigned int i = 0; i < fNumOutputPorts; i++) +// if (fOutputCardBuffer[i]) +// free(fOutputCardBuffer[i]); + + if( fInputCardBuffer ) + free( fInputCardBuffer ); + if( fOutputCardBuffer ) + free( fOutputCardBuffer ); + + return 0; + } + + ssize_t AudioInterface::interleavedBufferSize( + snd_pcm_channel_params_t* params ) + { + return params->buf.block.frag_size * params->format.voices; + } + + ssize_t AudioInterface::noninterleavedBufferSize( + snd_pcm_channel_params_t* params ) + { + return params->buf.block.frag_size; + } + + /** + * Read audio samples from the audio card. Convert samples to floats and take + * care of interleaved buffers + */ + int AudioInterface::read() + { + ssize_t count; + unsigned int s; + unsigned int c; + if( NULL != fInputDevice ) + { + switch( fInputFormat.interleave ) + { + case 1: + count = snd_pcm_read( fInputDevice, + fInputCardBuffer, + fInputBufferFrames ); + if( count < 0 ) + { + display_error_msg( count, + "reading interleaved samples" ); + check_error_msg( + snd_pcm_channel_prepare( fInputDevice, + SND_PCM_CHANNEL_CAPTURE ), + "preparing input stream" ); + } + jack_log( "JackIoAudioAdapter read %ld interleaved bytes", + count ); + if( fInputFormat.format == SND_PCM_SFMT_S16 ) + { + short* buffer16b = (short*)fInputCardBuffer; + for( s = 0; s < fInputBufferFrames; s++ ) + for( c = 0; c < fNumInputPorts; c++ ) + fJackInputBuffers[c][s] = + jack_default_audio_sample_t( + buffer16b[c + + s + * fNumInputPorts] ) + * ( jack_default_audio_sample_t( 1.0 ) + / jack_default_audio_sample_t( + SHRT_MAX ) ); + } + else // SND_PCM_SFMT_S32 + { + int32_t* buffer32b = (int32_t*)fInputCardBuffer; + for( s = 0; s < fInputBufferFrames; s++ ) + for( c = 0; c < fNumInputPorts; c++ ) + fJackInputBuffers[c][s] = + jack_default_audio_sample_t( + buffer32b[c + + s + * fNumInputPorts] ) + * ( jack_default_audio_sample_t( 1.0 ) + / jack_default_audio_sample_t( INT_MAX ) ); + } + break; + case 0: + count = snd_pcm_read( fInputDevice, + fInputCardBuffer, + fInputBufferFrames ); + if( count < 0 ) + { + display_error_msg( count, + "reading non-interleaved samples" ); + check_error_msg( + snd_pcm_channel_prepare( fInputDevice, + SND_PCM_CHANNEL_CAPTURE ), + "preparing input stream" ); + } + jack_log( "JackIoAudioAdapter read %ld non-interleaved bytes", + count ); + if( fInputFormat.format == SND_PCM_SFMT_S16 ) + { + short* buffer16b; + for( c = 0; c < fNumInputPorts; c++ ) + { + buffer16b = (short*)fInputCardBuffer; + for( s = 0; s < fInputBufferFrames; s++ ) + fJackInputBuffers[c][s] = + jack_default_audio_sample_t( buffer16b[s] ) + * ( jack_default_audio_sample_t( 1.0 ) + / jack_default_audio_sample_t( + SHRT_MAX ) ); + } + } + else // SND_PCM_SFMT_S32 + { + int32_t* buffer32b; + for( c = 0; c < fNumInputPorts; c++ ) + { + buffer32b = (int32_t*)fInputCardBuffer; + for( s = 0; s < fInputBufferFrames; s++ ) + fJackInputBuffers[c][s] = + jack_default_audio_sample_t( + buffer32b[s] ) + * ( jack_default_audio_sample_t( 1.0 ) + / jack_default_audio_sample_t( INT_MAX ) ); + } + } + break; + default: + check_error_msg( -10000, + "unknown access mode" ) + ; + break; + } + } + return 0; + } + + /** + * write the output soft channels to the audio card. Convert sample + * format and interleaves buffers when needed + */ + int AudioInterface::write() + { + ssize_t amount; + ssize_t count; + unsigned int f; + unsigned int c; + if( NULL != fOutputDevice ) + { + recovery: switch( fOutputFormat.interleave ) + { + case 1: + if( fOutputFormat.format == SND_PCM_SFMT_S16 ) + { + short* buffer16b = (short*)fOutputCardBuffer; + for( f = 0; f < fOutputBufferFrames; f++ ) + { + for( c = 0; c < fNumOutputPorts; c++ ) + { + jack_default_audio_sample_t x = + fJackOutputBuffers[c][f]; + buffer16b[c + ( f * fNumOutputPorts )] = + short( + max(min (x, jack_default_audio_sample_t(1.0)), jack_default_audio_sample_t(-1.0)) + * jack_default_audio_sample_t( + SHRT_MAX ) ); + } + } + } + else // SND_PCM_FORMAT_S32 + { + int32_t* buffer32b = (int32_t*)fOutputCardBuffer; + for( f = 0; f < fOutputBufferFrames; f++ ) + { + for( c = 0; c < fNumOutputPorts; c++ ) + { + jack_default_audio_sample_t x = + fJackOutputBuffers[c][f]; + buffer32b[c + f * fNumOutputPorts] = + int32_t( + max(min(x, jack_default_audio_sample_t(1.0)), jack_default_audio_sample_t(-1.0)) + * jack_default_audio_sample_t( + INT_MAX ) ); + } + } + } + amount = snd_pcm_format_size( fOutputFormat.format, + fOutputBufferFrames + * fNumOutputPorts ); + count = snd_pcm_write( fOutputDevice, + fOutputCardBuffer, + amount ); + if( count <= 0 ) + { + display_error_msg( count, + "writing interleaved" ); + int err = + snd_pcm_channel_prepare( fOutputDevice, + SND_PCM_CHANNEL_PLAYBACK ); + check_error_msg( err, + "preparing output stream" ); + goto recovery; + } + jack_log( "JackIoAudioAdapter wrote %ld interleaved bytes", + count ); + break; + case 0: + if( fOutputFormat.format == SND_PCM_SFMT_S16 ) + { + short* buffer16b = (short*)fOutputCardBuffer; + for( c = 0; c < fNumOutputPorts; c++ ) + { + for( f = 0; f < fOutputBufferFrames; f++ ) + { + jack_default_audio_sample_t x = + fJackOutputBuffers[c][f]; + buffer16b[f] = + short( + max(min (x, jack_default_audio_sample_t(1.0)), jack_default_audio_sample_t(-1.0)) + * jack_default_audio_sample_t( + SHRT_MAX ) ); + } + } + } + else + { + int32_t* buffer32b = (int32_t*)fOutputCardBuffer; + for( c = 0; c < fNumOutputPorts; c++ ) + { + for( f = 0; f < fOutputBufferFrames; f++ ) + { + jack_default_audio_sample_t x = + fJackOutputBuffers[c][f]; + buffer32b[f + ( c * fNumOutputPorts )] = + int32_t( + max(min(x, jack_default_audio_sample_t(1.0)), jack_default_audio_sample_t(-1.0)) + * jack_default_audio_sample_t( + INT_MAX ) ); + } + } + } + amount = snd_pcm_format_size( fOutputFormat.format, + fOutputBufferFrames + * fNumOutputPorts ); + count = snd_pcm_write( fOutputDevice, + fOutputCardBuffer, + fOutputBufferFrames ); + if( count <= 0 ) + { + display_error_msg( count, + "writing non-interleaved" ); + int err = + snd_pcm_channel_prepare( fOutputDevice, + SND_PCM_CHANNEL_PLAYBACK ); + check_error_msg( err, + "preparing output stream" ); + goto recovery; + } + jack_log( "JackIoAudioAdapter wrote %ld non-interleaved bytes", + count ); + break; + default: + check_error_msg( -10000, + "unknown access mode" ) + ; + break; + } + } + return 0; + } + + /** + * print short information on the audio device + */ + int AudioInterface::shortinfo() + { + snd_ctl_hw_info_t card_info; + snd_ctl_t* ctl_handle; + + int card = snd_card_name( fParams.fInputCardName ); + if( card < 0 ) + return card; + + check_error( snd_ctl_open( &ctl_handle, + card ) ); + check_error( snd_ctl_hw_info( ctl_handle, + &card_info ) ); + jack_info( "%s|%d|%d|%d|%d|%s", + card_info.name, + fNumInputPorts, + fNumOutputPorts, + fParams.fFrequency, + fOutputBufferFrames, + snd_pcm_get_format_name( fInputFormat.format ) ); + snd_ctl_close( ctl_handle ); + + return 0; + } + + /** + * print more detailled information on the audio device + */ + int AudioInterface::longinfo() + { + snd_ctl_hw_info_t card_info; + snd_ctl_t* ctl_handle; + +//display info + jack_info( "Audio Interface Description :" ); + jack_info( + "Sampling Frequency : %d, Sample Format : %s, buffering : %d, nperiod : %d", + fParams.fFrequency, + snd_pcm_get_format_name( fInputFormat.format ), + fOutputBufferFrames, + fParams.fPeriod ); + jack_info( "Software inputs : %2d, Software outputs : %2d", + fNumInputPorts, + fNumOutputPorts ); + jack_info( "Hardware inputs : %2d, Hardware outputs : %2d", + fNumInputPorts, + fNumOutputPorts ); + +//get audio card info and display + int card = snd_card_name( fParams.fInputCardName ); + check_error( snd_ctl_open( &ctl_handle, + card ) ); + check_error( snd_ctl_hw_info( ctl_handle, + &card_info ) ); + printCardInfo( &card_info ); + +//display input/output streams info + if( fNumInputPorts > 0 ) + printHWParams( fInputParams ); + if( fNumOutputPorts > 0 ) + printHWParams( fOutputParams ); + snd_ctl_close( ctl_handle ); + return 0; + } + + void AudioInterface::printCardInfo( + snd_ctl_hw_info_t* ci ) + { + jack_info( "Card info (address : %p)", + ci ); + jack_info( "\tID = %s", + ci->id ); + jack_info( "\tDriver = %s", + ci->abbreviation ); + jack_info( "\tName = %s", + ci->name ); + jack_info( "\tLongName = %s", + ci->longname ); + jack_info( "\tMixerName = %s", + ci ); + jack_info( "\tComponents = %s", + ci ); + jack_info( "--------------" ); + } + + void AudioInterface::printHWParams( + snd_pcm_channel_params_t* params ) + { + jack_info( "HW Params info (address : %p)\n", + params ); + jack_info( "\tChannels = %d", + params->format.voices ); + jack_info( "\tFormat = %s", + snd_pcm_get_format_name( params->format.format ) ); + jack_info( "\tAccess = %s", + "" ); + jack_info( "\tRate = %d", + params->format.rate ); + jack_info( "\tPeriods = %d", + 0 ); + jack_info( "\tPeriod size = %d", + 0 ); + jack_info( "\tPeriod time = %d", + 0 ); + jack_info( "\tBuffer size = %d", + 0 ); + jack_info( "\tBuffer time = %d", + 0 ); + jack_info( "--------------" ); + } + + JackIoAudioAdapter::JackIoAudioAdapter( + jack_nframes_t buffer_size, + jack_nframes_t sample_rate, + const JSList* params ) : + JackAudioAdapterInterface( buffer_size, + sample_rate ), + fThread( this ), + fAudioInterface( + buffer_size, + sample_rate ) + { + const JSList* node; + const jack_driver_param_t* param; + + fCaptureChannels = 2; + fPlaybackChannels = 2; + + fAudioInterface.fParams.fPeriod = 2; + + for( node = params; node; node = jack_slist_next( node ) ) + { + param = (const jack_driver_param_t*)node->data; + + switch( param->character ) + { + case 'i': + jack_info( "fCardInputVoices = %d", + param->value.ui ); + fAudioInterface.fParams.fCardInputVoices = param->value.ui; + break; + case 'o': + jack_info( "fCardOutputVoices = %d", + param->value.ui ); + fAudioInterface.fParams.fCardOutputVoices = param->value.ui; + break; + case 'C': + jack_info( "fInputCardName = %s", + param->value.str ); + fAudioInterface.fParams.fInputCardName = + strdup( param->value.str ); + break; + case 'P': + jack_info( "fOutputCardName = %s", + param->value.str ); + fAudioInterface.fParams.fOutputCardName = + strdup( param->value.str ); + break; + case 'D': + break; + case 'n': + jack_info( "fPeriod = %d", + param->value.ui ); + fAudioInterface.fParams.fPeriod = param->value.ui; + break; +// case 'd': +// fAudioInterface.fParams.fInputCardName = strdup(param->value.str); +// break; + case 'r': + jack_info( "fFrequency = %d", + param->value.ui ); + fAudioInterface.fParams.fFrequency = param->value.ui; + SetAdaptedSampleRate( param->value.ui ); + break; + case 'p': + jack_info( "fBuffering = %d", + param->value.ui ); + fAudioInterface.fParams.fBuffering = param->value.ui; + SetAdaptedBufferSize( param->value.ui ); + break; + case 'q': + jack_info( "fQuality = %d", + param->value.ui ); + fQuality = param->value.ui; + break; + case 'g': + jack_info( "fRingbufferCurSize = %d", + param->value.ui ); + fRingbufferCurSize = param->value.ui; + fAdaptative = false; + break; + } + } + +// fAudioInterface.setInputs(fCaptureChannels); +// fAudioInterface.setOutputs(fPlaybackChannels); + } + + int JackIoAudioAdapter::Open() + { +//open audio interface + if( fAudioInterface.open() ) + return -1; + +//start adapter thread + if( fThread.StartSync() < 0 ) + { + jack_error( "Cannot start audioadapter thread" ); + return -1; + } + +//display card info + fAudioInterface.longinfo(); + +//turn the thread realtime + fThread.AcquireRealTime( GetEngineControl()->fClientPriority ); + return 0; + } + + int JackIoAudioAdapter::Close() + { +#ifdef JACK_MONITOR + fTable.Save(fHostBufferSize, fHostSampleRate, fAdaptedSampleRate, fAdaptedBufferSize); +#endif + switch( fThread.GetStatus() ) + { + +// Kill the thread in Init phase + case JackThread::kStarting: + case JackThread::kIniting: + if( fThread.Kill() < 0 ) + { + jack_error( "Cannot kill thread" ); + return -1; + } + break; + + // Stop when the thread cycle is finished + case JackThread::kRunning: + if( fThread.Stop() < 0 ) + { + jack_error( "Cannot stop thread" ); + return -1; + } + break; + + default: + break; + } + return fAudioInterface.close(); + } + + void JackIoAudioAdapter::Create() + { + int err; + snd_pcm_channel_info_t channel_info; + snd_pcm_t* device; + + jack_info( "Fixed ringbuffer size = %d frames", + fRingbufferCurSize ); + + if( strncmp( fAudioInterface.fParams.fInputCardName, + "none", + 4 ) != 0 ) + { + err = snd_pcm_open_name( &device, + fAudioInterface.fParams.fInputCardName, + SND_PCM_OPEN_CAPTURE ); + + // get channel parameters + memset( &channel_info, + 0, + sizeof( channel_info ) ); + channel_info.channel = SND_PCM_CHANNEL_CAPTURE; + err = snd_pcm_channel_info( device, + &channel_info ); + snd_pcm_close( device ); + + if( 0 == fAudioInterface.fParams.fCardInputVoices ) + { + fCaptureChannels = channel_info.max_voices; + } + else + { + fCaptureChannels = + max( channel_info.min_voices, + (int )fAudioInterface.fParams.fCardInputVoices ); + fCaptureChannels = min( channel_info.max_voices, + fCaptureChannels ); + } + + fAudioInterface.fNumInputPorts = fCaptureChannels; + + //ringbuffers + fCaptureRingBuffer = new JackResampler*[fCaptureChannels]; + for( int i = 0; i < fCaptureChannels; i++ ) + { + fCaptureRingBuffer[i] = new JackResampler(); + fCaptureRingBuffer[i]->Reset( fRingbufferCurSize ); + } + jack_log( "ReadSpace = %ld", + fCaptureRingBuffer[0]->ReadSpace() ); + } + else + { + fCaptureChannels = 0; + fCaptureRingBuffer = NULL; + fAudioInterface.fNumInputPorts = 0; + } + + if( strncmp( fAudioInterface.fParams.fOutputCardName, + "none", + 4 ) != 0 ) + { + int err; + + err = snd_pcm_open_name( &device, + fAudioInterface.fParams.fOutputCardName, + SND_PCM_OPEN_PLAYBACK ); + + // get channel parameters + memset( &channel_info, + 0, + sizeof( channel_info ) ); + channel_info.channel = SND_PCM_CHANNEL_PLAYBACK; + err = snd_pcm_channel_info( device, + &channel_info ); + snd_pcm_close( device ); + + if( 0 == fAudioInterface.fParams.fCardOutputVoices ) + { + fPlaybackChannels = channel_info.max_voices; + } + else + { + fPlaybackChannels = + max( channel_info.min_voices, + (int )fAudioInterface.fParams.fCardOutputVoices ); + fPlaybackChannels = + min( channel_info.max_voices, + fPlaybackChannels ); + } + + fAudioInterface.fNumOutputPorts = fPlaybackChannels; + + //ringbuffers + fPlaybackRingBuffer = new JackResampler*[fPlaybackChannels]; + for( int i = 0; i < fPlaybackChannels; i++ ) + { + fPlaybackRingBuffer[i] = new JackResampler(); + fPlaybackRingBuffer[i]->Reset( fRingbufferCurSize ); + } + jack_log( "ReadSpace = %ld", + fPlaybackRingBuffer[0]->ReadSpace() ); + } + else + { + fPlaybackChannels = 0; + fPlaybackRingBuffer = NULL; + fAudioInterface.fNumOutputPorts = 0; + } + } + + void JackIoAudioAdapter::Destroy() + { + for( int i = 0; i < fCaptureChannels; i++ ) + { + delete ( fCaptureRingBuffer[i] ); + } + for( int i = 0; i < fPlaybackChannels; i++ ) + { + delete ( fPlaybackRingBuffer[i] ); + } + + delete[] fCaptureRingBuffer; + delete[] fPlaybackRingBuffer; + } + + bool JackIoAudioAdapter::Init() + { +//fill the hardware buffers + for( unsigned int i = 0; i < fAudioInterface.fParams.fPeriod; i++ ) + fAudioInterface.write(); + return true; + } + + bool JackIoAudioAdapter::Execute() + { +//read data from audio interface + if( fAudioInterface.read() < 0 ) + return false; + + PushAndPull( fAudioInterface.fJackInputBuffers, + fAudioInterface.fJackOutputBuffers, + fAdaptedBufferSize ); + +//write data to audio interface + if( fAudioInterface.write() < 0 ) + return false; + + return true; + } + + int JackIoAudioAdapter::SetSampleRate( + jack_nframes_t sample_rate ) + { + JackAudioAdapterInterface::SetHostSampleRate( sample_rate ); + Close(); + return Open(); + } + + int JackIoAudioAdapter::SetBufferSize( + jack_nframes_t buffer_size ) + { + JackAudioAdapterInterface::SetHostBufferSize( buffer_size ); + Close(); + return Open(); + } + +} // namespace + +#ifdef __cplusplus +extern "C" +{ +#endif + + SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor() + { + jack_driver_desc_t * desc; + jack_driver_desc_filler_t filler; + jack_driver_param_value_t value; + + desc = + jack_driver_descriptor_construct( "audioadapter", + JackDriverNone, + "netjack audio <==> net backend adapter", + &filler ); + + strcpy( value.str, + "pcmPreferredc" ); + jack_driver_descriptor_add_parameter( desc, + &filler, + "capture", + 'C', + JackDriverParamString, + &value, + NULL, + "Provide capture ports for this io-audio device", + "Opens the given io-audio device for capture and provides ports for its voices.\n" + "Pass \"none\" to disable." ); + + strcpy( value.str, + "pcmPreferredp" ); + jack_driver_descriptor_add_parameter( desc, + &filler, + "playback", + 'P', + JackDriverParamString, + &value, + NULL, + "Provide playback ports for this io-audio device", + "Opens the given io-audio device for playback and provides ports for its voices.\n" + "Pass \"none\" to disable." ); + + value.ui = 48000U; + jack_driver_descriptor_add_parameter( desc, + &filler, + "rate", + 'r', + JackDriverParamUInt, + &value, + NULL, + "Sample rate", + NULL ); + + value.ui = 512U; + jack_driver_descriptor_add_parameter( desc, + &filler, + "periodsize", + 'p', + JackDriverParamUInt, + &value, + NULL, + "Period size", + NULL ); + + value.ui = 2U; + jack_driver_descriptor_add_parameter( desc, + &filler, + "nperiods", + 'n', + JackDriverParamUInt, + &value, + NULL, + "Number of periods of playback latency", + NULL ); + + value.i = true; + jack_driver_descriptor_add_parameter( desc, + &filler, + "duplex", + 'D', + JackDriverParamBool, + &value, + NULL, + "Provide both capture and playback ports", + NULL ); + + value.i = 0; + jack_driver_descriptor_add_parameter( desc, + &filler, + "in-channels", + 'i', + JackDriverParamInt, + &value, + NULL, + "Number of capture voices (defaults to hardware max)", + "Provides up to this many input ports corresponding to voices on the capture device." ); + + jack_driver_descriptor_add_parameter( desc, + &filler, + "out-channels", + 'o', + JackDriverParamInt, + &value, + NULL, + "Number of playback voices (defaults to hardware max)", + "Provides up to this many output ports corresponding to voices on the playback device." ); + + value.ui = 0; + jack_driver_descriptor_add_parameter( desc, + &filler, + "quality", + 'q', + JackDriverParamUInt, + &value, + NULL, + "Resample algorithm quality (0 - 4)", + NULL ); + + value.ui = 32768; + jack_driver_descriptor_add_parameter( desc, + &filler, + "ring-buffer", + 'g', + JackDriverParamUInt, + &value, + NULL, + "Fixed ringbuffer size", + "Fixed ringbuffer size (if not set => automatic adaptative)" ); + + return desc; + } + +#ifdef __cplusplus +} +#endif + diff --git a/qnx/ioaudio/JackIoAudioAdapter.h b/qnx/ioaudio/JackIoAudioAdapter.h new file mode 100644 index 00000000..ff7bbf2f --- /dev/null +++ b/qnx/ioaudio/JackIoAudioAdapter.h @@ -0,0 +1,295 @@ +/* + Copyright (C) 2008 Grame + + 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. + + */ + +#ifndef __JackIoAudioAdapter__ +#define __JackIoAudioAdapter__ + +#include +#include +#include +#include +#include "JackAudioAdapterInterface.h" +#include "JackPlatformPlug.h" +#include "JackError.h" +#include "jack.h" +#include "jslist.h" + +namespace Jack + { + +#define max(x,y) (((x)>(y)) ? (x) : (y)) +#define min(x,y) (((x)<(y)) ? (x) : (y)) +#define NUM_BUFFERS 256 + + /** + * A convenient class to pass parameters to AudioInterface + */ + class AudioParam + { + public: + const char* fInputCardName; + const char* fOutputCardName; + unsigned int fFrequency; + int fBuffering; + + unsigned int fCardInputVoices; + unsigned int fCardOutputVoices; + + unsigned int fPeriod; + + public: + AudioParam( + jack_nframes_t buffer_size = 512, + jack_nframes_t sample_rate = 44100, + const char* input_card = "pcmPreferredc", + int input_ports = 2, + const char* output_card = "pcmPreferredp", + int output_ports = 2, + int periods = 2) : + fInputCardName(input_card), + fOutputCardName(output_card), + fFrequency(sample_rate), + fBuffering(buffer_size), + fCardInputVoices(input_ports), + fCardOutputVoices(output_ports), + fPeriod(periods) + { + } + + AudioParam& inputCardName(const char* n) + { + fInputCardName = n; + return *this; + } + + AudioParam& outputCardName(const char* n) + { + fOutputCardName = n; + return *this; + } + + AudioParam& frequency(int f) + { + fFrequency = f; + return *this; + } + + AudioParam& buffering(int fpb) + { + fBuffering = fpb; + return *this; + } + + void setInputs(int inputs) + { + fCardInputVoices = inputs; + } + + AudioParam& inputs(int n) + { + fCardInputVoices = n; + return *this; + } + + void setOutputs(int outputs) + { + fCardOutputVoices = outputs; + } + + AudioParam& outputs(int n) + { + fCardOutputVoices = n; + return *this; + } + }; + + /** + * An ALSA audio interface + */ + class AudioInterface + { + public: + AudioParam fParams; + + //device info + snd_pcm_t* fOutputDevice; + snd_pcm_t* fInputDevice; + snd_pcm_channel_params_t *fInputParams; + snd_pcm_channel_params_t *fOutputParams; + snd_pcm_channel_setup_t *fInputSetup; + snd_pcm_channel_setup_t *fOutputSetup; + + //samples info + snd_pcm_format_t fInputFormat; + snd_pcm_format_t fOutputFormat; +// uint32_t fSampleAccess; + +//channels +// const char* fCaptureName; +// const char* fPlaybackName; + + unsigned int fNumInputPorts; + unsigned int fNumOutputPorts; + + //Number of frames for one voice/port + jack_nframes_t fInputBufferFrames; + jack_nframes_t fOutputBufferFrames; + +//interleaved mode audiocard buffers + void* fInputCardBuffer; + void* fOutputCardBuffer; + +// //non-interleaved mode audiocard buffers +// void* fCardInputBuffers[NUM_BUFFERS]; +// void* fCardOutputBuffers[NUM_BUFFERS]; +// +// //non-interleaved mod, floating point software buffers +// jack_default_audio_sample_t* fJackInputBuffers[NUM_BUFFERS]; +// jack_default_audio_sample_t* fJackOutputBuffers[NUM_BUFFERS]; + + //non-interleaved mod, floating point software buffers + jack_default_audio_sample_t** fJackInputBuffers; + jack_default_audio_sample_t** fJackOutputBuffers; + + //public methods --------------------------------------------------------- + +// const char* cardName() +// { +// return fInputCardName; +// } +// +// int frequency() +// { +// return fFrequency; +// } +// +// int buffering() +// { +// return fBuffering; +// } + +// jack_default_audio_sample_t** inputSoftChannels() +// { +// return fJackInputBuffers; +// } +// +// jack_default_audio_sample_t** outputSoftChannels() +// { +// return fJackOutputBuffers; +// } + + AudioInterface(const AudioParam& ap = AudioParam()); + + AudioInterface(jack_nframes_t buffer_size, jack_nframes_t sample_rate); + + ~AudioInterface(); + + /** + * Open the audio interface + */ + int open(); + + int close(); + + int setAudioParams(snd_pcm_t* stream, snd_pcm_channel_params_t* params); + + ssize_t interleavedBufferSize(snd_pcm_channel_params_t* params); + + ssize_t noninterleavedBufferSize(snd_pcm_channel_params_t* params); + + /** + * Read audio samples from the audio card. Convert samples to floats and take + * care of interleaved buffers + */ + int read(); + + /** + * write the output soft channels to the audio card. Convert sample + * format and interleaves buffers when needed + */ + int write(); + + /** + * print short information on the audio device + */ + int shortinfo(); + + /** + * print more detailled information on the audio device + */ + int longinfo(); + + void printCardInfo(snd_ctl_hw_info_t* ci); + + void printHWParams(snd_pcm_channel_params_t* params); + + private: + int AudioInterface_common(); + }; + + /*! + \brief Audio adapter using io-audio API. + */ + + class JackIoAudioAdapter: public JackAudioAdapterInterface, + public JackRunnableInterface + { + + private: + JackThread fThread; + AudioInterface fAudioInterface; + + public: + JackIoAudioAdapter( + jack_nframes_t buffer_size, + jack_nframes_t sample_rate, + const JSList* params); + ~JackIoAudioAdapter() + { + } + + virtual int Open(); + virtual int Close(); + + virtual void Create(); + virtual void Destroy(); + + virtual int SetSampleRate(jack_nframes_t sample_rate); + virtual int SetBufferSize(jack_nframes_t buffer_size); + + virtual bool Init(); + virtual bool Execute(); + + }; + + } + +#ifdef __cplusplus +extern "C" + { +#endif +#include "JackCompilerDeps.h" +#include "driver_interface.h" + SERVER_EXPORT jack_driver_desc_t* jack_get_descriptor(); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/tests/qnx/TestIoAudioAdapter.cpp b/tests/qnx/TestIoAudioAdapter.cpp new file mode 100644 index 00000000..ad4b8aea --- /dev/null +++ b/tests/qnx/TestIoAudioAdapter.cpp @@ -0,0 +1,56 @@ +#include +#include "JackIoAudioAdapter.h" + +namespace Jack +{ + class TestIoAudioAdapter: public ::testing::Test + { + public: + TestIoAudioAdapter() + { + + } + + }; + + TEST_F( TestIoAudioAdapter, Open_Success ) + { + + } + + TEST_F( TestIoAudioAdapter, Close_Success ) + { + + } + + TEST_F( TestIoAudioAdapter, Create_Success ) + { + + } + + TEST_F( TestIoAudioAdapter, Destroy_Success ) + { + + } + + TEST_F( TestIoAudioAdapter, SetSampleRate_Success ) + { + + } + + TEST_F( TestIoAudioAdapter, SetBufferSize_Success ) + { + + } + + TEST_F( TestIoAudioAdapter, Init_Success ) + { + + } + + TEST_F( TestIoAudioAdapter, Execute_Success ) + { + + } + +} diff --git a/tests/qnx/TestIoAudioInterface.cpp b/tests/qnx/TestIoAudioInterface.cpp new file mode 100644 index 00000000..bf793eb5 --- /dev/null +++ b/tests/qnx/TestIoAudioInterface.cpp @@ -0,0 +1,44 @@ +#include +#include "JackIoAudioAdapter.h" + +namespace Jack +{ + class TestIoAudioInterface: public ::testing::Test + { + public: + TestIoAudioInterface() + { + + } + }; + + TEST_F( TestIoAudioInterface, Open_Success ) + { + + } + + TEST_F( TestIoAudioInterface, Close_Success ) + { + + } + + TEST_F( TestIoAudioInterface, Read_Success ) + { + + } + + TEST_F( TestIoAudioInterface, Write_Success ) + { + + } + + TEST_F( TestIoAudioInterface, ShortInfo_Success ) + { + + } + + TEST_F( TestIoAudioInterface, LongInfo_Success ) + { + + } +} From 29fd3ad250d312ad8703c43749a6b27000d5cdb4 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Thu, 27 Aug 2015 08:39:18 -0500 Subject: [PATCH 08/10] Add QNX io-audio driver Adds a driver for QNX io-audio system that acts as a JACK client. --- qnx/ioaudio/ioaudio_driver.c | 956 +++++++++++++++++++++++++++++++++++ qnx/wscript | 5 + wscript | 2 + 3 files changed, 963 insertions(+) create mode 100644 qnx/ioaudio/ioaudio_driver.c diff --git a/qnx/ioaudio/ioaudio_driver.c b/qnx/ioaudio/ioaudio_driver.c new file mode 100644 index 00000000..081fdd34 --- /dev/null +++ b/qnx/ioaudio/ioaudio_driver.c @@ -0,0 +1,956 @@ +/* + * ioaudio_driver.c + * + * Copyright 2015 Garmin + */ + +#define HW_CONTEXT_T struct jack_card +#define PCM_SUBCHN_CONTEXT_T struct subchn + +#include + +#include + +#include +#include +#include + +#include + +static const char* PORTNAME_FMT = "playback_%d"; +typedef jack_default_audio_sample_t sample_t; + +struct subchn +{ + ado_pcm_subchn_t *pcm_subchn; + ado_pcm_config_t *pcm_config; + int32_t pcm_offset; /* holds offset of data populated in PCM subchannel buffer */ + uint8_t go; /* indicates if trigger GO has been issue by client for data transfer */ + void *strm; /* pointer back to parent stream structure */ +}; + +typedef struct jack_card +{ + /** + * PCM device instance + */ + ado_pcm_t* pcm; + + /** + * Audio Card Instance + */ + ado_card_t* card; + + /** + * Audio Hardware Mutex + */ + ado_mutex_t hw_lock; + + /** + * Audio Mixer Instance + */ + ado_mixer_t* mixer; + + /** + * PCM Device Capabilities + */ + ado_pcm_cap_t caps; + + /** + * PCM Device Callbacks + */ + ado_pcm_hw_t funcs; + + /** + * PCM Subchannel used by client + */ + struct subchn subchn; + + /** + * Name of JACK server to communicate with + */ + char* server; + + /** + * Name of this client + */ + char* name; + + /** + * Handle to this client + */ + jack_client_t* client; + + /** + * SND_PCM_CHANNEL_CAPTURE or SND_PCM_CHANNEL_PLAYBACK + */ + int channel_type; + + /** + * Number of voices / ports + */ + int voices; + + /** + * Ringbuffer between io-audio and JACK + */ + jack_ringbuffer_t ringbuffer; + + /** + * Number of audio frames per fragment + */ + size_t fragsize; + + /** + * Array of ports between io-audio and JACK + * + * size of array is 'voices' + */ + jack_port_t** ports; + + /** + * Mutex controlling JACK processing + */ + pthread_mutex_t process_lock; +} jack_card_t; + +static int parse_commandline( + jack_card_t* jack_card, + char* theArgs ) +{ + int opt = 0; + char* value; + char* argdup = ado_strdup( theArgs ); + char* args = argdup; + char* opts[] = { "server", "name", "type", "voices", NULL }; + + while( ( NULL != args ) && ( '\0' != args[0] ) ) + { + opt = getsubopt( &args, + opts, + &value ); + + if( opt >= 0 ) + { + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: Parsed option %d (%s)", + opt, + opts[opt] ); + switch( opt ) + { + case 0: /* server: JACK server name */ + if( NULL == value ) + { + ado_error( "server option given without value; ignoring" ); + jack_card->server = NULL; + } + else + { + jack_card->server = ado_strdup( value ); + } + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: argument 'server' parsed as %s", + jack_card->server ); + break; + case 1: /* name: JACK client name */ + if( NULL == value ) + { + ado_error( "name option given without value; ignoring" ); + jack_card->name = ado_strdup( "jack_ioaudio" ); + } + else + { + jack_card->name = ado_strdup( value ); + } + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: argument 'name' parsed as %s", + jack_card->name ); + + break; + case 2: /* type: io-audio channel type */ + if( NULL != value ) + { + if( value[0] == 'P' || value[0] == 'p' ) + { + jack_card->channel_type = SND_PCM_CHANNEL_PLAYBACK; + } + else if( value[0] == 'C' || value[0] == 'c' ) + { + jack_card->channel_type = SND_PCM_CHANNEL_CAPTURE; + } + } + else + { + ado_error( + "type option value not 'c' or 'p'; defaulting to 'p'" ); + jack_card->channel_type = SND_PCM_CHANNEL_PLAYBACK; + } + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: argument 'type' parsed as %d", + jack_card->channel_type ); + break; + case 3: /* voices: number of voices in io-audio channel and JACK ports */ + if( NULL != value ) + { + char* endptr; + int val; + errno = 0; /* To distinguish success/failure after call */ + val = strtol( value, + &endptr, + 0 ); + + /* Check for various possible errors */ + + if( ( errno == ERANGE + && ( val == LONG_MAX || val == LONG_MIN ) ) + || ( errno != 0 && val == 0 ) ) + { + ado_error( "voices option value out of range" ); + return errno; + } + + if( endptr == value ) + { + ado_error( "voices option value contains no digits" ); + return EINVAL; + } + + jack_card->voices = val; + } + else + { + ado_error( "voices option given without value; failing" ); + return EINVAL; + } + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: argument 'voices' with value '%s' parsed as %d", + value ? value : "NULL", + jack_card->voices ); + break; + default: + break; + } + } + else + { + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: found unknown argument %s", + value ); + } + } + + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: finished parsing arguments" ); + + ado_free( argdup ); + return EOK; +} + +int32_t cb_capabilities( + HW_CONTEXT_T * mcasp_card, + ado_pcm_t *pcm, + snd_pcm_channel_info_t * info ) +{ + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: capabilities()" ); + return EOK; +} + +int32_t cb_aquire( + HW_CONTEXT_T *jack_card, + PCM_SUBCHN_CONTEXT_T **pc, + ado_pcm_config_t *config, + ado_pcm_subchn_t *subchn, + uint32_t *why_failed ) +{ + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: aquire()" ); + if( jack_card->subchn.pcm_subchn ) + { + *why_failed = SND_PCM_PARAMS_NO_CHANNEL; + return ( EAGAIN ); + } + ado_debug( DB_LVL_DRIVER, + "config:" ); + ado_debug( DB_LVL_DRIVER, + " format:" ); + ado_debug( DB_LVL_DRIVER, + " interleave: %d", + config->format.interleave ); + ado_debug( DB_LVL_DRIVER, + " format: {id: %d, name: '%s'}", + config->format.format, + snd_pcm_get_format_name( config->format.format ) ); + ado_debug( DB_LVL_DRIVER, + " rate: %d", + config->format.rate ); + ado_debug( DB_LVL_DRIVER, + " voices: %d", + config->format.voices ); + ado_debug( DB_LVL_DRIVER, + " mode.block:" ); + ado_debug( DB_LVL_DRIVER, + " frag_size: %d", + config->mode.block.frag_size ); + ado_debug( DB_LVL_DRIVER, + " frags_min: %d", + config->mode.block.frags_min ); + ado_debug( DB_LVL_DRIVER, + " frags_max: %d", + config->mode.block.frags_max ); + ado_debug( DB_LVL_DRIVER, + " frags_total: %d", + config->mode.block.frags_total ); + ado_debug( DB_LVL_DRIVER, + " dmabuf:" ); + ado_debug( DB_LVL_DRIVER, + " addr: %x", + config->dmabuf.addr ); + ado_debug( DB_LVL_DRIVER, + " phys_addr: %x", + config->dmabuf.phys_addr ); + ado_debug( DB_LVL_DRIVER, + " size: %d", + config->dmabuf.size ); + ado_debug( DB_LVL_DRIVER, + " name: '%s'", + config->dmabuf.name ); + ado_debug( DB_LVL_DRIVER, + " mixer_device: %d", + config->mixer_device ); + ado_debug( DB_LVL_DRIVER, + " mixer_eid:" ); + ado_debug( DB_LVL_DRIVER, + " type: %d", + config->mixer_eid.type ); + ado_debug( DB_LVL_DRIVER, + " name: '%s'", + config->mixer_eid.name ); + ado_debug( DB_LVL_DRIVER, + " index: %d", + config->mixer_eid.index ); + ado_debug( DB_LVL_DRIVER, + " weight: %d", + config->mixer_eid.weight ); + ado_debug( DB_LVL_DRIVER, + " mixer_gid:" ); + ado_debug( DB_LVL_DRIVER, + " type: %d", + config->mixer_gid.type ); + ado_debug( DB_LVL_DRIVER, + " name: '%s'", + config->mixer_gid.name ); + ado_debug( DB_LVL_DRIVER, + " index: %d", + config->mixer_gid.index ); + ado_debug( DB_LVL_DRIVER, + " weight: %d", + config->mixer_gid.weight ); + + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: creating ringbuffer of %u bytes", + config->dmabuf.size ); + + /* + * Create shared memory region for ringbuffer + */ + config->dmabuf.addr = ado_shm_alloc( config->dmabuf.size, + config->dmabuf.name, + ADO_SHM_DMA_SAFE, + &config->dmabuf.phys_addr ); + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: ado_shm_alloc() has finished. " ); + ado_debug( DB_LVL_DRIVER, + " dmabuf:" ); + ado_debug( DB_LVL_DRIVER, + " addr: %x", + config->dmabuf.addr ); + ado_debug( DB_LVL_DRIVER, + " phys_addr: %x", + config->dmabuf.phys_addr ); + ado_debug( DB_LVL_DRIVER, + " size: %d", + config->dmabuf.size ); + ado_debug( DB_LVL_DRIVER, + " name: '%s'", + config->dmabuf.name ); + + /* + * Set up JACK ringbuffer structure to use SHM region instead of own + * buffer. + */ + jack_card->ringbuffer.buf = (char*)config->dmabuf.addr; + jack_card->ringbuffer.size = config->dmabuf.size; + jack_card->ringbuffer.size_mask = config->dmabuf.size - 1; + jack_ringbuffer_reset( &jack_card->ringbuffer ); + + /* + * Store parameters for future use + */ + jack_card->subchn.pcm_config = config; + jack_card->subchn.pcm_subchn = subchn; + + /* + * Set output parameters + */ + *pc = &jack_card->subchn; + + return EOK; +} + +int32_t cb_release( + HW_CONTEXT_T *jack_card, + PCM_SUBCHN_CONTEXT_T *pc, + ado_pcm_config_t *config ) +{ + pthread_mutex_lock( &jack_card->process_lock ); + + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: release()" ); + jack_card->subchn.go = 0; + + jack_ringbuffer_reset( &jack_card->ringbuffer ); + + ado_shm_free( pc->pcm_config->dmabuf.addr, + pc->pcm_config->dmabuf.size, + pc->pcm_config->dmabuf.name ); + pc->pcm_subchn = NULL; + + pthread_mutex_unlock( &jack_card->process_lock ); + return EOK; +} + +int32_t cb_prepare( + HW_CONTEXT_T *jack_card, + PCM_SUBCHN_CONTEXT_T *pc, + ado_pcm_config_t *config ) +{ + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: prepare()" ); + jack_ringbuffer_reset( &jack_card->ringbuffer ); + return EOK; +} + +int32_t cb_trigger( + HW_CONTEXT_T *jack_card, + PCM_SUBCHN_CONTEXT_T *pc, + uint32_t cmd ) +{ + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: trigger( %d )", + cmd ); + + if( ADO_PCM_TRIGGER_STOP == cmd ) + { + jack_card->subchn.go = 0; + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: ADO_PCM_TRIGGER_STOP" ); + } + else if( ADO_PCM_TRIGGER_GO == cmd ) + { + /* + * Signal readiness to JACK and ADO + */ + jack_card->subchn.go = 1; + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: ADO_PCM_TRIGGER_GO" ); + } + else + { + jack_card->subchn.go = 0; + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: ADO_PCM_TRIGGER_SYNC_AND_GO" ); + } + return EOK; +} + +uint32_t cb_position( + HW_CONTEXT_T *jack_card, + PCM_SUBCHN_CONTEXT_T *pc, + ado_pcm_config_t *config ) +{ + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: position()" ); + return EOK; +} + +void irq_handler( + HW_CONTEXT_T *jack_card, + int32_t event ) +{ + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: irq_handler()" ); +} + +/** + * Prototype for the client supplied function that is called + * by the engine anytime there is work to be done. + * + * @pre nframes == jack_get_buffer_size() + * @pre nframes == pow(2,x) + * + * @param nframes number of frames to process + * @param arg pointer to a client supplied structure + * + * @return zero on success, non-zero on error + */ +int jack_process( + jack_nframes_t nframes, + void *arg ) +{ + int v; + jack_card_t* jack_card = (jack_card_t*)arg; + + size_t total_size = nframes * sizeof(sample_t); + size_t size_completed; + + if( ( jack_card->fragsize != total_size ) ) + { + ado_error( + "deva_ctrl-jack: mismatch of fragment size with number of frames requested" ); + } + + /* + * Try to lock the process mutex. If the lock can't be acquired, assume something is happening with the subchannel + * and write zeroes to JACK instead. + */ + if( jack_card->subchn.go + && ( 0 == pthread_mutex_trylock( &jack_card->process_lock ) ) ) + { + ado_debug( DB_LVL_PCM, + "deva-ctrl-jack: jack_process( %d )", + nframes ); + ado_debug( DB_LVL_PCM, + " total_size: %d", + total_size ); + + int voices = jack_card->subchn.pcm_config->format.voices; + ado_debug( DB_LVL_PCM, + " voices: %d", + voices ); + + size_t size_per_voice = total_size / voices; + ado_debug( DB_LVL_PCM, + " size_per_voice: %d", + size_per_voice ); + + void* jack_buf[voices]; + for( v = 0; v < voices; ++v ) + { + jack_buf[v] = jack_port_get_buffer( jack_card->ports[v], + nframes ); + } + + for( size_completed = 0; size_completed < total_size; size_completed += + size_per_voice ) + { + ado_debug( DB_LVL_PCM, + " size_completed: %d", + size_completed ); + for( v = 0; v < voices; ++v ) + { + ado_debug( DB_LVL_PCM, + " voice: %d", + v ); + /* + * Advance ringbuffer write pointer on the assumption that io-audio has filled in nframes of data + */ + jack_ringbuffer_write_advance( &jack_card->ringbuffer, + size_per_voice ); + + jack_ringbuffer_data_t read_buf[2]; + jack_ringbuffer_get_read_vector( &jack_card->ringbuffer, + read_buf ); + + if( SND_PCM_SFMT_FLOAT_LE + == jack_card->subchn.pcm_config->format.format ) + { + ado_debug( DB_LVL_PCM, + " ringbuffer:" ); + ado_debug( DB_LVL_PCM, + " - { buf: %x, len: %d }", + read_buf[0].buf, + read_buf[0].len ); + ado_debug( DB_LVL_PCM, + " - { buf: %x, len: %d }", + read_buf[1].buf, + read_buf[1].len ); + jack_ringbuffer_read( &jack_card->ringbuffer, + jack_buf[v], + size_per_voice ); + } + else if( SND_PCM_SFMT_S32_LE + == jack_card->subchn.pcm_config->format.format ) + { + int s; + size_t remaining = size_per_voice / sizeof(sample_t); + read_buf[0].len /= sizeof(sample_t); + read_buf[1].len /= sizeof(sample_t); + ado_debug( DB_LVL_PCM, + " samples_remaining: %d", + remaining ); + ado_debug( DB_LVL_PCM, + " ringbuffer:" ); + ado_debug( DB_LVL_PCM, + " - { buf: %x, len: %d }", + read_buf[0].buf, + read_buf[0].len ); + ado_debug( DB_LVL_PCM, + " - { buf: %x, len: %d }", + read_buf[1].buf, + read_buf[1].len ); + + int32_t* src = (sample_t*)read_buf[0].buf; + sample_t* dest = (sample_t*)jack_buf[v]; + size_t amt = min( read_buf[0].len, + remaining ); + for( s = 0; s < amt; s += sizeof(sample_t) ) + { + dest[s] = ( (sample_t)src[s] ) * ( (sample_t)1.0 ) + / ( (sample_t)INT_MAX ); +// ado_debug(DB_LVL_PCM, " %x : %d : %f", &src[s], src[s], dest[s]); + } + remaining -= amt; + ado_debug( DB_LVL_PCM, + " samples_remaining: %d", + remaining ); + ado_debug( DB_LVL_PCM, + " ringbuffer:" ); + ado_debug( DB_LVL_PCM, + " - { buf: %x, len: %d }", + read_buf[0].buf, + read_buf[0].len ); + ado_debug( DB_LVL_PCM, + " - { buf: %x, len: %d }", + read_buf[1].buf, + read_buf[1].len ); + if( remaining > 0 ) + { + src = (int32_t*)read_buf[1].buf; + dest += amt; + amt = min( read_buf[1].len, + remaining ); + for( s = 0; s < amt; s += sizeof(sample_t) ) + { + dest[s] = ( (sample_t)src[s] ) * ( (sample_t)1.0 ) + / ( (sample_t)INT_MAX ); + } + } + jack_ringbuffer_read_advance( &jack_card->ringbuffer, + size_per_voice ); + } + + jack_buf[v] += size_per_voice; + + } + dma_interrupt( jack_card->subchn.pcm_subchn ); + } + ado_debug( DB_LVL_PCM, + " size_completed: %d", + size_completed ); + pthread_mutex_unlock( &jack_card->process_lock ); + } + else + { + for( v = 0; v < jack_card->voices; ++v ) + { + void* jack_buf = jack_port_get_buffer( jack_card->ports[v], + nframes ); + memset( jack_buf, + 0, + total_size ); + } + } + + return 0; +} + +/** + * Prototype for the client supplied function that is called + * whenever jackd is shutdown. Note that after server shutdown, + * the client pointer is *not* deallocated by libjack, + * the application is responsible to properly use jack_client_close() + * to release client ressources. Warning: jack_client_close() cannot be + * safely used inside the shutdown callback and has to be called outside of + * the callback context. + * + * @param arg pointer to a client supplied structure + */ +void jack_shutdown( + void *arg ) +{ + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: jack_shutdown" ); + jack_card_t* card = (jack_card_t*)arg; + + /* + * Find a way to trigger io-audio to call ctrl_destroy() to clean up + */ +} + +ado_ctrl_dll_init_t ctrl_init; +int ctrl_init( + HW_CONTEXT_T ** hw_context, + ado_card_t * card, + char *args ) +{ + int retval; + int i; + jack_card_t* jack_card; + + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: ctrl_init" ); + + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: allocating card" ); + jack_card = ado_calloc( 1, + sizeof(jack_card_t) ); + if( NULL == jack_card ) + { + ado_error( "Unable to allocate memory for jack_card (%s)", + strerror( errno ) ); + return -1; + } + + *hw_context = jack_card; + jack_card->card = card; + + pthread_mutex_init( &jack_card->process_lock, + NULL ); + + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: setting card name" ); + ado_card_set_shortname( card, + "jack_card" ); + ado_card_set_longname( card, + "jack_card", + 0x1000 ); + + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: parsing arguments '%s'", + args ); + retval = parse_commandline( jack_card, + args ); + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: returned from parsing arguments" ); + if( EOK != retval ) + { + ado_error( + "Received error from parse_commandline(): %d; cannot continue", + retval ); + return -1; + } + + /* + * Create client per command line arguments + */ + int client_name_size = jack_client_name_size(); + if( strlen( jack_card->name ) > client_name_size ) + { + ado_error( + "Client name %s is too long (%d chars).\nPlease use a client name of %d characters or less.", + strlen( jack_card->name ), + client_name_size ); + return -1; + } + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: Opening jack client with name '%s' on server '%s'", + jack_card->name, + jack_card->server ? jack_card->server : "DEFAULT" ); + jack_status_t open_status; + if( NULL != jack_card->server ) + { + jack_card->client = jack_client_open( jack_card->name, + JackServerName, + &open_status, + jack_card->server ); + } + else + { + jack_card->client = jack_client_open( jack_card->name, + JackNullOption, + &open_status ); + } + if( NULL == jack_card->client ) + { + ado_error( "Error opening JACK client (%x)", + open_status ); + ado_free( jack_card ); + return -1; + } + + /* + * Set JACK and ADO processing callbacks + */ + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: registering callbacks" ); + jack_set_process_callback( jack_card->client, + jack_process, + jack_card ); + jack_on_shutdown( jack_card->client, + jack_shutdown, + 0 ); + + /* + * Allocate jack_port_t array + */ + int voices = jack_card->voices; + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: allocating array of %d ports", + voices ); + jack_card->ports = ado_calloc( voices, + sizeof(jack_port_t*) ); + + /* + * Register JACK ports and create their ringbuffers + */ + int port_name_size = jack_port_name_size(); + char* portname = calloc( jack_port_name_size(), + sizeof(char) ); + for( i = 0; i < voices; ++i ) + { + snprintf( portname, + port_name_size, + PORTNAME_FMT, + ( i + 1 ) ); + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: registering port %d", + ( i + 1 ) ); + jack_card->ports[i] = jack_port_register( jack_card->client, + portname, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, + 0 ); + } + free( portname ); + + /* + * Initialize Capabilities + */ + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: initializing driver capabilities" ); + jack_card->caps.chn_flags = SND_PCM_CHNINFO_BLOCK + | SND_PCM_CHNINFO_NONINTERLEAVE; + jack_card->caps.formats = 0xFFFF; + jack_card->caps.formats = SND_PCM_FMT_FLOAT_LE | SND_PCM_FMT_S32_LE; +// jack_card->caps.formats = SND_PCM_FMT_FLOAT_LE; + + jack_nframes_t rate = jack_get_sample_rate( jack_card->client ); + uint32_t rateflag = ado_pcm_rate2flag( rate ); + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: sample rate of JACK: %u; flag: 0x%x", + rate, + rateflag ); + jack_card->caps.rates = rateflag; + + jack_card->caps.min_voices = 1; + jack_card->caps.max_voices = jack_card->voices; + + /* + * Get size of ringbuffer from JACK server + */ + jack_card->fragsize = jack_get_buffer_size( jack_card->client ) + * sizeof(sample_t); + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: fragment is %u bytes", + jack_card->fragsize ); + jack_card->caps.min_fragsize = jack_card->fragsize; + jack_card->caps.max_fragsize = jack_card->fragsize; + jack_card->caps.max_dma_size = 0; + jack_card->caps.max_frags = 0; + + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: initializing driver callbacks" ); + jack_card->funcs.capabilities2 = cb_capabilities; + jack_card->funcs.aquire = cb_aquire; + jack_card->funcs.release = cb_release; + jack_card->funcs.prepare = cb_prepare; + jack_card->funcs.trigger = cb_trigger; + jack_card->funcs.position = cb_position; + + /* + * Create Audio PCM Device + */ + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: creating PCM device" ); + retval = ado_pcm_create( card, + "JACK io-audio driver", + ( jack_card->channel_type + == SND_PCM_CHANNEL_PLAYBACK ) ? + SND_PCM_INFO_PLAYBACK : SND_PCM_INFO_CAPTURE, + "jack-audio", + ( jack_card->channel_type + == SND_PCM_CHANNEL_PLAYBACK ) ? 1 : 0, + ( jack_card->channel_type + == SND_PCM_CHANNEL_PLAYBACK ) ? + &jack_card->caps : NULL, + ( jack_card->channel_type + == SND_PCM_CHANNEL_PLAYBACK ) ? + &jack_card->funcs : NULL, + ( jack_card->channel_type + == SND_PCM_CHANNEL_CAPTURE ) ? 1 : 0, + ( jack_card->channel_type + == SND_PCM_CHANNEL_CAPTURE ) ? + &jack_card->caps : NULL, + ( jack_card->channel_type + == SND_PCM_CHANNEL_CAPTURE ) ? + &jack_card->funcs : NULL, + &jack_card->pcm ); + + if( -1 == retval ) + { + ado_error( "deva_ctrl_jack: ERROR: %s", + strerror( errno ) ); + ado_free( jack_card->ports ); + ado_free( jack_card ); + return -1; + } + + /* + * Activate JACK client + */ + return jack_activate( jack_card->client ); +} + +ado_ctrl_dll_destroy_t ctrl_destroy; +int ctrl_destroy( + HW_CONTEXT_T * context ) +{ + jack_card_t* jack_card = (jack_card_t*)context; + + ado_debug( DB_LVL_DRIVER, + "deva-ctrl-jack: ctrl_destroy()" ); + + jack_deactivate( jack_card->client ); + jack_client_close( jack_card->client ); + + ado_free( jack_card->ports ); + ado_free( jack_card->server ); + ado_free( jack_card->name ); + + pthread_mutex_destroy( &jack_card->process_lock ); + ado_free( jack_card ); + + return EOK; +} + +ado_dll_version_t ctrl_version; +void ctrl_version( + int *major, + int *minor, + char *date ) +{ + *major = ADO_MAJOR_VERSION; + *minor = 1; + date = __DATE__; +} + +int ctrl_devctl( + uint32_t cmd, + uint8_t *msg, + uint16_t *msg_size, + HW_CONTEXT_T *context ) +{ + +} + diff --git a/qnx/wscript b/qnx/wscript index 31148878..00f23e50 100644 --- a/qnx/wscript +++ b/qnx/wscript @@ -36,3 +36,8 @@ def build(bld): create_jack_driver_obj(bld, 'dummy', '../common/JackDummyDriver.cpp') create_jack_driver_obj(bld, 'loopback', '../common/JackLoopbackDriver.cpp') + + if bld.env['BUILD_DRIVER_IOAUDIO']: + deva = create_jack_driver_obj(bld, 'jack', 'ioaudio/ioaudio_driver.c', ['IOAUDIO', 'clientlib', 'PTHREAD']) + deva.env['cxxshlib_PATTERN'] = 'deva-ctrl-%s.so' + deva.install_path = '/lib/dll' diff --git a/wscript b/wscript index a3fe2ae3..476c0c65 100644 --- a/wscript +++ b/wscript @@ -438,6 +438,8 @@ def options(opt): readline.add_library('readline') readline.set_check_hook(check_for_readline, check_for_readline_error) + ioaudio = add_auto_option(opt, 'ioaudio', help='Enable io-audio driver', conf_dest='BUILD_DRIVER_IOAUDIO') + # dbus options opt.recurse('dbus') From 1af2252bfff1f7e5e8ebd604b7a1f6eeb1f94acf Mon Sep 17 00:00:00 2001 From: Matt Fischer Date: Thu, 3 Sep 2015 15:50:43 -0500 Subject: [PATCH 09/10] More QNX build fixes This patch adds a missing case to the tests wscript, to handle the QNX platform. It also adds code to the setup for the various directory macros, to handle the case when the build is taking place on Windows, to ensure that the directory paths still come across in POSIX format. Change-Id: If8054284d390e40c97c8c585af2851e94275aad8 --- tests/wscript | 2 ++ wscript | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/wscript b/tests/wscript index 70d48f0f..da7e7b7e 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 index 476c0c65..0479bdcd 100644 --- a/wscript +++ b/wscript @@ -525,6 +525,15 @@ def configure(conf): else: conf.env['BUILD_JACKD'] = True + path = os.path + if sys.platform == 'win32' and (conf.env['IS_QNX'] or conf.env['IS_LINUX']): + # If we are cross-compiling from Windows to a system with POSIX style filenames, + # we need to construct file paths using posixpath instead of os.path. Also, we + # need fix PREFIX, since Waf constructed it incorrectly + import posixpath + path = posixpath + conf.env['PREFIX'] = os.path.splitdrive(conf.env['PREFIX'])[1].replace('\\', '/') + conf.env['BINDIR'] = conf.env['PREFIX'] + '/bin' if Options.options.htmldir: @@ -581,9 +590,9 @@ def configure(conf): # don't define ADDON_DIR in config.h, use the default 'jack' defined in # windows/JackPlatformPlug_os.h else: - conf.env['ADDON_DIR'] = os.path.normpath(os.path.join(conf.env['LIBDIR'], 'jack')) + conf.env['ADDON_DIR'] = path.normpath(path.join(conf.env['LIBDIR'], 'jack')) conf.define('ADDON_DIR', conf.env['ADDON_DIR']) - conf.define('JACK_LOCATION', os.path.normpath(os.path.join(conf.env['PREFIX'], 'bin'))) + conf.define('JACK_LOCATION', path.normpath(path.join(conf.env['PREFIX'], 'bin'))) if not conf.env['IS_WINDOWS']: conf.define('USE_POSIX_SHM', 1) From 33000e7bc6e1e62629db278ea67ce48ed5cff6f8 Mon Sep 17 00:00:00 2001 From: Andrew Cooper Date: Fri, 4 Sep 2015 11:43:36 -0500 Subject: [PATCH 10/10] Address review comments from shaun-tierney Removed a number of commented sections Made a few sample types explicitly sized Removed debugging statements --- qnx/cycles.h | 1 - qnx/ioaudio/JackIoAudioAdapter.cpp | 80 ++++------- qnx/ioaudio/JackIoAudioAdapter.h | 44 +----- qnx/ioaudio/ioaudio_driver.c | 223 ++--------------------------- tests/qnx/TestIoAudioAdapter.cpp | 56 -------- tests/qnx/TestIoAudioInterface.cpp | 44 ------ tests/test.cpp | 2 +- 7 files changed, 43 insertions(+), 407 deletions(-) delete mode 100644 tests/qnx/TestIoAudioAdapter.cpp delete mode 100644 tests/qnx/TestIoAudioInterface.cpp diff --git a/qnx/cycles.h b/qnx/cycles.h index d0d9df2e..ae721166 100644 --- a/qnx/cycles.h +++ b/qnx/cycles.h @@ -23,7 +23,6 @@ #define __jack_cycles_h__ #include -//#include typedef uint64_t cycles_t; #define get_cycles() ClockCycles() diff --git a/qnx/ioaudio/JackIoAudioAdapter.cpp b/qnx/ioaudio/JackIoAudioAdapter.cpp index b285e335..b71a417e 100644 --- a/qnx/ioaudio/JackIoAudioAdapter.cpp +++ b/qnx/ioaudio/JackIoAudioAdapter.cpp @@ -166,7 +166,7 @@ namespace Jack //get hardware input parameters fInputParams->mode = SND_PCM_MODE_BLOCK; fInputParams->channel = SND_PCM_CHANNEL_CAPTURE; - //fInputParams->sync = Not Supported + //fInputParams->sync = Not Supported by io-audio // Check supported formats, preferring in order: float, sint32, sint16, plugin converted if( inputInfo.formats & SND_PCM_FMT_FLOAT ) @@ -192,11 +192,11 @@ namespace Jack fInputParams->format.interleave = 1; fInputParams->format.rate = fParams.fFrequency; fInputParams->format.voices = fNumInputPorts; - //fInputParams->digital = Not Implemented + //fInputParams->digital = Not currently implemented in io-audio fInputParams->start_mode = SND_PCM_START_DATA; fInputParams->stop_mode = SND_PCM_STOP_STOP; - //fInputParams->time = 1; - //fInputParams->ust_time + //fInputParams->time = 1; // If set, the driver offers the time when the transfer began (gettimeofday() format) + //fInputParams->ust_time = 1; // If set, the driver offers the time when the transfer began (UST format) fInputParams->buf.block.frag_size = snd_pcm_format_size( fInputParams->format.format, fParams.fBuffering * fNumInputPorts ); @@ -219,9 +219,6 @@ namespace Jack return err; } - //set nonblocking mode - //check_error(snd_pcm_nonblock_mode(fInputDevice, 1)); - //get params record with actual values fInputSetup->channel = SND_PCM_CHANNEL_CAPTURE; check_error( snd_pcm_channel_setup( fInputDevice, @@ -289,7 +286,7 @@ namespace Jack check_error( snd_pcm_malloc_struct( &fOutputParams ) ); fOutputParams->channel = SND_PCM_CHANNEL_PLAYBACK; fOutputParams->mode = SND_PCM_MODE_BLOCK; - //fOutputParams->sync = Not Supported + //fOutputParams->sync = Not Supported by io-audio // Check supported formats, preferring in order: float, sint32, sint16, plugin converted if( outputInfo.formats & SND_PCM_FMT_FLOAT ) @@ -315,11 +312,11 @@ namespace Jack fOutputParams->format.interleave = 1; fOutputParams->format.rate = fParams.fFrequency; fOutputParams->format.voices = fNumOutputPorts; - //fOutputParams->digital = Not Implemented + //fOutputParams->digital = Not currently implemented by io-audio fOutputParams->start_mode = SND_PCM_START_DATA; fOutputParams->stop_mode = SND_PCM_STOP_STOP; - fOutputParams->time = 1; - //fOutputParams->ust_time + //fOutputParams->time = 1; // If set, the driver offers the time when the transfer began (gettimeofday() format) + //fOutputParams->ust_time = 1; // If set, the driver offers the time when the transfer began (UST Format) fOutputParams->buf.block.frag_size = snd_pcm_format_size( fOutputParams->format.format, fParams.fBuffering * fNumOutputPorts ); @@ -341,9 +338,6 @@ namespace Jack return err; } -// //set nonblocking mode -// check_error(snd_pcm_nonblock_mode(fOutputDevice, 1)); - //get params record with actual values check_error( snd_pcm_channel_setup( fOutputDevice, fOutputSetup ) ); @@ -392,14 +386,6 @@ namespace Jack if( fJackOutputBuffers[i] ) free( fJackOutputBuffers[i] ); -// for (unsigned int i = 0; i < fNumInputPorts; i++) -// if (fInputCardBuffer[i]) -// free(fInputCardBuffer[i]); -// -// for (unsigned int i = 0; i < fNumOutputPorts; i++) -// if (fOutputCardBuffer[i]) -// free(fOutputCardBuffer[i]); - if( fInputCardBuffer ) free( fInputCardBuffer ); if( fOutputCardBuffer ) @@ -450,7 +436,7 @@ namespace Jack count ); if( fInputFormat.format == SND_PCM_SFMT_S16 ) { - short* buffer16b = (short*)fInputCardBuffer; + int16_t* buffer16b = (int16_t*)fInputCardBuffer; for( s = 0; s < fInputBufferFrames; s++ ) for( c = 0; c < fNumInputPorts; c++ ) fJackInputBuffers[c][s] = @@ -493,10 +479,10 @@ namespace Jack count ); if( fInputFormat.format == SND_PCM_SFMT_S16 ) { - short* buffer16b; + int16_t* buffer16b; for( c = 0; c < fNumInputPorts; c++ ) { - buffer16b = (short*)fInputCardBuffer; + buffer16b = (int16_t*)fInputCardBuffer; for( s = 0; s < fInputBufferFrames; s++ ) fJackInputBuffers[c][s] = jack_default_audio_sample_t( buffer16b[s] ) @@ -547,7 +533,7 @@ namespace Jack case 1: if( fOutputFormat.format == SND_PCM_SFMT_S16 ) { - short* buffer16b = (short*)fOutputCardBuffer; + int16_t* buffer16b = (int16_t*)fOutputCardBuffer; for( f = 0; f < fOutputBufferFrames; f++ ) { for( c = 0; c < fNumOutputPorts; c++ ) @@ -602,7 +588,7 @@ namespace Jack case 0: if( fOutputFormat.format == SND_PCM_SFMT_S16 ) { - short* buffer16b = (short*)fOutputCardBuffer; + int16_t* buffer16b = (int16_t*)fOutputCardBuffer; for( c = 0; c < fNumOutputPorts; c++ ) { for( f = 0; f < fOutputBufferFrames; f++ ) @@ -617,7 +603,7 @@ namespace Jack } } } - else + else // SND_PCM_FORMAT_S32 { int32_t* buffer32b = (int32_t*)fOutputCardBuffer; for( c = 0; c < fNumOutputPorts; c++ ) @@ -700,7 +686,7 @@ namespace Jack snd_ctl_hw_info_t card_info; snd_ctl_t* ctl_handle; -//display info + //display info jack_info( "Audio Interface Description :" ); jack_info( "Sampling Frequency : %d, Sample Format : %s, buffering : %d, nperiod : %d", @@ -715,7 +701,7 @@ namespace Jack fNumInputPorts, fNumOutputPorts ); -//get audio card info and display + //get audio card info and display int card = snd_card_name( fParams.fInputCardName ); check_error( snd_ctl_open( &ctl_handle, card ) ); @@ -723,7 +709,7 @@ namespace Jack &card_info ) ); printCardInfo( &card_info ); -//display input/output streams info + //display input/output streams info if( fNumInputPorts > 0 ) printHWParams( fInputParams ); if( fNumOutputPorts > 0 ) @@ -832,9 +818,6 @@ namespace Jack param->value.ui ); fAudioInterface.fParams.fPeriod = param->value.ui; break; -// case 'd': -// fAudioInterface.fParams.fInputCardName = strdup(param->value.str); -// break; case 'r': jack_info( "fFrequency = %d", param->value.ui ); @@ -860,28 +843,25 @@ namespace Jack break; } } - -// fAudioInterface.setInputs(fCaptureChannels); -// fAudioInterface.setOutputs(fPlaybackChannels); } int JackIoAudioAdapter::Open() { -//open audio interface + //open audio interface if( fAudioInterface.open() ) return -1; -//start adapter thread + //start adapter thread if( fThread.StartSync() < 0 ) { jack_error( "Cannot start audioadapter thread" ); return -1; } -//display card info + //display card info fAudioInterface.longinfo(); -//turn the thread realtime + //turn the thread realtime fThread.AcquireRealTime( GetEngineControl()->fClientPriority ); return 0; } @@ -894,9 +874,9 @@ namespace Jack switch( fThread.GetStatus() ) { -// Kill the thread in Init phase + // Kill the thread in Init phase case JackThread::kStarting: - case JackThread::kIniting: + case JackThread::kIniting: if( fThread.Kill() < 0 ) { jack_error( "Cannot kill thread" ); @@ -904,7 +884,7 @@ namespace Jack } break; - // Stop when the thread cycle is finished + // Stop when the thread cycle is finished case JackThread::kRunning: if( fThread.Stop() < 0 ) { @@ -945,6 +925,7 @@ namespace Jack &channel_info ); snd_pcm_close( device ); + // Determine number of capture channels from card or parameter if( 0 == fAudioInterface.fParams.fCardInputVoices ) { fCaptureChannels = channel_info.max_voices; @@ -960,7 +941,7 @@ namespace Jack fAudioInterface.fNumInputPorts = fCaptureChannels; - //ringbuffers + // Create capture channel ringbuffers fCaptureRingBuffer = new JackResampler*[fCaptureChannels]; for( int i = 0; i < fCaptureChannels; i++ ) { @@ -996,6 +977,7 @@ namespace Jack &channel_info ); snd_pcm_close( device ); + // Determine number of playback channels from card or parameter if( 0 == fAudioInterface.fParams.fCardOutputVoices ) { fPlaybackChannels = channel_info.max_voices; @@ -1012,7 +994,7 @@ namespace Jack fAudioInterface.fNumOutputPorts = fPlaybackChannels; - //ringbuffers + // Create playback channel ringbuffers fPlaybackRingBuffer = new JackResampler*[fPlaybackChannels]; for( int i = 0; i < fPlaybackChannels; i++ ) { @@ -1047,7 +1029,7 @@ namespace Jack bool JackIoAudioAdapter::Init() { -//fill the hardware buffers + //fill the hardware buffers for( unsigned int i = 0; i < fAudioInterface.fParams.fPeriod; i++ ) fAudioInterface.write(); return true; @@ -1055,7 +1037,7 @@ namespace Jack bool JackIoAudioAdapter::Execute() { -//read data from audio interface + //read data from audio interface if( fAudioInterface.read() < 0 ) return false; @@ -1063,7 +1045,7 @@ namespace Jack fAudioInterface.fJackOutputBuffers, fAdaptedBufferSize ); -//write data to audio interface + //write data to audio interface if( fAudioInterface.write() < 0 ) return false; diff --git a/qnx/ioaudio/JackIoAudioAdapter.h b/qnx/ioaudio/JackIoAudioAdapter.h index ff7bbf2f..3e9b9081 100644 --- a/qnx/ioaudio/JackIoAudioAdapter.h +++ b/qnx/ioaudio/JackIoAudioAdapter.h @@ -120,7 +120,7 @@ namespace Jack }; /** - * An ALSA audio interface + * An io-audio client interface */ class AudioInterface { @@ -138,11 +138,6 @@ namespace Jack //samples info snd_pcm_format_t fInputFormat; snd_pcm_format_t fOutputFormat; -// uint32_t fSampleAccess; - -//channels -// const char* fCaptureName; -// const char* fPlaybackName; unsigned int fNumInputPorts; unsigned int fNumOutputPorts; @@ -151,49 +146,16 @@ namespace Jack jack_nframes_t fInputBufferFrames; jack_nframes_t fOutputBufferFrames; -//interleaved mode audiocard buffers + // audiocard buffers void* fInputCardBuffer; void* fOutputCardBuffer; -// //non-interleaved mode audiocard buffers -// void* fCardInputBuffers[NUM_BUFFERS]; -// void* fCardOutputBuffers[NUM_BUFFERS]; -// -// //non-interleaved mod, floating point software buffers -// jack_default_audio_sample_t* fJackInputBuffers[NUM_BUFFERS]; -// jack_default_audio_sample_t* fJackOutputBuffers[NUM_BUFFERS]; - - //non-interleaved mod, floating point software buffers + // floating point JACK buffers jack_default_audio_sample_t** fJackInputBuffers; jack_default_audio_sample_t** fJackOutputBuffers; //public methods --------------------------------------------------------- -// const char* cardName() -// { -// return fInputCardName; -// } -// -// int frequency() -// { -// return fFrequency; -// } -// -// int buffering() -// { -// return fBuffering; -// } - -// jack_default_audio_sample_t** inputSoftChannels() -// { -// return fJackInputBuffers; -// } -// -// jack_default_audio_sample_t** outputSoftChannels() -// { -// return fJackOutputBuffers; -// } - AudioInterface(const AudioParam& ap = AudioParam()); AudioInterface(jack_nframes_t buffer_size, jack_nframes_t sample_rate); diff --git a/qnx/ioaudio/ioaudio_driver.c b/qnx/ioaudio/ioaudio_driver.c index 081fdd34..44c2354a 100644 --- a/qnx/ioaudio/ioaudio_driver.c +++ b/qnx/ioaudio/ioaudio_driver.c @@ -132,10 +132,6 @@ static int parse_commandline( if( opt >= 0 ) { - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: Parsed option %d (%s)", - opt, - opts[opt] ); switch( opt ) { case 0: /* server: JACK server name */ @@ -148,9 +144,6 @@ static int parse_commandline( { jack_card->server = ado_strdup( value ); } - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: argument 'server' parsed as %s", - jack_card->server ); break; case 1: /* name: JACK client name */ if( NULL == value ) @@ -162,10 +155,6 @@ static int parse_commandline( { jack_card->name = ado_strdup( value ); } - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: argument 'name' parsed as %s", - jack_card->name ); - break; case 2: /* type: io-audio channel type */ if( NULL != value ) @@ -185,9 +174,6 @@ static int parse_commandline( "type option value not 'c' or 'p'; defaulting to 'p'" ); jack_card->channel_type = SND_PCM_CHANNEL_PLAYBACK; } - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: argument 'type' parsed as %d", - jack_card->channel_type ); break; case 3: /* voices: number of voices in io-audio channel and JACK ports */ if( NULL != value ) @@ -222,10 +208,6 @@ static int parse_commandline( ado_error( "voices option given without value; failing" ); return EINVAL; } - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: argument 'voices' with value '%s' parsed as %d", - value ? value : "NULL", - jack_card->voices ); break; default: break; @@ -238,10 +220,6 @@ static int parse_commandline( value ); } } - - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: finished parsing arguments" ); - ado_free( argdup ); return EOK; } @@ -270,86 +248,6 @@ int32_t cb_aquire( *why_failed = SND_PCM_PARAMS_NO_CHANNEL; return ( EAGAIN ); } - ado_debug( DB_LVL_DRIVER, - "config:" ); - ado_debug( DB_LVL_DRIVER, - " format:" ); - ado_debug( DB_LVL_DRIVER, - " interleave: %d", - config->format.interleave ); - ado_debug( DB_LVL_DRIVER, - " format: {id: %d, name: '%s'}", - config->format.format, - snd_pcm_get_format_name( config->format.format ) ); - ado_debug( DB_LVL_DRIVER, - " rate: %d", - config->format.rate ); - ado_debug( DB_LVL_DRIVER, - " voices: %d", - config->format.voices ); - ado_debug( DB_LVL_DRIVER, - " mode.block:" ); - ado_debug( DB_LVL_DRIVER, - " frag_size: %d", - config->mode.block.frag_size ); - ado_debug( DB_LVL_DRIVER, - " frags_min: %d", - config->mode.block.frags_min ); - ado_debug( DB_LVL_DRIVER, - " frags_max: %d", - config->mode.block.frags_max ); - ado_debug( DB_LVL_DRIVER, - " frags_total: %d", - config->mode.block.frags_total ); - ado_debug( DB_LVL_DRIVER, - " dmabuf:" ); - ado_debug( DB_LVL_DRIVER, - " addr: %x", - config->dmabuf.addr ); - ado_debug( DB_LVL_DRIVER, - " phys_addr: %x", - config->dmabuf.phys_addr ); - ado_debug( DB_LVL_DRIVER, - " size: %d", - config->dmabuf.size ); - ado_debug( DB_LVL_DRIVER, - " name: '%s'", - config->dmabuf.name ); - ado_debug( DB_LVL_DRIVER, - " mixer_device: %d", - config->mixer_device ); - ado_debug( DB_LVL_DRIVER, - " mixer_eid:" ); - ado_debug( DB_LVL_DRIVER, - " type: %d", - config->mixer_eid.type ); - ado_debug( DB_LVL_DRIVER, - " name: '%s'", - config->mixer_eid.name ); - ado_debug( DB_LVL_DRIVER, - " index: %d", - config->mixer_eid.index ); - ado_debug( DB_LVL_DRIVER, - " weight: %d", - config->mixer_eid.weight ); - ado_debug( DB_LVL_DRIVER, - " mixer_gid:" ); - ado_debug( DB_LVL_DRIVER, - " type: %d", - config->mixer_gid.type ); - ado_debug( DB_LVL_DRIVER, - " name: '%s'", - config->mixer_gid.name ); - ado_debug( DB_LVL_DRIVER, - " index: %d", - config->mixer_gid.index ); - ado_debug( DB_LVL_DRIVER, - " weight: %d", - config->mixer_gid.weight ); - - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: creating ringbuffer of %u bytes", - config->dmabuf.size ); /* * Create shared memory region for ringbuffer @@ -358,22 +256,6 @@ int32_t cb_aquire( config->dmabuf.name, ADO_SHM_DMA_SAFE, &config->dmabuf.phys_addr ); - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: ado_shm_alloc() has finished. " ); - ado_debug( DB_LVL_DRIVER, - " dmabuf:" ); - ado_debug( DB_LVL_DRIVER, - " addr: %x", - config->dmabuf.addr ); - ado_debug( DB_LVL_DRIVER, - " phys_addr: %x", - config->dmabuf.phys_addr ); - ado_debug( DB_LVL_DRIVER, - " size: %d", - config->dmabuf.size ); - ado_debug( DB_LVL_DRIVER, - " name: '%s'", - config->dmabuf.name ); /* * Set up JACK ringbuffer structure to use SHM region instead of own @@ -403,10 +285,11 @@ int32_t cb_release( PCM_SUBCHN_CONTEXT_T *pc, ado_pcm_config_t *config ) { - pthread_mutex_lock( &jack_card->process_lock ); - ado_debug( DB_LVL_DRIVER, "deva-ctrl-jack: release()" ); + + pthread_mutex_lock( &jack_card->process_lock ); + jack_card->subchn.go = 0; jack_ringbuffer_reset( &jack_card->ringbuffer ); @@ -517,22 +400,8 @@ int jack_process( if( jack_card->subchn.go && ( 0 == pthread_mutex_trylock( &jack_card->process_lock ) ) ) { - ado_debug( DB_LVL_PCM, - "deva-ctrl-jack: jack_process( %d )", - nframes ); - ado_debug( DB_LVL_PCM, - " total_size: %d", - total_size ); - int voices = jack_card->subchn.pcm_config->format.voices; - ado_debug( DB_LVL_PCM, - " voices: %d", - voices ); - size_t size_per_voice = total_size / voices; - ado_debug( DB_LVL_PCM, - " size_per_voice: %d", - size_per_voice ); void* jack_buf[voices]; for( v = 0; v < voices; ++v ) @@ -544,14 +413,8 @@ int jack_process( for( size_completed = 0; size_completed < total_size; size_completed += size_per_voice ) { - ado_debug( DB_LVL_PCM, - " size_completed: %d", - size_completed ); for( v = 0; v < voices; ++v ) { - ado_debug( DB_LVL_PCM, - " voice: %d", - v ); /* * Advance ringbuffer write pointer on the assumption that io-audio has filled in nframes of data */ @@ -565,16 +428,6 @@ int jack_process( if( SND_PCM_SFMT_FLOAT_LE == jack_card->subchn.pcm_config->format.format ) { - ado_debug( DB_LVL_PCM, - " ringbuffer:" ); - ado_debug( DB_LVL_PCM, - " - { buf: %x, len: %d }", - read_buf[0].buf, - read_buf[0].len ); - ado_debug( DB_LVL_PCM, - " - { buf: %x, len: %d }", - read_buf[1].buf, - read_buf[1].len ); jack_ringbuffer_read( &jack_card->ringbuffer, jack_buf[v], size_per_voice ); @@ -586,19 +439,6 @@ int jack_process( size_t remaining = size_per_voice / sizeof(sample_t); read_buf[0].len /= sizeof(sample_t); read_buf[1].len /= sizeof(sample_t); - ado_debug( DB_LVL_PCM, - " samples_remaining: %d", - remaining ); - ado_debug( DB_LVL_PCM, - " ringbuffer:" ); - ado_debug( DB_LVL_PCM, - " - { buf: %x, len: %d }", - read_buf[0].buf, - read_buf[0].len ); - ado_debug( DB_LVL_PCM, - " - { buf: %x, len: %d }", - read_buf[1].buf, - read_buf[1].len ); int32_t* src = (sample_t*)read_buf[0].buf; sample_t* dest = (sample_t*)jack_buf[v]; @@ -608,22 +448,8 @@ int jack_process( { dest[s] = ( (sample_t)src[s] ) * ( (sample_t)1.0 ) / ( (sample_t)INT_MAX ); -// ado_debug(DB_LVL_PCM, " %x : %d : %f", &src[s], src[s], dest[s]); } remaining -= amt; - ado_debug( DB_LVL_PCM, - " samples_remaining: %d", - remaining ); - ado_debug( DB_LVL_PCM, - " ringbuffer:" ); - ado_debug( DB_LVL_PCM, - " - { buf: %x, len: %d }", - read_buf[0].buf, - read_buf[0].len ); - ado_debug( DB_LVL_PCM, - " - { buf: %x, len: %d }", - read_buf[1].buf, - read_buf[1].len ); if( remaining > 0 ) { src = (int32_t*)read_buf[1].buf; @@ -645,13 +471,14 @@ int jack_process( } dma_interrupt( jack_card->subchn.pcm_subchn ); } - ado_debug( DB_LVL_PCM, - " size_completed: %d", - size_completed ); pthread_mutex_unlock( &jack_card->process_lock ); } else { + /* + * In the case where there is no subchannel or we can't aquire the ringbuffer lock, + * write zeroes to the JACK buffers rather than let stale data sit in them. + */ for( v = 0; v < jack_card->voices; ++v ) { void* jack_buf = jack_port_get_buffer( jack_card->ports[v], @@ -684,6 +511,7 @@ void jack_shutdown( jack_card_t* card = (jack_card_t*)arg; /* + * TODO * Find a way to trigger io-audio to call ctrl_destroy() to clean up */ } @@ -701,8 +529,6 @@ int ctrl_init( ado_debug( DB_LVL_DRIVER, "deva-ctrl-jack: ctrl_init" ); - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: allocating card" ); jack_card = ado_calloc( 1, sizeof(jack_card_t) ); if( NULL == jack_card ) @@ -718,21 +544,14 @@ int ctrl_init( pthread_mutex_init( &jack_card->process_lock, NULL ); - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: setting card name" ); ado_card_set_shortname( card, "jack_card" ); ado_card_set_longname( card, "jack_card", 0x1000 ); - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: parsing arguments '%s'", - args ); retval = parse_commandline( jack_card, args ); - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: returned from parsing arguments" ); if( EOK != retval ) { ado_error( @@ -753,10 +572,6 @@ int ctrl_init( client_name_size ); return -1; } - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: Opening jack client with name '%s' on server '%s'", - jack_card->name, - jack_card->server ? jack_card->server : "DEFAULT" ); jack_status_t open_status; if( NULL != jack_card->server ) { @@ -782,8 +597,6 @@ int ctrl_init( /* * Set JACK and ADO processing callbacks */ - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: registering callbacks" ); jack_set_process_callback( jack_card->client, jack_process, jack_card ); @@ -795,9 +608,6 @@ int ctrl_init( * Allocate jack_port_t array */ int voices = jack_card->voices; - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: allocating array of %d ports", - voices ); jack_card->ports = ado_calloc( voices, sizeof(jack_port_t*) ); @@ -813,9 +623,6 @@ int ctrl_init( port_name_size, PORTNAME_FMT, ( i + 1 ) ); - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: registering port %d", - ( i + 1 ) ); jack_card->ports[i] = jack_port_register( jack_card->client, portname, JACK_DEFAULT_AUDIO_TYPE, @@ -827,20 +634,13 @@ int ctrl_init( /* * Initialize Capabilities */ - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: initializing driver capabilities" ); jack_card->caps.chn_flags = SND_PCM_CHNINFO_BLOCK | SND_PCM_CHNINFO_NONINTERLEAVE; jack_card->caps.formats = 0xFFFF; jack_card->caps.formats = SND_PCM_FMT_FLOAT_LE | SND_PCM_FMT_S32_LE; -// jack_card->caps.formats = SND_PCM_FMT_FLOAT_LE; jack_nframes_t rate = jack_get_sample_rate( jack_card->client ); uint32_t rateflag = ado_pcm_rate2flag( rate ); - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: sample rate of JACK: %u; flag: 0x%x", - rate, - rateflag ); jack_card->caps.rates = rateflag; jack_card->caps.min_voices = 1; @@ -851,16 +651,11 @@ int ctrl_init( */ jack_card->fragsize = jack_get_buffer_size( jack_card->client ) * sizeof(sample_t); - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: fragment is %u bytes", - jack_card->fragsize ); jack_card->caps.min_fragsize = jack_card->fragsize; jack_card->caps.max_fragsize = jack_card->fragsize; jack_card->caps.max_dma_size = 0; jack_card->caps.max_frags = 0; - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: initializing driver callbacks" ); jack_card->funcs.capabilities2 = cb_capabilities; jack_card->funcs.aquire = cb_aquire; jack_card->funcs.release = cb_release; @@ -871,8 +666,6 @@ int ctrl_init( /* * Create Audio PCM Device */ - ado_debug( DB_LVL_DRIVER, - "deva-ctrl-jack: creating PCM device" ); retval = ado_pcm_create( card, "JACK io-audio driver", ( jack_card->channel_type diff --git a/tests/qnx/TestIoAudioAdapter.cpp b/tests/qnx/TestIoAudioAdapter.cpp deleted file mode 100644 index ad4b8aea..00000000 --- a/tests/qnx/TestIoAudioAdapter.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include -#include "JackIoAudioAdapter.h" - -namespace Jack -{ - class TestIoAudioAdapter: public ::testing::Test - { - public: - TestIoAudioAdapter() - { - - } - - }; - - TEST_F( TestIoAudioAdapter, Open_Success ) - { - - } - - TEST_F( TestIoAudioAdapter, Close_Success ) - { - - } - - TEST_F( TestIoAudioAdapter, Create_Success ) - { - - } - - TEST_F( TestIoAudioAdapter, Destroy_Success ) - { - - } - - TEST_F( TestIoAudioAdapter, SetSampleRate_Success ) - { - - } - - TEST_F( TestIoAudioAdapter, SetBufferSize_Success ) - { - - } - - TEST_F( TestIoAudioAdapter, Init_Success ) - { - - } - - TEST_F( TestIoAudioAdapter, Execute_Success ) - { - - } - -} diff --git a/tests/qnx/TestIoAudioInterface.cpp b/tests/qnx/TestIoAudioInterface.cpp deleted file mode 100644 index bf793eb5..00000000 --- a/tests/qnx/TestIoAudioInterface.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include "JackIoAudioAdapter.h" - -namespace Jack -{ - class TestIoAudioInterface: public ::testing::Test - { - public: - TestIoAudioInterface() - { - - } - }; - - TEST_F( TestIoAudioInterface, Open_Success ) - { - - } - - TEST_F( TestIoAudioInterface, Close_Success ) - { - - } - - TEST_F( TestIoAudioInterface, Read_Success ) - { - - } - - TEST_F( TestIoAudioInterface, Write_Success ) - { - - } - - TEST_F( TestIoAudioInterface, ShortInfo_Success ) - { - - } - - TEST_F( TestIoAudioInterface, LongInfo_Success ) - { - - } -} diff --git a/tests/test.cpp b/tests/test.cpp index f995ea32..bedb91b9 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -479,7 +479,7 @@ int process4(jack_nframes_t nframes, void *arg) jack_nframes_t delta_time = cur_time - last_time; Log("calling process4 callback : jack_frame_time = %ld delta_time = %ld\n", cur_time, delta_time); - if (delta_time > 0 && (jack_nframes_t)abs((long int)delta_time - (long int)cur_buffer_size) > tolerance) { + if (delta_time > 0 && (jack_nframes_t)abs((int64_t)delta_time - (int64_t)cur_buffer_size) > tolerance) { printf("!!! ERROR !!! jack_frame_time seems to return incorrect values cur_buffer_size = %d, delta_time = %d tolerance %d\n", cur_buffer_size, delta_time, tolerance); }