diff --git a/Makefile b/Makefile new file mode 100755 index 00000000..3ff58399 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +.PHONY: all +all: + CC=${QNX_HOST}/usr/bin/aarch64-unknown-nto-qnx7.0.0-gcc CXX=${QNX_HOST}/usr/bin/aarch64-unknown-nto-qnx7.0.0-g++ AR=${QNX_HOST}/usr/bin/aarch64-unknown-nto-qnx7.0.0-ar LDFLAGS="-L${INSTALL_ROOT_nto}/aarch64le/lib -L${INSTALL_ROOT_nto}/aarch64le/usr/lib -L${QNX_TARGET}/aarch64le/lib -L${QNX_TARGET}/aarch64le/usr/lib" PKG_CONFIG_LIBDIR=${INSTALL_ROOT_nto}/aarch64le/usr/lib/pkgconfig ./waf configure --platform=qnx --prefix=/usr --libdir=/usr/lib64 + ./waf build + ./waf install --destdir=${INSTALL_ROOT_nto}/aarch64le + +.PHONY: install +install: all + +.PHONY:clean +clean: + # ignore error codes otherwise it would for example fail if clean is called before configure + -./waf clean + diff --git a/common/JackAPI.cpp b/common/JackAPI.cpp index 6789d987..7a58f6de 100644 --- a/common/JackAPI.cpp +++ b/common/JackAPI.cpp @@ -277,6 +277,8 @@ extern "C" LIB_EXPORT void jack_uuid_unparse(jack_uuid_t, char buf[JACK_UUID_STRING_SIZE]); LIB_EXPORT int jack_uuid_empty(jack_uuid_t); + LIB_EXPORT int jack_client_reload_master(jack_client_t* ext_client); + #ifdef __cplusplus } #endif @@ -2140,3 +2142,17 @@ LIB_EXPORT int jack_uuid_empty(jack_uuid_t u) { return u == JACK_UUID_EMPTY_INITIALIZER; } + +LIB_EXPORT int jack_client_reload_master(jack_client_t* ext_client) +{ + JackGlobals::CheckContext("jack_client_reload_master"); + + JackClient* client = (JackClient*)ext_client; + if (client == NULL) { + jack_error("jack_client_reload_master called with a NULL client"); + return -1; + } else { + WaitGraphChange(); + return client->ClientReloadMaster(); + } +} diff --git a/common/JackAudioAdapterInterface.cpp b/common/JackAudioAdapterInterface.cpp index ee90edd4..0aeffa05 100644 --- a/common/JackAudioAdapterInterface.cpp +++ b/common/JackAudioAdapterInterface.cpp @@ -22,7 +22,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #endif #include "JackAudioAdapter.h" -#ifndef MY_TARGET_OS_IPHONE +#if !defined(MY_TARGET_OS_IPHONE) && !defined(__QNXNTO__) #include "JackLibSampleRateResampler.h" #endif #include "JackTime.h" @@ -185,7 +185,7 @@ namespace Jack fRunning = false; } -#ifdef MY_TARGET_OS_IPHONE +#if defined(MY_TARGET_OS_IPHONE) || defined(__QNXNTO__) void JackAudioAdapterInterface::Create() {} #else diff --git a/common/JackAudioDriver.cpp b/common/JackAudioDriver.cpp index ac686e3a..5ca29705 100644 --- a/common/JackAudioDriver.cpp +++ b/common/JackAudioDriver.cpp @@ -199,7 +199,19 @@ int JackAudioDriver::Write() int JackAudioDriver::Process() { - return (fEngineControl->fSyncMode) ? ProcessSync() : ProcessAsync(); + int err = 0; + int retries = -1; + + while (retries++ < fMaxRetryCount) { + err = (fEngineControl->fSyncMode) ? ProcessSync() : ProcessAsync(); + if (err == 0) { + break; + } + + jack_error("JackAudioDriver::Process failed, retry %d", retries); + } + + return err; } /* @@ -211,13 +223,13 @@ int JackAudioDriver::ProcessAsync() { // Read input buffers for the current cycle if (Read() < 0) { - jack_error("JackAudioDriver::ProcessAsync: read error, stopping..."); + jack_error("JackAudioDriver::ProcessAsync: read error"); return -1; } // Write output buffers from the previous cycle if (Write() < 0) { - jack_error("JackAudioDriver::ProcessAsync: write error, stopping..."); + jack_error("JackAudioDriver::ProcessAsync: write error"); return -1; } @@ -285,7 +297,7 @@ int JackAudioDriver::ProcessSync() { // Read input buffers for the current cycle if (Read() < 0) { - jack_error("JackAudioDriver::ProcessSync: read error, stopping..."); + jack_error("JackAudioDriver::ProcessSync: read error"); return -1; } @@ -294,7 +306,7 @@ int JackAudioDriver::ProcessSync() // Write output buffers from the current cycle if (Write() < 0) { - jack_error("JackAudioDriver::ProcessSync: write error, stopping..."); + jack_error("JackAudioDriver::ProcessSync: write error"); return -1; } diff --git a/common/JackAudioDriver.h b/common/JackAudioDriver.h index e6f332f0..76b503cb 100644 --- a/common/JackAudioDriver.h +++ b/common/JackAudioDriver.h @@ -61,6 +61,8 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver void ProcessGraphSyncMaster(); void ProcessGraphSyncSlave(); + static const int fMaxRetryCount = 5; + public: JackAudioDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); diff --git a/common/JackChannel.h b/common/JackChannel.h index 56687043..35aeda8f 100644 --- a/common/JackChannel.h +++ b/common/JackChannel.h @@ -137,6 +137,9 @@ class JackClientChannelInterface virtual void ClientDeactivate(int refnum, int* result) {} + virtual void ClientReloadMaster(int* result) + {} + virtual void PortRegister(int refnum, const char* name, const char* type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result) {} virtual void PortUnRegister(int refnum, jack_port_id_t port_index, int* result) diff --git a/common/JackClient.cpp b/common/JackClient.cpp index b03dbc25..01a00dee 100644 --- a/common/JackClient.cpp +++ b/common/JackClient.cpp @@ -34,6 +34,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. using namespace std; +#ifdef __QNXNTO__ +void __attribute__((constructor)) init_output_for_percent_s_NULL_for_clients(); +void init_output_for_percent_s_NULL_for_clients() +{ + extern const char *output_for_percent_s_NULL; + output_for_percent_s_NULL = "(null)"; +} +#endif + namespace Jack { @@ -493,6 +502,13 @@ int JackClient::Deactivate() if (IsRealTime()) { fThread.Kill(); } + return result; +} + +int JackClient::ClientReloadMaster() +{ + int result = -1; + fChannel->ClientReloadMaster(&result); return result; } diff --git a/common/JackClient.h b/common/JackClient.h index a0e506e7..d18fd774 100644 --- a/common/JackClient.h +++ b/common/JackClient.h @@ -145,6 +145,8 @@ class SERVER_EXPORT JackClient : public JackClientInterface, public JackRunnable virtual int Activate(); virtual int Deactivate(); + virtual int ClientReloadMaster(); + // Context virtual int SetBufferSize(jack_nframes_t buffer_size); virtual int SetFreeWheel(int onoff); diff --git a/common/JackControlAPI.cpp b/common/JackControlAPI.cpp index bbabbac1..7e028948 100644 --- a/common/JackControlAPI.cpp +++ b/common/JackControlAPI.cpp @@ -663,7 +663,10 @@ jackctl_setup_signals( sigfillset(&allsignals); action.sa_handler = signal_handler; action.sa_mask = allsignals; - action.sa_flags = SA_RESTART|SA_RESETHAND; + action.sa_flags = SA_RESETHAND; +#ifndef __QNXNTO__ + action.sa_flags |= SA_RESTART; +#endif for (i = 1; i < NSIG; i++) { diff --git a/common/JackDriver.cpp b/common/JackDriver.cpp index 8700b1c9..8974b2d6 100644 --- a/common/JackDriver.cpp +++ b/common/JackDriver.cpp @@ -110,6 +110,10 @@ int JackDriver::Open(jack_nframes_t buffer_size, fClientControl.fRefNum = refnum; fClientControl.fActive = true; fEngineControl->fDriverNum++; + if (buffer_size > BUFFER_SIZE_MAX) { + jack_error("Buffer size %ld exceeds BUFFER_SIZE_MAX %d", buffer_size, BUFFER_SIZE_MAX); + return -1; + } if (buffer_size > 0) { fEngineControl->fBufferSize = buffer_size; } @@ -351,6 +355,11 @@ int JackDriver::Stop() return StopSlaves(); } +int JackDriver::Reload() +{ + return 0; +} + int JackDriver::StartSlaves() { int res = 0; diff --git a/common/JackDriver.h b/common/JackDriver.h index c66e2500..9caa884a 100644 --- a/common/JackDriver.h +++ b/common/JackDriver.h @@ -72,6 +72,7 @@ class SERVER_EXPORT JackDriverInterface virtual int Start() = 0; virtual int Stop() = 0; + virtual int Reload() = 0; virtual bool IsFixedBufferSize() = 0; virtual int SetBufferSize(jack_nframes_t buffer_size) = 0; @@ -227,6 +228,7 @@ class SERVER_EXPORT JackDriver : public JackDriverClientInterface virtual int Start(); virtual int Stop(); + virtual int Reload(); // For "master" driver int ProcessReadSlaves(); diff --git a/common/JackGenericClientChannel.cpp b/common/JackGenericClientChannel.cpp index 96401d29..c8151240 100644 --- a/common/JackGenericClientChannel.cpp +++ b/common/JackGenericClientChannel.cpp @@ -137,6 +137,13 @@ void JackGenericClientChannel::ClientDeactivate(int refnum, int* result) ServerSyncCall(&req, &res, result); } +void JackGenericClientChannel::ClientReloadMaster(int *result) +{ + JackClientReloadMasterRequest req; + JackResult res; + ServerSyncCall(&req, &res, result); +} + void JackGenericClientChannel::PortRegister(int refnum, const char* name, const char* type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result) { JackPortRegisterRequest req(refnum, name, type, flags, buffer_size); diff --git a/common/JackGenericClientChannel.h b/common/JackGenericClientChannel.h index b2c7b4ad..a82b4a61 100644 --- a/common/JackGenericClientChannel.h +++ b/common/JackGenericClientChannel.h @@ -62,6 +62,8 @@ class JackGenericClientChannel : public detail::JackClientChannelInterface void ClientActivate(int refnum, int is_real_time, int* result); void ClientDeactivate(int refnum, int* result); + void ClientReloadMaster(int* result); + void PortRegister(int refnum, const char* name, const char* type, unsigned int flags, unsigned int buffer_size, jack_port_id_t* port_index, int* result); void PortUnRegister(int refnum, jack_port_id_t port_index, int* result); diff --git a/common/JackRequest.h b/common/JackRequest.h index a1ede642..2048ef5b 100644 --- a/common/JackRequest.h +++ b/common/JackRequest.h @@ -90,7 +90,8 @@ struct JackRequest kGetUUIDByClient = 37, kClientHasSessionCallback = 38, kComputeTotalLatencies = 39, - kPropertyChangeNotify = 40 + kPropertyChangeNotify = 40, + kClientReloadMaster = 41, }; RequestType fType; @@ -1736,6 +1737,31 @@ struct JackClientNotification }; +/*! +\brief Restart Master Backend Request. +*/ + +struct JackClientReloadMasterRequest : public JackRequest +{ + JackClientReloadMasterRequest(): JackRequest(JackRequest::kClientReloadMaster) + { + } + + int Read(detail::JackChannelTransactionInterface* trans) + { + CheckSize(); + return 0; + } + + int Write(detail::JackChannelTransactionInterface* trans) + { + CheckRes(JackRequest::Write(trans, Size())); + return 0; + } + + int Size() { return 0; } +}; + } // end of namespace #endif diff --git a/common/JackRequestDecoder.cpp b/common/JackRequestDecoder.cpp index 46bbd1f3..533d2e50 100644 --- a/common/JackRequestDecoder.cpp +++ b/common/JackRequestDecoder.cpp @@ -346,6 +346,16 @@ int JackRequestDecoder::HandleRequest(detail::JackChannelTransactionInterface* s break; } + case JackRequest::kClientReloadMaster: { + jack_log("JackRequest::ClientReloadMaster"); + JackClientReloadMasterRequest req; + JackResult res; + CheckRead(req, socket); + res.fResult = fServer->ReloadMaster(); + CheckWrite("JackRequest::ClientReloadMaster", socket); + break; + } + default: jack_error("Unknown request %ld", type); return -1; diff --git a/common/JackServer.cpp b/common/JackServer.cpp index 5cee13fb..0f9b4cd3 100644 --- a/common/JackServer.cpp +++ b/common/JackServer.cpp @@ -430,6 +430,11 @@ error: return -1; } +int JackServer::ReloadMaster() +{ + return fAudioDriver->Reload(); +} + //---------------------- // Transport management //---------------------- diff --git a/common/JackServer.h b/common/JackServer.h index 71192e9e..c7921b03 100644 --- a/common/JackServer.h +++ b/common/JackServer.h @@ -98,6 +98,7 @@ class SERVER_EXPORT JackServer JackDriverInfo* AddSlave(jack_driver_desc_t* driver_desc, JSList* driver_params); void RemoveSlave(JackDriverInfo* info); int SwitchMaster(jack_driver_desc_t* driver_desc, JSList* driver_params); + int ReloadMaster(); // Object access JackLockedEngine* GetEngine(); diff --git a/common/JackThreadedDriver.cpp b/common/JackThreadedDriver.cpp index b56a7343..40d03e52 100644 --- a/common/JackThreadedDriver.cpp +++ b/common/JackThreadedDriver.cpp @@ -239,6 +239,28 @@ int JackThreadedDriver::Stop() return 0; } +int JackThreadedDriver::Reload() +{ + if (Stop() < 0) { + jack_error("JackThreadedDriver::Reload stop failed"); + return -1; + } + + // not able to use Close() and Open() since we dont have original Open() parameters, these + // are internal to fDriver, reload should reopen with same parameters + if (fDriver->Reload() < 0) { + jack_error("JackThreadedDriver::Reload reload failed"); + return -1; + } + + if (Start() < 0) { + jack_error("JackThreadedDriver::Reload start failed"); + return -1; + } + + return 0; +} + bool JackThreadedDriver::Execute() { return (Process() == 0); diff --git a/common/JackThreadedDriver.h b/common/JackThreadedDriver.h index cdf10abb..1f83ecad 100644 --- a/common/JackThreadedDriver.h +++ b/common/JackThreadedDriver.h @@ -70,6 +70,7 @@ class SERVER_EXPORT JackThreadedDriver : public JackDriverClientInterface, publi virtual int Start(); virtual int Stop(); + virtual int Reload(); virtual bool IsFixedBufferSize(); virtual int SetBufferSize(jack_nframes_t buffer_size); diff --git a/common/jack/jack.h b/common/jack/jack.h index e982b6df..c5eb27ed 100644 --- a/common/jack/jack.h +++ b/common/jack/jack.h @@ -139,6 +139,22 @@ int jack_client_close (jack_client_t *client) JACK_OPTIONAL_WEAK_EXPORT; */ int jack_client_name_size (void) JACK_OPTIONAL_WEAK_EXPORT; +/** + * Reloads audio backend. + * + * This is useful when client wants audio backend to reload its state. + * Currently used only for alsa audio backend. + * + * Alsa backend will close or open audio devices on reload depending on + * the state of the ports asociated with audio device. If all ports + * are disconnected audio device is closed, opened otherwise. This + * behaviour is modifiable by alsa backend options provided to jackd + * on startup. + * + * @return 0 if successful. + */ +int jack_client_reload_master(jack_client_t* ext_client) JACK_OPTIONAL_WEAK_EXPORT; + /** * @return pointer to actual client name. This is useful when @ref * JackUseExactName is not specified on open and @ref diff --git a/common/jack/systemdeps.h b/common/jack/systemdeps.h old mode 100644 new mode 100755 index 1a111927..2a2ffbeb --- a/common/jack/systemdeps.h +++ b/common/jack/systemdeps.h @@ -107,7 +107,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #endif /* _WIN32 && !__CYGWIN__ && !GNU_WIN32 */ -#if defined(__APPLE__) || defined(__linux__) || defined(__sun__) || defined(sun) || defined(__unix__) || defined(__CYGWIN__) || defined(GNU_WIN32) +#if defined(__APPLE__) || defined(__linux__) || defined(__sun__) || defined(sun) || defined(__unix__) || defined(__CYGWIN__) || defined(GNU_WIN32) || defined(__QNXNTO__) #if defined(__CYGWIN__) || defined(GNU_WIN32) #include diff --git a/common/netjack_packet.c b/common/netjack_packet.c index 4671368f..e59806c1 100644 --- a/common/netjack_packet.c +++ b/common/netjack_packet.c @@ -428,7 +428,9 @@ netjack_poll (int sockfd, int timeout) action.sa_handler = SIG_DFL; action.sa_mask = sigmask; +#ifndef __QNXNTO__ action.sa_flags = SA_RESTART; +#endif for (i = 1; i < NSIG; i++) if (sigismember (&sigmask, i)) diff --git a/common/shm.c b/common/shm.c index b1e7fc71..690de799 100644 --- a/common/shm.c +++ b/common/shm.c @@ -49,11 +49,16 @@ #include #include #include -#include #include #include #include "promiscuous.h" +#ifdef __QNXNTO__ +#include +#else +#include +#endif + #endif #include "shm.h" @@ -148,8 +153,6 @@ static jack_shm_registry_t *jack_shm_registry = NULL; #define JACK_SHM_REGISTRY_KEY JACK_SEMAPHORE_KEY #endif -static int semid = -1; - #ifdef WIN32 #include @@ -178,12 +181,61 @@ static BOOL check_process_running(DWORD process_id) } static int -semaphore_init () {return 0;} +jack_shm_lock_registry () {return 0;} + +static void +jack_shm_unlock_registry () { } + +#elif __QNXNTO__ +#include + +static sem_t* semid = SEM_FAILED; + +static int +semaphore_init () +{ + const char name[] = "/jack-shm-registry-lock"; + const int oflag = O_CREAT | O_RDWR; + const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + if ((semid = sem_open(name, oflag, mode, 1)) == SEM_FAILED) { + jack_error("Creating semaphore %s failed", name); + return -1; + } + + return 0; +} + +static int +jack_shm_lock_registry (void) +{ + if (semid == SEM_FAILED) { + if (semaphore_init () < 0) + return -1; + } + + // TODO automatically unblock in case the process terminates + const int ret = sem_wait(semid); + if (ret < 0) { + jack_error("sem_wait() failed with %s", strerror(ret)); + return -1; + } -static int -semaphore_add (int value) {return 0;} + return 0; +} + +static void +jack_shm_unlock_registry (void) +{ + const int ret = sem_post(semid); + + if (ret < 0) { + jack_error("sem_post() failed with %s", strerror(ret)); + } +} #else +static int semid = -1; + /* all semaphore errors are fatal -- issue message, but do not return */ static void semaphore_error (char *msg) @@ -247,8 +299,6 @@ semaphore_add (int value) return 0; } -#endif - static int jack_shm_lock_registry (void) { @@ -266,6 +316,8 @@ jack_shm_unlock_registry (void) semaphore_add (1); } +#endif + static void jack_shm_init_registry () { @@ -1308,4 +1360,3 @@ jack_attach_shm_read (jack_shm_info_t* si) } #endif /* !USE_POSIX_SHM */ - diff --git a/common/wscript b/common/wscript old mode 100644 new mode 100755 index cb794e28..c41089ea --- a/common/wscript +++ b/common/wscript @@ -28,6 +28,8 @@ def create_jack_process_obj(bld, target, sources, uselib = None, framework = Non env_includes = ['../macosx', '../posix', '../macosx/coreaudio'] if bld.env['IS_LINUX']: env_includes = ['../linux', '../posix', '../linux/alsa'] + if bld.env['IS_QNX']: + env_includes = ['../qnx', '../posix'] if bld.env['IS_SUN']: env_includes = ['../solaris', '../posix', '../solaris/oss'] if bld.env['IS_WINDOWS']: @@ -36,7 +38,7 @@ def create_jack_process_obj(bld, target, sources, uselib = None, framework = Non process.name = target process.target = target process.source = sources - if bld.env['IS_LINUX'] or bld.env['IS_MACOSX']: + if bld.env['IS_LINUX'] or bld.env['IS_MACOSX'] or bld.env['IS_QNX']: process.env.append_value('CPPFLAGS', '-fvisibility=hidden') process.install_path = '${ADDON_DIR}/' process.use = [uselib.name] @@ -92,6 +94,22 @@ def build(bld): uselib.append('RT') uselib.append('DL') + if bld.env['IS_QNX']: + common_libsources += [ + 'JackDebugClient.cpp', + 'timestamps.c', + 'promiscuous.c', + '../posix/JackPosixThread.cpp', + '../posix/JackPosixProcessSync.cpp', + '../posix/JackPosixMutex.cpp', + '../posix/JackSocket.cpp', + '../posix/JackFifo.cpp', + '../linux/JackLinuxTime.c', + ] + includes = ['../qnx', '../posix'] + includes + uselib.append('SOCKET') + # libdl and librt is included in libc in QNX + if bld.env['IS_SUN']: common_libsources += [ 'JackDebugClient.cpp', @@ -163,7 +181,7 @@ def build(bld): 'JackMetadata.cpp', ] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: clientlib.source += [ '../posix/JackSocketClientChannel.cpp', '../posix/JackPosixServerLaunch.cpp', @@ -192,7 +210,7 @@ def build(bld): if not bld.env['IS_WINDOWS']: clientlib.vnum = bld.env['JACK_API_VERSION'] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: clientlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_MACOSX']: @@ -270,7 +288,7 @@ def build(bld): 'JackMetadata.cpp', ] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: serverlib.source += [ '../posix/JackSocketServerChannel.cpp', '../posix/JackSocketNotifyChannel.cpp', @@ -307,7 +325,7 @@ def build(bld): if not bld.env['IS_WINDOWS']: serverlib.vnum = bld.env['JACK_API_VERSION'] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: serverlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_MACOSX']: @@ -333,7 +351,7 @@ def build(bld): if skipshared: netlib.env['SHLIB_MARKER'] = '' netlib.use += ['WS2_32', 'WINMM'] - elif not bld.env['IS_MACOSX']: + elif not bld.env['IS_MACOSX'] and not bld.env['IS_QNX']: netlib.use += ['RT'] netlib.install_path = '${LIBDIR}' netlib.source = [ @@ -347,7 +365,7 @@ def build(bld): 'JackGlobals.cpp', 'ringbuffer.c'] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: netlib.source += ['../posix/JackNetUnixSocket.cpp','../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', '../linux/JackLinuxTime.c'] netlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') diff --git a/example-clients/reload.c b/example-clients/reload.c new file mode 100644 index 00000000..7619efd9 --- /dev/null +++ b/example-clients/reload.c @@ -0,0 +1,32 @@ +#include +#include +#include + + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + printf("usage: %s [server_name [server_name ...]]", argv[0]); + return 1; + } + + const int client_count = argc - 1; + char** server_names = &argv[1]; + + jack_client_t* clients[client_count]; + + for (int i = 0; i < client_count; ++i) { + const jack_options_t options = (jack_options_t) (JackNoStartServer | JackServerName); + jack_status_t status; + + printf("Connecting to JACK server %s\n", server_names[i]); + clients[i] = jack_client_open("reload", options, &status, server_names[i]); + jack_client_reload_master(clients[i]); + } + + for (int i = 0; i < client_count; ++i) { + jack_client_close(clients[i]); + } + + return 0; +} diff --git a/example-clients/wscript b/example-clients/wscript index a8857aa7..542e61b0 100644 --- a/example-clients/wscript +++ b/example-clients/wscript @@ -31,6 +31,7 @@ example_programs = { 'jack_midi_latency_test' : 'midi_latency_test.c', 'jack_simdtests' : 'simdtests.cpp', 'jack_property' : 'property.c', + 'jack_reload' : 'reload.c', } example_libs = { @@ -49,6 +50,8 @@ def build(bld): os_incdir = ['../linux', '../posix'] if bld.env['IS_MACOSX']: os_incdir = ['../macosx', '../posix'] + if bld.env['IS_QNX']: + os_incdir = ['../qnx', '../posix'] if bld.env['IS_SUN']: os_incdir = ['../solaris', '../posix'] if bld.env['IS_WINDOWS']: @@ -76,7 +79,7 @@ def build(bld): prog.use = use if bld.env['IS_LINUX']: prog.use += ['RT', 'M'] - if bld.env['IS_SUN']: + if bld.env['IS_SUN'] or bld.env['IS_QNX']: prog.use += ['M'] #prog.cflags = ['-Wno-deprecated-declarations', '-Wno-misleading-indentation'] #prog.cxxflags = ['-Wno-deprecated-declarations', '-Wno-misleading-indentation'] @@ -90,7 +93,7 @@ def build(bld): prog.use = ['clientlib'] if bld.env['IS_LINUX']: prog.use += ['RT', 'READLINE'] - if bld.env['IS_MACOSX']: + if bld.env['IS_MACOSX'] or bld.env['IS_QNX']: prog.use += ['READLINE'] if bld.env['IS_WINDOWS']: prog.use += ['READLINE'] @@ -101,7 +104,7 @@ def build(bld): prog.includes = os_incdir + ['../common/jack', '../common'] prog.source = 'capture_client.c' prog.use = ['clientlib'] - if bld.env['IS_MACOSX']: + if bld.env['IS_MACOSX'] or bld.env['IS_QNX']: prog.use += ['SNDFILE'] if bld.env['IS_LINUX']: prog.use += ['RT', 'SNDFILE'] @@ -111,7 +114,7 @@ def build(bld): prog.uselib = ['SNDFILE'] prog.target = 'jack_rec' - if bld.env['IS_LINUX'] or bld.env['IS_MACOSX']: + if bld.env['IS_LINUX'] or bld.env['IS_MACOSX'] or bld.env['IS_QNX']: prog = bld(features = 'c cprogram') prog.includes = os_incdir + ['.', '..', '../common/jack', '../common'] prog.source = ['netsource.c', '../common/netjack_packet.c'] diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index 0bd904d5..e848e2ac 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -32,6 +32,11 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include #include #include +#include +#include +#include +#include +#include #include "JackAlsaDriver.h" #include "JackEngineControl.h" @@ -126,7 +131,7 @@ void JackAlsaDriver::UpdateLatencies() int JackAlsaDriver::Attach() { JackPort* port; - jack_port_id_t port_index; + jack_port_id_t port_id; unsigned long port_flags = (unsigned long)CaptureDriverFlags; char name[REAL_JACK_PORT_NAME_SIZE+1]; char alias[REAL_JACK_PORT_NAME_SIZE+1]; @@ -145,41 +150,47 @@ int JackAlsaDriver::Attach() jack_log("JackAlsaDriver::Attach fBufferSize %ld fSampleRate %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); - for (int i = 0; i < fCaptureChannels; i++) { - snprintf(alias, sizeof(alias), "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); - snprintf(name, sizeof(name), "%s:capture_%d", fClientControl.fName, i + 1); - if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize, &port_index) < 0) { + for (int i = 0, port_list_index = 0; i < alsa_driver->devices_c_count; ++i) { + alsa_device_t *device = &alsa_driver->devices[i]; + for (int j = 0; j < device->capture_nchannels; ++j, ++port_list_index) { + snprintf(name, sizeof(name), "%s:capture_%d", fClientControl.fName, port_list_index + 1); + snprintf(alias, sizeof(alias), "%s:%s:capture_%d", fAliasName, device->capture_name, j + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize, &port_id) < 0) { jack_error("driver: cannot register port for %s", name); - return -1; + return -1; + } + port = fGraphManager->GetPort(port_id); + port->SetAlias(alias); + fCapturePortList[port_list_index] = port_id; + jack_log("JackAlsaDriver::Attach fCapturePortList[i] %ld ", port_id); } - port = fGraphManager->GetPort(port_index); - port->SetAlias(alias); - fCapturePortList[i] = port_index; - jack_log("JackAlsaDriver::Attach fCapturePortList[i] %ld ", port_index); } port_flags = (unsigned long)PlaybackDriverFlags; - for (int i = 0; i < fPlaybackChannels; i++) { - snprintf(alias, sizeof(alias), "%s:%s:in%d", fAliasName, fPlaybackDriverName, i + 1); - snprintf(name, sizeof(name), "%s:playback_%d", fClientControl.fName, i + 1); - if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize, &port_index) < 0) { - jack_error("driver: cannot register port for %s", name); - return -1; - } - port = fGraphManager->GetPort(port_index); - port->SetAlias(alias); - fPlaybackPortList[i] = port_index; - jack_log("JackAlsaDriver::Attach fPlaybackPortList[i] %ld ", port_index); + for (int i = 0, port_list_index = 0; i < alsa_driver->devices_p_count; ++i) { + alsa_device_t *device = &alsa_driver->devices[i]; + for (int j = 0; j < device->playback_nchannels; ++j, ++port_list_index) { + snprintf(name, sizeof(name), "%s:playback_%d", fClientControl.fName, port_list_index + 1); + snprintf(alias, sizeof(alias), "%s:%s:playback_%d", fAliasName, device->playback_name, j + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, (JackPortFlags)port_flags, fEngineControl->fBufferSize, &port_id) < 0) { + jack_error("driver: cannot register port for %s", name); + return -1; + } + port = fGraphManager->GetPort(port_id); + port->SetAlias(alias); + fPlaybackPortList[port_list_index] = port_id; + jack_log("JackAlsaDriver::Attach fPlaybackPortList[i] %ld ", port_id); - // Monitor ports - if (fWithMonitorPorts) { - jack_log("Create monitor port"); - snprintf(name, sizeof(name), "%s:monitor_%d", fClientControl.fName, i + 1); - if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, MonitorDriverFlags, fEngineControl->fBufferSize, &port_index) < 0) { - jack_error("ALSA: cannot register monitor port for %s", name); - } else { - fMonitorPortList[i] = port_index; + // Monitor ports + if (fWithMonitorPorts) { + jack_log("Create monitor port"); + snprintf(name, sizeof(name), "%s:monitor_%d", fClientControl.fName, port_list_index + 1); + if (fEngine->PortRegister(fClientControl.fRefNum, name, JACK_DEFAULT_AUDIO_TYPE, MonitorDriverFlags, fEngineControl->fBufferSize, &port_id) < 0) { + jack_error("ALSA: cannot register monitor port for %s", name); + } else { + fMonitorPortList[port_list_index] = port_id; + } } } } @@ -204,6 +215,7 @@ int JackAlsaDriver::Detach() return JackAudioDriver::Detach(); } +#ifndef __QNXNTO__ extern "C" char* get_control_device_name(const char * device_name) { char * ctl_name; @@ -233,7 +245,9 @@ extern "C" char* get_control_device_name(const char * device_name) return ctl_name; } +#endif +#ifndef __QNXNTO__ static int card_to_num(const char* device) { int err; @@ -273,50 +287,47 @@ free: fail: return i; } +#endif -int JackAlsaDriver::Open(jack_nframes_t nframes, - jack_nframes_t user_nperiods, - jack_nframes_t samplerate, - bool hw_monitoring, - bool hw_metering, - bool capturing, - bool playing, - DitherAlgorithm dither, - bool soft_mode, - bool monitor, - int inchannels, - int outchannels, - bool shorts_first, - const char* capture_driver_name, - const char* playback_driver_name, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency, - const char* midi_driver_name) +int JackAlsaDriver::Open(alsa_driver_info_t info) { // Generic JackAudioDriver Open - if (JackAudioDriver::Open(nframes, samplerate, capturing, playing, - inchannels, outchannels, monitor, capture_driver_name, playback_driver_name, - capture_latency, playback_latency) != 0) { + if (JackAudioDriver::Open( + info.frames_per_period, + info.frame_rate, + info.devices_capture_size > 0, + info.devices_playback_size > 0, + -1, + -1, + info.monitor, + info.devices_capture_size > 0 ? info.devices[0].capture_name : "-", + info.devices_playback_size > 0 ? info.devices[0].playback_name : "-", + info.capture_latency, + info.playback_latency) != 0) { return -1; } - alsa_midi_t *midi = 0; + jack_log("JackAlsaDriver::Open capture_driver_name = %s", info.devices_capture_size > 0 ? info.devices[0].capture_name : "-"); + jack_log("JackAlsaDriver::Open playback_driver_name = %s", info.devices_playback_size > 0 ? info.devices[0].playback_name : "-"); + +#ifndef __QNXNTO__ #ifndef __ANDROID__ - if (strcmp(midi_driver_name, "seq") == 0) - midi = alsa_seqmidi_new((jack_client_t*)this, 0); - else if (strcmp(midi_driver_name, "raw") == 0) - midi = alsa_rawmidi_new((jack_client_t*)this); + if (strcmp(info.midi_name, "seq") == 0) + info.midi_driver = alsa_seqmidi_new((jack_client_t*)this, 0); + else if (strcmp(info.midi_name, "raw") == 0) + info.midi_driver = alsa_rawmidi_new((jack_client_t*)this); #endif + // FIXME: needs adaptation for multiple drivers if (JackServerGlobals::on_device_acquire != NULL) { - int capture_card = card_to_num(capture_driver_name); - int playback_card = card_to_num(playback_driver_name); + int capture_card = card_to_num(info.devices_capture_size > 0 ? info.devices[0].capture_name : "-"); + int playback_card = card_to_num(info.devices_playback_size > 0 ? info.devices[0].playback_name : "-"); char audio_name[32]; if (capture_card >= 0) { snprintf(audio_name, sizeof(audio_name), "Audio%d", capture_card); if (!JackServerGlobals::on_device_acquire(audio_name)) { - jack_error("Audio device %s cannot be acquired...", capture_driver_name); + jack_error("Audio device %s cannot be acquired...", info.devices_capture_size > 0 ? info.devices[0].capture_name : "-"); return -1; } } @@ -324,7 +335,7 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, if (playback_card >= 0 && playback_card != capture_card) { snprintf(audio_name, sizeof(audio_name), "Audio%d", playback_card); if (!JackServerGlobals::on_device_acquire(audio_name)) { - jack_error("Audio device %s cannot be acquired...", playback_driver_name); + jack_error("Audio device %s cannot be acquired...",info.devices_playback_size > 0 ? info.devices[0].playback_name : "-" ); if (capture_card >= 0) { snprintf(audio_name, sizeof(audio_name), "Audio%d", capture_card); JackServerGlobals::on_device_release(audio_name); @@ -333,40 +344,56 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, } } } +#endif - fDriver = alsa_driver_new ((char*)"alsa_pcm", (char*)playback_driver_name, (char*)capture_driver_name, - NULL, - nframes, - user_nperiods, - samplerate, - hw_monitoring, - hw_metering, - capturing, - playing, - dither, - soft_mode, - monitor, - inchannels, - outchannels, - shorts_first, - capture_latency, - playback_latency, - midi); - if (fDriver) { - // ALSA driver may have changed the in/out values - fCaptureChannels = ((alsa_driver_t *)fDriver)->capture_nchannels; - fPlaybackChannels = ((alsa_driver_t *)fDriver)->playback_nchannels; - if (JackServerGlobals::on_device_reservation_loop != NULL) { - device_reservation_loop_running = true; - if (JackPosixThread::StartImp(&fReservationLoopThread, 0, 0, on_device_reservation_loop, NULL) != 0) { - device_reservation_loop_running = false; + fDriver = alsa_driver_new ((char*)"alsa_pcm", info, NULL); + if (!fDriver) { + Close(); + return -1; + } + + /* we need to initialize variables for all devices, mainly channels count since this is required by Jack to setup ports */ + UpdateDriverTargetState(DriverMode::Init); + + if (alsa_driver_open((alsa_driver_t *)fDriver) < 0) { + Close(); + return -1; + } + + /* we are starting with all alsa devices closed, therfore populate jack channels based on user hint */ + if (info.features & ALSA_DRIVER_FEAT_BACKEND_EVAL_ON_INIT && info.features & ALSA_DRIVER_FEAT_BACKEND_CLOSE_IDLE) { + for (size_t i = 0; i < std::max(info.devices_capture_size, info.devices_playback_size); ++i) { + if (i < info.devices_capture_size && info.devices[i].capture_channels < 1) { + jack_error ("invalid or missing channels parameter with '-x' option 'start-closed' for device C: '%s'", info.devices[i].capture_name); + Close(); + return -1; } + + if (i < info.devices_playback_size && info.devices[i].playback_channels < 1) { + jack_error ("invalid or missing channels parameter with '-x' option 'start-closed' for device P: '%s'", info.devices[i].playback_name); + Close(); + return -1; + } + + fCaptureChannels += info.devices[i].capture_channels; + fPlaybackChannels += info.devices[i].playback_channels; } - return 0; + /* in case we really opened alsa devices, channel information is generated by driver */ } else { - Close(); - return -1; + fCaptureChannels = ((alsa_driver_t *)fDriver)->capture_nchannels; + fPlaybackChannels = ((alsa_driver_t *)fDriver)->playback_nchannels; } + +#ifndef __QNXNTO__ + if (JackServerGlobals::on_device_reservation_loop != NULL) { + device_reservation_loop_running = true; + if (JackPosixThread::StartImp(&fReservationLoopThread, 0, 0, on_device_reservation_loop, NULL) != 0) { + device_reservation_loop_running = false; + } + } +#endif + + return 0; } int JackAlsaDriver::Close() @@ -374,15 +401,20 @@ int JackAlsaDriver::Close() // Generic audio driver close int res = JackAudioDriver::Close(); + UpdateDriverTargetState(DriverMode::Shutdown); + alsa_driver_close((alsa_driver_t *)fDriver); + if (fDriver) { alsa_driver_delete((alsa_driver_t*)fDriver); } +#ifndef __QNXNTO__ if (device_reservation_loop_running) { device_reservation_loop_running = false; JackPosixThread::StopImp(fReservationLoopThread); } + // FIXME: needs adaptation for multiple drivers if (JackServerGlobals::on_device_release != NULL) { char audio_name[32]; @@ -398,6 +430,7 @@ int JackAlsaDriver::Close() JackServerGlobals::on_device_release(audio_name); } } +#endif return res; } @@ -423,28 +456,39 @@ int JackAlsaDriver::Stop() return res; } +int JackAlsaDriver::Reload() +{ + UpdateDriverTargetState(DriverMode::Runtime); + + alsa_driver_t* driver = (alsa_driver_t*) fDriver; + if (alsa_driver_close (driver) < 0) { + jack_error("JackAlsaDriver::Reload close failed"); + return -1; + } + if (alsa_driver_open (driver) < 0) { + jack_error("JackAlsaDriver::Reload open failed"); + return -1; + } + + return 0; +} + int JackAlsaDriver::Read() { /* Taken from alsa_driver_run_cycle */ - int wait_status; - jack_nframes_t nframes; + alsa_driver_wait_status_t wait_status; + jack_nframes_t nframes = 0; fDelayedUsecs = 0.f; -retry: - - nframes = alsa_driver_wait((alsa_driver_t *)fDriver, -1, &wait_status, &fDelayedUsecs); - - if (wait_status < 0) - return -1; /* driver failed */ + /* wait until all devices have some data available */ + do { + nframes = alsa_driver_wait((alsa_driver_t *)fDriver, -1, &wait_status, &fDelayedUsecs); - if (nframes == 0) { - /* we detected an xrun and restarted: notify - * clients about the delay. - */ - jack_log("ALSA XRun wait_status = %d", wait_status); - NotifyXRun(fBeginDateUst, fDelayedUsecs); - goto retry; /* recoverable error*/ - } + if (wait_status != ALSA_DRIVER_WAIT_OK) { + jack_error("JackAlsaDriver::Read wait failed, xrun recovery"); + goto retry; + } + } while (nframes == 0); if (nframes != fEngineControl->fBufferSize) jack_log("JackAlsaDriver::Read warning fBufferSize = %ld nframes = %ld", fEngineControl->fBufferSize, nframes); @@ -452,20 +496,45 @@ retry: // Has to be done before read JackDriver::CycleIncTime(); - return alsa_driver_read((alsa_driver_t *)fDriver, fEngineControl->fBufferSize); + if (alsa_driver_read((alsa_driver_t *)fDriver, fEngineControl->fBufferSize) != 0) { + jack_error("JackAlsaDriver::Read read failed, xrun recovery"); + goto retry; + } + + return 0; + +retry: + /* we detected an xrun and restarted: notify + * clients about the delay. + */ + jack_error("JackAlsaDriver::Read failed, xrun recovery"); + alsa_driver_xrun_recovery((alsa_driver_t *)fDriver, &fDelayedUsecs); + NotifyXRun(fBeginDateUst, fDelayedUsecs); + + return -1; } int JackAlsaDriver::Write() { - return alsa_driver_write((alsa_driver_t *)fDriver, fEngineControl->fBufferSize); + if (alsa_driver_write((alsa_driver_t *)fDriver, fEngineControl->fBufferSize) != 0) { + jack_error("JackAlsaDriver::Write failed, xrun recovery"); + alsa_driver_xrun_recovery((alsa_driver_t *)fDriver, &fDelayedUsecs); + NotifyXRun(fBeginDateUst, fDelayedUsecs); + return -1; + } + + return 0; } -void JackAlsaDriver::ReadInputAux(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread) +void JackAlsaDriver::ReadInputAux(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread) { - for (int chn = 0; chn < fCaptureChannels; chn++) { - if (fGraphManager->GetConnectionsNum(fCapturePortList[chn]) > 0) { - jack_default_audio_sample_t* buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[chn], orig_nframes); - alsa_driver_read_from_channel((alsa_driver_t *)fDriver, chn, buf + nread, contiguous); + /* global channel offset to fCapturePortList of this capture alsa device */ + channel_t port_n = device->capture_channel_offset; + + for (channel_t chn = 0; chn < device->capture_nchannels; ++chn, ++port_n) { + if (fGraphManager->GetConnectionsNum(fCapturePortList[port_n]) > 0) { + jack_default_audio_sample_t* buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[port_n], orig_nframes); + alsa_driver_read_from_channel((alsa_driver_t *)fDriver, device, chn, buf + nread, contiguous); } } } @@ -499,16 +568,69 @@ int JackAlsaDriver::PortSetDefaultMetadata(jack_port_id_t port_id, const char* p return fEngine->PortSetDefaultMetadata(fClientControl.fRefNum, port_id, pretty_name); } -void JackAlsaDriver::WriteOutputAux(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten) +int JackAlsaDriver::UpdateDriverTargetState(DriverMode mode) { - for (int chn = 0; chn < fPlaybackChannels; chn++) { + int c_list_index = 0, p_list_index = 0; + alsa_driver_t* driver = (alsa_driver_t*) fDriver; + + for (int i = 0; i < driver->devices_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + int capture_connections_count = 0; + for (int j = 0; j < device->capture_nchannels; ++j) { + capture_connections_count += fGraphManager->GetConnectionsNum(fCapturePortList[c_list_index]); + c_list_index++; + } + device->capture_target_state = TargetState(mode, capture_connections_count); + + int playback_connections_count = 0; + for (int j = 0; j < device->playback_nchannels; ++j) { + playback_connections_count += fGraphManager->GetConnectionsNum(fPlaybackPortList[p_list_index]); + p_list_index++; + } + device->playback_target_state = TargetState(mode, playback_connections_count); + } + + return 0; +} + +int JackAlsaDriver::TargetState(DriverMode mode, int connections_count) +{ + alsa_driver_t* driver = (alsa_driver_t*) fDriver; + + if (mode == DriverMode::Shutdown) { + return SND_PCM_STATE_NOTREADY; + } + + if (connections_count > 0) { + return SND_PCM_STATE_RUNNING; + } + + // evaluation during init is disabled by user option + if (mode == DriverMode::Init && !(driver->features & ALSA_DRIVER_FEAT_BACKEND_EVAL_ON_INIT)) { + return SND_PCM_STATE_RUNNING; + } + + if (driver->features & ALSA_DRIVER_FEAT_BACKEND_CLOSE_IDLE) { + return SND_PCM_STATE_NOTREADY; + } + + return SND_PCM_STATE_PREPARED; +} + +void JackAlsaDriver::WriteOutputAux(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten) +{ + /* global channel offset to fPlaybackPortList of this playback alsa device */ + channel_t port_n = device->playback_channel_offset; + + for (channel_t chn = 0; chn < device->playback_nchannels; ++chn, ++port_n) { // Output ports - if (fGraphManager->GetConnectionsNum(fPlaybackPortList[chn]) > 0) { - jack_default_audio_sample_t* buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[chn], orig_nframes); - alsa_driver_write_to_channel(((alsa_driver_t *)fDriver), chn, buf + nwritten, contiguous); + if (fGraphManager->GetConnectionsNum(fPlaybackPortList[port_n]) > 0) { + jack_default_audio_sample_t* buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[port_n], orig_nframes); + alsa_driver_write_to_channel(((alsa_driver_t *)fDriver), device, chn, buf + nwritten, contiguous); // Monitor ports - if (fWithMonitorPorts && fGraphManager->GetConnectionsNum(fMonitorPortList[chn]) > 0) { - jack_default_audio_sample_t* monbuf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fMonitorPortList[chn], orig_nframes); + if (fWithMonitorPorts && fGraphManager->GetConnectionsNum(fMonitorPortList[port_n]) > 0) { + jack_default_audio_sample_t* monbuf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fMonitorPortList[port_n], orig_nframes); memcpy(monbuf + nwritten, buf + nwritten, contiguous * sizeof(jack_default_audio_sample_t)); } } @@ -578,6 +700,7 @@ extern "C" { #endif +#ifndef __QNXNTO__ static jack_driver_param_constraint_desc_t * enum_alsa_devices() @@ -667,6 +790,7 @@ fail: jack_constraint_free(constraint_ptr); return NULL; } +#endif static int dither_opt (char c, DitherAlgorithm* dither) @@ -705,11 +829,14 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () desc = jack_driver_descriptor_construct("alsa", JackDriverMaster, "Linux ALSA API based audio backend", &filler); strcpy(value.str, "hw:0"); +#ifndef __QNXNTO__ #ifdef __ANDROID__ jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, NULL, "ALSA device name", NULL); #else jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, enum_alsa_devices(), "ALSA device name", NULL); #endif +#endif + strcpy(value.str, "none"); jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamString, &value, NULL, "Provide capture ports. Optionally set device", NULL); @@ -753,9 +880,9 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () "Dithering mode", NULL); - value.ui = 0; - jack_driver_descriptor_add_parameter(desc, &filler, "inchannels", 'i', JackDriverParamUInt, &value, NULL, "Number of capture channels (defaults to hardware max)", NULL); - jack_driver_descriptor_add_parameter(desc, &filler, "outchannels", 'o', JackDriverParamUInt, &value, NULL, "Number of playback channels (defaults to hardware max)", NULL); + strcpy(value.str, "none"); + jack_driver_descriptor_add_parameter(desc, &filler, "inchannels", 'i', JackDriverParamString, &value, NULL, "List of device capture channels (defaults to hw max)", NULL); + jack_driver_descriptor_add_parameter(desc, &filler, "outchannels", 'o', JackDriverParamString, &value, NULL, "List of device playback channels (defaults to hw max)", NULL); value.i = FALSE; jack_driver_descriptor_add_parameter(desc, &filler, "shorts", 'S', JackDriverParamBool, &value, NULL, "Try 16-bit samples before 32-bit", NULL); @@ -778,33 +905,94 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () "ALSA MIDI driver", NULL); + value.i = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "eval-on-init", 'x', JackDriverParamBool, &value, NULL, "Do not start ALSA devices on jack startup", NULL); + + value.i = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "close-idle", 'c', JackDriverParamBool, &value, NULL, "Close idle devices on alsa driver restart request", NULL); + + value.i = 0; + jack_driver_descriptor_add_parameter(desc, &filler, "unlinked-devs", 'u', JackDriverParamBool, &value, NULL, "Do not link devices", NULL); + return desc; } +struct array_string_t +{ + enum flags { + none, + discard_duplicate, + }; + + std::vector data; +}; + +void array_string_free(struct array_string_t *obj) +{ + if (obj == NULL) { + return; + } + for (size_t i = 0; i < obj->data.size(); ++i) { + free(obj->data[i]); + } +} + +struct array_string_t array_string_split(const char *str, const char sep, array_string_t::flags flags = array_string_t::none) +{ + struct array_string_t result; + + std::stringstream stream; + stream << std::string(str); + if (stream.str().find(sep) == std::string::npos) { + result.data.push_back((char*) calloc(JACK_CLIENT_NAME_SIZE + 1, sizeof(char))); + strncpy(result.data[0], str, JACK_CLIENT_NAME_SIZE); + result.data[0][JACK_CLIENT_NAME_SIZE] = '\0'; + return result; + } + + std::string driver; + while (std::getline(stream, driver, sep)) { + driver.erase(std::remove_if(driver.begin(), driver.end(), isspace), driver.end()); + if (std::find(result.data.begin(), result.data.end(), driver) != result.data.end() && (flags & array_string_t::discard_duplicate)) + continue; + char *str = (char*) calloc(JACK_CLIENT_NAME_SIZE + 1, sizeof(char)); + strncpy(str, driver.c_str(), JACK_CLIENT_NAME_SIZE); + str[JACK_CLIENT_NAME_SIZE] = '\0'; + result.data.push_back(str); + } + + return result; +} + static Jack::JackAlsaDriver* g_alsa_driver; SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) { - jack_nframes_t srate = 48000; - jack_nframes_t frames_per_interrupt = 1024; - unsigned long user_nperiods = 2; - const char *playback_pcm_name = "hw:0"; - const char *capture_pcm_name = "hw:0"; - int hw_monitoring = FALSE; - int hw_metering = FALSE; - int capture = FALSE; - int playback = FALSE; - int soft_mode = FALSE; - int monitor = FALSE; - DitherAlgorithm dither = None; - int user_capture_nchnls = 0; - int user_playback_nchnls = 0; - int shorts_first = FALSE; - jack_nframes_t systemic_input_latency = 0; - jack_nframes_t systemic_output_latency = 0; const JSList * node; const jack_driver_param_t * param; - const char *midi_driver = "none"; + + alsa_driver_info_t info = {}; + info.devices = NULL; + info.midi_name = strdup("none"); + info.hw_monitoring = FALSE; + info.hw_metering = FALSE; + info.monitor = FALSE; + info.soft_mode = FALSE; + info.frame_rate = 48000; + info.frames_per_period = 1024; + info.periods_n = 2; + info.dither = None; + info.shorts_first = FALSE; + info.capture_latency = 0; + info.playback_latency = 0; + + char *capture_names_param = NULL; + char *playback_names_param = NULL; + + char *capture_channels_param = NULL; + char *playback_channels_param = NULL; + + int duplex = FALSE; for (node = params; node; node = jack_slist_next (node)) { param = (const jack_driver_param_t *) node->data; @@ -812,112 +1000,175 @@ SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLocke switch (param->character) { case 'C': - capture = TRUE; if (strcmp (param->value.str, "none") != 0) { - capture_pcm_name = strdup (param->value.str); - jack_log("capture device %s", capture_pcm_name); + capture_names_param = strdup (param->value.str); + jack_log("capture device %s", capture_names_param); } break; case 'P': - playback = TRUE; if (strcmp (param->value.str, "none") != 0) { - playback_pcm_name = strdup (param->value.str); - jack_log("playback device %s", playback_pcm_name); + playback_names_param = strdup (param->value.str); + jack_log("playback device %s", playback_names_param); } break; case 'D': - playback = TRUE; - capture = TRUE; + duplex = TRUE; break; case 'd': if (strcmp (param->value.str, "none") != 0) { - playback_pcm_name = strdup (param->value.str); - capture_pcm_name = strdup (param->value.str); - jack_log("playback device %s", playback_pcm_name); - jack_log("capture device %s", capture_pcm_name); + playback_names_param = strdup (param->value.str); + capture_names_param = strdup (param->value.str); + jack_log("playback device %s", playback_names_param); + jack_log("capture device %s", capture_names_param); } break; case 'H': - hw_monitoring = param->value.i; + info.hw_monitoring = param->value.i; break; case 'm': - monitor = param->value.i; + info.monitor = param->value.i; break; case 'M': - hw_metering = param->value.i; + info.hw_metering = param->value.i; break; case 'r': - srate = param->value.ui; - jack_log("apparent rate = %d", srate); + info.frame_rate = param->value.ui; + jack_log("apparent rate = %d", info.frame_rate); break; case 'p': - frames_per_interrupt = param->value.ui; - jack_log("frames per period = %d", frames_per_interrupt); + info.frames_per_period = param->value.ui; + jack_log("frames per period = %d", info.frames_per_period); break; case 'n': - user_nperiods = param->value.ui; - if (user_nperiods < 2) { /* enforce minimum value */ - user_nperiods = 2; + info.periods_n = param->value.ui; + if (info.periods_n < 2) { /* enforce minimum value */ + info.periods_n = 2; } break; case 's': - soft_mode = param->value.i; + info.soft_mode = param->value.i; break; case 'z': - if (dither_opt (param->value.c, &dither)) { + if (dither_opt (param->value.c, &info.dither)) { return NULL; } break; case 'i': - user_capture_nchnls = param->value.ui; + capture_channels_param = strdup(param->value.str); break; case 'o': - user_playback_nchnls = param->value.ui; + playback_channels_param = strdup(param->value.str); break; case 'S': - shorts_first = param->value.i; + info.shorts_first = param->value.i; break; case 'I': - systemic_input_latency = param->value.ui; + info.capture_latency = param->value.ui; break; case 'O': - systemic_output_latency = param->value.ui; + info.playback_latency = param->value.ui; break; case 'X': - midi_driver = strdup(param->value.str); + free(info.midi_name); + info.midi_name = strdup(param->value.str); + break; + + case 'x': + info.features |= param->value.i ? ALSA_DRIVER_FEAT_BACKEND_EVAL_ON_INIT : 0; + break; + + case 'c': + info.features |= param->value.i ? ALSA_DRIVER_FEAT_BACKEND_CLOSE_IDLE : 0; + break; + + case 'u': + info.features |= param->value.i ? ALSA_DRIVER_FEAT_UNLINKED_DEVS : 0; break; } } /* duplex is the default */ - if (!capture && !playback) { - capture = TRUE; - playback = TRUE; + if (!capture_names_param && !playback_names_param) { + duplex = TRUE; + } + + if (duplex) { + if (!capture_names_param) { + capture_names_param = strdup("hw:0"); + } + if (!playback_names_param) { + playback_names_param = strdup("hw:0"); + } + } + + struct array_string_t capture_names = {}; + if (capture_names_param) { + capture_names = array_string_split(capture_names_param, ' ', array_string_t::discard_duplicate); + free(capture_names_param); } + struct array_string_t playback_names = {}; + if (playback_names_param) { + playback_names = array_string_split(playback_names_param, ' ', array_string_t::discard_duplicate); + free(playback_names_param); + } + + struct array_string_t capture_channels = {}; + if (capture_channels_param) { + capture_channels = array_string_split(capture_channels_param, ' '); + free(capture_channels_param); + } + + struct array_string_t playback_channels = {}; + if (playback_channels_param) { + playback_channels = array_string_split(playback_channels_param, ' '); + free(playback_channels_param); + } + + info.devices_capture_size = capture_names.data.size(); + info.devices_playback_size = playback_names.data.size(); + info.devices = (alsa_device_info_t*) calloc(std::max(info.devices_capture_size, info.devices_playback_size), sizeof(alsa_device_info_t)); + for (size_t i = 0; i < std::max(info.devices_capture_size, info.devices_playback_size); ++i) { + if (i < capture_names.data.size()) { + info.devices[i].capture_name = strdup(capture_names.data[i]); + } + if (i < capture_channels.data.size()) { + info.devices[i].capture_channels = atoi(capture_channels.data[i]); + } + if (i < playback_names.data.size()) { + info.devices[i].playback_name = strdup(playback_names.data[i]); + } + if (i < playback_channels.data.size()) { + info.devices[i].playback_channels = atoi(playback_channels.data[i]); + } + } + + array_string_free(&capture_names); + array_string_free(&playback_names); + array_string_free(&capture_channels); + array_string_free(&playback_channels); + g_alsa_driver = new Jack::JackAlsaDriver("system", "alsa_pcm", engine, table); Jack::JackDriverClientInterface* threaded_driver = new Jack::JackThreadedDriver(g_alsa_driver); // Special open for ALSA driver... - if (g_alsa_driver->Open(frames_per_interrupt, user_nperiods, srate, hw_monitoring, hw_metering, capture, playback, dither, soft_mode, monitor, - user_capture_nchnls, user_playback_nchnls, shorts_first, capture_pcm_name, playback_pcm_name, - systemic_input_latency, systemic_output_latency, midi_driver) == 0) { + if (g_alsa_driver->Open(info) == 0) { return threaded_driver; } else { delete threaded_driver; // Delete the decorated driver @@ -927,9 +1178,9 @@ SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLocke // Code to be used in alsa_driver.c -void ReadInput(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread) +void ReadInput(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread) { - g_alsa_driver->ReadInputAux(orig_nframes, contiguous, nread); + g_alsa_driver->ReadInputAux(device, orig_nframes, contiguous, nread); } void MonitorInput() { @@ -939,9 +1190,9 @@ void ClearOutput() { g_alsa_driver->ClearOutputAux(); } -void WriteOutput(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten) +void WriteOutput(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten) { - g_alsa_driver->WriteOutputAux(orig_nframes, contiguous, nwritten); + g_alsa_driver->WriteOutputAux(device, orig_nframes, contiguous, nwritten); } void SetTime(jack_time_t time) { @@ -951,8 +1202,13 @@ void SetTime(jack_time_t time) int Restart() { int res; - if ((res = g_alsa_driver->Stop()) == 0) { - res = g_alsa_driver->Start(); + if ((res = g_alsa_driver->Stop()) != 0) { + jack_error("restart: stop driver failed"); + return res; + } + if ((res = g_alsa_driver->Start()) != 0) { + jack_error("restart: start driver failed"); + return res; } return res; } diff --git a/linux/alsa/JackAlsaDriver.h b/linux/alsa/JackAlsaDriver.h index da055f67..52fc47a8 100644 --- a/linux/alsa/JackAlsaDriver.h +++ b/linux/alsa/JackAlsaDriver.h @@ -38,6 +38,12 @@ class JackAlsaDriver : public JackAudioDriver private: + enum DriverMode { + Init, + Runtime, + Shutdown, + }; + jack_driver_t* fDriver; jack_native_thread_t fReservationLoopThread; @@ -51,24 +57,7 @@ class JackAlsaDriver : public JackAudioDriver virtual ~JackAlsaDriver() {} - int Open(jack_nframes_t buffer_size, - jack_nframes_t user_nperiods, - jack_nframes_t samplerate, - bool hw_monitoring, - bool hw_metering, - bool capturing, - bool playing, - DitherAlgorithm dither, - bool soft_mode, - bool monitor, - int inchannels, - int outchannels, - bool shorts_first, - const char* capture_driver_name, - const char* playback_driver_name, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency, - const char* midi_driver_name); + int Open(alsa_driver_info_t info); int Close(); int Attach(); @@ -76,6 +65,7 @@ class JackAlsaDriver : public JackAudioDriver int Start(); int Stop(); + int Reload(); int Read(); int Write(); @@ -88,14 +78,18 @@ class JackAlsaDriver : public JackAudioDriver int SetBufferSize(jack_nframes_t buffer_size); - void ReadInputAux(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread); + void ReadInputAux(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread); void MonitorInputAux(); void ClearOutputAux(); - void WriteOutputAux(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten); + void WriteOutputAux(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten); void SetTimetAux(jack_time_t time); int PortSetDefaultMetadata(jack_port_id_t port_id, const char* pretty_name); + int UpdateDriverTargetState(DriverMode mode); + + int TargetState(DriverMode mode, int connections_count); + // JACK API emulation for the midi driver int is_realtime() const; int create_thread(pthread_t *thread, int prio, int rt, void *(*start_func)(void*), void *arg); diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 977d4c42..e3126dc0 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -56,8 +56,48 @@ char* strcasestr(const char* haystack, const char* needle); /* Delay (in process calls) before jackd will report an xrun */ #define XRUN_REPORT_DELAY 0 -/* Max re-try count for Alsa poll timeout handling */ -#define MAX_RETRY_COUNT 5 + +#ifdef __QNXNTO__ + +char qnx_channel_error_str[9][35] = { + "SND_PCM_STATUS_NOTREADY", + "SND_PCM_STATUS_READY", + "SND_PCM_STATUS_PREPARED", + "SND_PCM_STATUS_RUNNING", + "SND_PCM_STATUS_PAUSED", + "SND_PCM_STATUS_SUSPENDED", + "SND_PCM_STATUS_UNDERRUN", + "SND_PCM_STATUS_OVERRUN", + "SND_PCM_STATUS_UNKNOWN", +}; + +static char* alsa_channel_status_error_str(int code) +{ + switch(code) { + case SND_PCM_STATUS_NOTREADY: + return qnx_channel_error_str[0]; + case SND_PCM_STATUS_READY: + return qnx_channel_error_str[1]; + case SND_PCM_STATUS_PREPARED: + return qnx_channel_error_str[2]; + case SND_PCM_STATUS_RUNNING: + return qnx_channel_error_str[3]; + case SND_PCM_STATUS_PAUSED: + return qnx_channel_error_str[4]; + case SND_PCM_STATUS_SUSPENDED: + return qnx_channel_error_str[5]; + case SND_PCM_STATUS_UNDERRUN: + return qnx_channel_error_str[6]; + case SND_PCM_STATUS_OVERRUN: + return qnx_channel_error_str[7]; + } + return qnx_channel_error_str[8]; +} +#endif + +static int alsa_driver_link (alsa_driver_t *driver); +static int alsa_driver_open_device (alsa_driver_t *driver, alsa_device_t *device, bool is_capture); +static int alsa_driver_get_state (snd_pcm_t *handle, int is_capture); void jack_driver_init (jack_driver_t *driver) @@ -95,35 +135,75 @@ jack_driver_nt_init (jack_driver_nt_t * driver) driver->nt_run_cycle = 0; } +static int +alsa_driver_prepare (snd_pcm_t *handle, int is_capture) +{ + int res = 0; + +#ifndef __QNXNTO__ + res = snd_pcm_prepare (handle); +#else + res = snd_pcm_plugin_prepare(handle, is_capture); +#endif + if (res < 0) { + jack_error("error preparing: %s", snd_strerror(res)); + } + + return res; +} + static void -alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) +alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver, alsa_device_t *device) { - bitset_destroy (&driver->channels_done); - bitset_destroy (&driver->channels_not_done); + bitset_destroy (&device->channels_done); + bitset_destroy (&device->channels_not_done); + + device->capture_channel_offset = 0; + device->playback_channel_offset = 0; - if (driver->playback_addr) { - free (driver->playback_addr); - driver->playback_addr = 0; + /* if we have only 1 device reuse user requested channels, otherwise 0 will attemp to allocate max channels on next setup pass */ + if (driver->devices_count != 1) { + driver->capture_nchannels = 0; + driver->playback_nchannels = 0; + device->capture_nchannels = 0; + device->playback_nchannels = 0; } - if (driver->capture_addr) { - free (driver->capture_addr); - driver->capture_addr = 0; + if (device->playback_addr) { + free (device->playback_addr); + device->playback_addr = 0; } - if (driver->playback_interleave_skip) { - free (driver->playback_interleave_skip); - driver->playback_interleave_skip = NULL; + if (device->capture_addr) { + free (device->capture_addr); + device->capture_addr = 0; } - if (driver->capture_interleave_skip) { - free (driver->capture_interleave_skip); - driver->capture_interleave_skip = NULL; +#ifdef __QNXNTO__ + if (device->playback_areas_ptr) { + free(device->playback_areas_ptr); + device->playback_areas_ptr = NULL; } - if (driver->silent) { - free (driver->silent); - driver->silent = 0; + if (device->capture_areas_ptr) { + free(device->capture_areas_ptr); + device->capture_areas_ptr = NULL; + } +#endif + + if (device->playback_interleave_skip) { + free (device->playback_interleave_skip); + device->playback_interleave_skip = NULL; + } + + if (device->capture_interleave_skip) { + free (device->capture_interleave_skip); + device->capture_interleave_skip = NULL; + } + + if (device->silent) { + free (device->silent); + device->silent = 0; } if (driver->dither_state) { @@ -132,8 +212,10 @@ alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) } } +#ifndef __QNXNTO__ + static int -alsa_driver_check_capabilities (alsa_driver_t *driver) +alsa_driver_check_capabilities (alsa_driver_t *driver, alsa_device_t *device) { return 0; } @@ -141,7 +223,7 @@ alsa_driver_check_capabilities (alsa_driver_t *driver) char* get_control_device_name(const char * device_name); static int -alsa_driver_check_card_type (alsa_driver_t *driver) +alsa_driver_check_card_type (alsa_driver_t *driver, alsa_device_t *device) { int err; snd_ctl_card_info_t *card_info; @@ -149,98 +231,98 @@ alsa_driver_check_card_type (alsa_driver_t *driver) snd_ctl_card_info_alloca (&card_info); - ctl_name = get_control_device_name(driver->alsa_name_playback); + ctl_name = get_control_device_name(device->playback_name); // XXX: I don't know the "right" way to do this. Which to use // driver->alsa_name_playback or driver->alsa_name_capture. - if ((err = snd_ctl_open (&driver->ctl_handle, ctl_name, 0)) < 0) { + if ((err = snd_ctl_open (&device->ctl_handle, ctl_name, 0)) < 0) { jack_error ("control open \"%s\" (%s)", ctl_name, snd_strerror(err)); - } else if ((err = snd_ctl_card_info(driver->ctl_handle, card_info)) < 0) { + } else if ((err = snd_ctl_card_info(device->ctl_handle, card_info)) < 0) { jack_error ("control hardware info \"%s\" (%s)", - driver->alsa_name_playback, snd_strerror (err)); - snd_ctl_close (driver->ctl_handle); + device->playback_name, snd_strerror (err)); + snd_ctl_close (device->ctl_handle); } - driver->alsa_driver = strdup(snd_ctl_card_info_get_driver (card_info)); + device->alsa_driver = strdup(snd_ctl_card_info_get_driver (card_info)); free(ctl_name); - return alsa_driver_check_capabilities (driver); + return alsa_driver_check_capabilities (driver, device); } static int -alsa_driver_hammerfall_hardware (alsa_driver_t *driver) +alsa_driver_hammerfall_hardware (alsa_driver_t *driver, alsa_device_t *device) { - driver->hw = jack_alsa_hammerfall_hw_new (driver); + device->hw = jack_alsa_hammerfall_hw_new (device); return 0; } static int -alsa_driver_hdsp_hardware (alsa_driver_t *driver) +alsa_driver_hdsp_hardware (alsa_driver_t *driver, alsa_device_t *device) { - driver->hw = jack_alsa_hdsp_hw_new (driver); + device->hw = jack_alsa_hdsp_hw_new (device); return 0; } static int -alsa_driver_ice1712_hardware (alsa_driver_t *driver) +alsa_driver_ice1712_hardware (alsa_driver_t *driver, alsa_device_t *device) { - driver->hw = jack_alsa_ice1712_hw_new (driver); + device->hw = jack_alsa_ice1712_hw_new (device); return 0; } // JACK2 /* static int -alsa_driver_usx2y_hardware (alsa_driver_t *driver) +alsa_driver_usx2y_hardware (alsa_driver_t *driver, alsa_device_t *device) { - driver->hw = jack_alsa_usx2y_hw_new (driver); + driver->hw = jack_alsa_usx2y_hw_new (device); return 0; } */ static int -alsa_driver_generic_hardware (alsa_driver_t *driver) +alsa_driver_generic_hardware (alsa_driver_t *driver, alsa_device_t *device) { - driver->hw = jack_alsa_generic_hw_new (driver); + device->hw = jack_alsa_generic_hw_new (device); return 0; } static int -alsa_driver_hw_specific (alsa_driver_t *driver, int hw_monitoring, +alsa_driver_hw_specific (alsa_driver_t *driver, alsa_device_t *device, int hw_monitoring, int hw_metering) { int err; - if (!strcmp(driver->alsa_driver, "RME9652")) { - if ((err = alsa_driver_hammerfall_hardware (driver)) != 0) { + if (!strcmp(device->alsa_driver, "RME9652")) { + if ((err = alsa_driver_hammerfall_hardware (driver, device)) != 0) { return err; } - } else if (!strcmp(driver->alsa_driver, "H-DSP")) { - if ((err = alsa_driver_hdsp_hardware (driver)) !=0) { + } else if (!strcmp(device->alsa_driver, "H-DSP")) { + if ((err = alsa_driver_hdsp_hardware (driver, device)) !=0) { return err; } - } else if (!strcmp(driver->alsa_driver, "ICE1712")) { - if ((err = alsa_driver_ice1712_hardware (driver)) !=0) { + } else if (!strcmp(device->alsa_driver, "ICE1712")) { + if ((err = alsa_driver_ice1712_hardware (driver, device)) !=0) { return err; } } // JACK2 /* - else if (!strcmp(driver->alsa_driver, "USB US-X2Y")) { - if ((err = alsa_driver_usx2y_hardware (driver)) !=0) { + else if (!strcmp(device->alsa_driver, "USB US-X2Y")) { + if ((err = alsa_driver_usx2y_hardware (driver, device)) !=0) { return err; } } */ else { - if ((err = alsa_driver_generic_hardware (driver)) != 0) { + if ((err = alsa_driver_generic_hardware (driver, device)) != 0) { return err; } } - if (driver->hw->capabilities & Cap_HardwareMonitoring) { + if (device->hw->capabilities & Cap_HardwareMonitoring) { driver->has_hw_monitoring = TRUE; /* XXX need to ensure that this is really FALSE or * TRUE or whatever*/ @@ -250,13 +332,13 @@ alsa_driver_hw_specific (alsa_driver_t *driver, int hw_monitoring, driver->hw_monitoring = FALSE; } - if (driver->hw->capabilities & Cap_ClockLockReporting) { + if (device->hw->capabilities & Cap_ClockLockReporting) { driver->has_clock_sync_reporting = TRUE; } else { driver->has_clock_sync_reporting = FALSE; } - if (driver->hw->capabilities & Cap_HardwareMetering) { + if (device->hw->capabilities & Cap_HardwareMetering) { driver->has_hw_metering = TRUE; driver->hw_metering = hw_metering; } else { @@ -266,40 +348,41 @@ alsa_driver_hw_specific (alsa_driver_t *driver, int hw_monitoring, return 0; } +#endif static void -alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) +alsa_driver_setup_io_function_pointers (alsa_driver_t *driver, alsa_device_t *device) { - if (driver->playback_handle) { - if (SND_PCM_FORMAT_FLOAT_LE == driver->playback_sample_format) { - driver->write_via_copy = sample_move_dS_floatLE; + if (device->playback_handle) { + if (SND_PCM_FORMAT_FLOAT_LE == device->playback_sample_format) { + device->write_via_copy = sample_move_dS_floatLE; } else { - switch (driver->playback_sample_bytes) { + switch (device->playback_sample_bytes) { case 2: switch (driver->dither) { case Rectangular: jack_info("Rectangular dithering at 16 bits"); - driver->write_via_copy = driver->quirk_bswap? + device->write_via_copy = device->quirk_bswap? sample_move_dither_rect_d16_sSs: sample_move_dither_rect_d16_sS; break; case Triangular: jack_info("Triangular dithering at 16 bits"); - driver->write_via_copy = driver->quirk_bswap? + device->write_via_copy = device->quirk_bswap? sample_move_dither_tri_d16_sSs: sample_move_dither_tri_d16_sS; break; case Shaped: jack_info("Noise-shaped dithering at 16 bits"); - driver->write_via_copy = driver->quirk_bswap? + device->write_via_copy = device->quirk_bswap? sample_move_dither_shaped_d16_sSs: sample_move_dither_shaped_d16_sS; break; default: - driver->write_via_copy = driver->quirk_bswap? + device->write_via_copy = device->quirk_bswap? sample_move_d16_sSs : sample_move_d16_sS; break; @@ -307,43 +390,43 @@ alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) break; case 3: /* NO DITHER */ - driver->write_via_copy = driver->quirk_bswap? + device->write_via_copy = device->quirk_bswap? sample_move_d24_sSs: sample_move_d24_sS; break; case 4: /* NO DITHER */ - driver->write_via_copy = driver->quirk_bswap? + device->write_via_copy = device->quirk_bswap? sample_move_d32u24_sSs: sample_move_d32u24_sS; break; default: jack_error ("impossible sample width (%d) discovered!", - driver->playback_sample_bytes); + device->playback_sample_bytes); exit (1); } } } - if (driver->capture_handle) { - if (SND_PCM_FORMAT_FLOAT_LE == driver->capture_sample_format) { - driver->read_via_copy = sample_move_floatLE_sSs; + if (device->capture_handle) { + if (SND_PCM_FORMAT_FLOAT_LE == device->capture_sample_format) { + device->read_via_copy = sample_move_floatLE_sSs; } else { - switch (driver->capture_sample_bytes) { + switch (device->capture_sample_bytes) { case 2: - driver->read_via_copy = driver->quirk_bswap? + device->read_via_copy = device->quirk_bswap? sample_move_dS_s16s: sample_move_dS_s16; break; case 3: - driver->read_via_copy = driver->quirk_bswap? + device->read_via_copy = device->quirk_bswap? sample_move_dS_s24s: sample_move_dS_s24; break; case 4: - driver->read_via_copy = driver->quirk_bswap? + device->read_via_copy = device->quirk_bswap? sample_move_dS_s32u24s: sample_move_dS_s32u24; break; @@ -352,15 +435,128 @@ alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) } } +#ifdef __QNXNTO__ + +static int +alsa_driver_allocate_buffer(alsa_driver_t *driver, alsa_device_t *device, int frames, int channels, bool is_capture) +{ + const long ALIGNMENT = 32; + + // TODO driver->playback_sample_bytes + char* const fBuffer = malloc(channels * ((sizeof(alsa_driver_default_format_t)) * frames) + ALIGNMENT); + if(fBuffer) { + /* Provide an 32 byte aligned buffer */ + char* const aligned_buffer = (char*)((uintptr_t)fBuffer & ~(ALIGNMENT-1)) + ALIGNMENT; + + if(is_capture) { + device->capture_areas_ptr = fBuffer; + device->capture_areas = aligned_buffer; + } else { + device->playback_areas_ptr = fBuffer; + device->playback_areas = aligned_buffer; + } + + return 0; + } + + jack_error ("ALSA: could not allocate audio buffer"); + return -1; +} + +static int +alsa_driver_get_setup (alsa_driver_t *driver, alsa_device_t *device, snd_pcm_channel_setup_t *setup, bool is_capture) +{ + int err = 0; + + memset(setup, 0, sizeof(*setup)); + setup->channel = is_capture; + + if(is_capture) { + err = snd_pcm_plugin_setup(device->capture_handle, setup); + } else { + err = snd_pcm_plugin_setup(device->playback_handle, setup); + } + if (err < 0) { + jack_error("couldn't get channel setup for %s, err = %s ", + is_capture ? device->capture_name : device->playback_name, + strerror(err)); + return -1; + } + + return 0; +} + +static int +alsa_driver_configure_stream (alsa_driver_t *driver, alsa_device_t *device, char *device_name, + const char *stream_name, + snd_pcm_t *handle, + unsigned int *nperiodsp, + channel_t *nchns, + unsigned long preferred_sample_bytes, + bool is_capture) +{ + int err = 0; + snd_pcm_channel_info_t ch_info; + snd_pcm_channel_params_t ch_params; + const unsigned long sample_size = is_capture ? device->capture_sample_bytes + : device->playback_sample_bytes; + + memset(&ch_info, 0, sizeof(ch_info)); + /*A pointer to a snd_pcm_channel_info_t structure that snd_pcm_plugin_info() fills in with information about the PCM channel. + * Before calling snd_pcm_plugin_info(), set the info structure's channel member to specify the direction. + * This function sets all the other members.*/ + ch_info.channel = is_capture; + if ((err = snd_pcm_plugin_info(handle, &ch_info)) < 0) { + jack_error("couldn't get channel info for %s, %s, err = (%s)", stream_name, device_name, snd_strerror(err)); + alsa_driver_delete(driver); + return -1; + } + + if (!*nchns) { + *nchns = ch_info.max_voices; + } + + ch_params.format.format = (preferred_sample_bytes == 4) ? SND_PCM_SFMT_S32_LE : SND_PCM_SFMT_S16_LE; + + ch_params.mode = SND_PCM_MODE_BLOCK; + ch_params.start_mode = SND_PCM_START_GO; + ch_params.stop_mode = SND_PCM_STOP_STOP; + ch_params.buf.block.frag_size = driver->frames_per_cycle * *nchns * snd_pcm_format_width(ch_params.format.format) / 8; + + *nperiodsp = driver->user_nperiods; + ch_params.buf.block.frags_min = 1; + /* the maximal available periods (-1 due to one period is always processed + * by DMA and therefore not free) + */ + ch_params.buf.block.frags_max = *nperiodsp - 1; + ch_params.format.interleave = 1; + ch_params.format.rate = driver->frame_rate; + ch_params.format.voices = *nchns; + ch_params.channel = is_capture; + + /*Set the configurable parameters for a PCM channel*/ + if ((err = snd_pcm_plugin_params(handle, &ch_params)) < 0) { + jack_error("snd_pcm_plugin_params failed for %s %s with err = (%s)", snd_strerror(err), stream_name, device_name); + return -1; + } + + /* + * The buffer has to be able to hold a full HW audio buffer + * (periods * period_size) because the silence prefill will fill the + * complete buffer + */ + return alsa_driver_allocate_buffer(driver, device, driver->frames_per_cycle * *nperiodsp, *nchns, is_capture); +} +#else static int -alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, +alsa_driver_configure_stream (alsa_driver_t *driver, alsa_device_t *device, char *device_name, const char *stream_name, snd_pcm_t *handle, snd_pcm_hw_params_t *hw_params, snd_pcm_sw_params_t *sw_params, unsigned int *nperiodsp, channel_t *nchns, - unsigned long sample_width) + unsigned long preferred_sample_bytes) { int err, format; unsigned int frame_rate; @@ -410,13 +606,13 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, } } - format = (sample_width == 4) ? 0 : NUMFORMATS - 1; + format = (preferred_sample_bytes == 4) ? 0 : NUMFORMATS - 1; while (1) { if ((err = snd_pcm_hw_params_set_format ( handle, hw_params, formats[format].format)) < 0) { - if ((sample_width == 4 + if ((preferred_sample_bytes == 4 ? format++ >= NUMFORMATS - 1 : format-- <= 0)) { jack_error ("Sorry. The audio interface \"%s\"" @@ -428,9 +624,9 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, } } else { if (formats[format].swapped) { - driver->quirk_bswap = 1; + device->quirk_bswap = 1; } else { - driver->quirk_bswap = 0; + device->quirk_bswap = 0; } jack_info ("ALSA: final selected sample format for %s: %s", stream_name, formats[format].Name); break; @@ -577,7 +773,7 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, } #endif - if (handle == driver->playback_handle) + if (handle == device->playback_handle) err = snd_pcm_sw_params_set_avail_min ( handle, sw_params, driver->frames_per_cycle @@ -613,14 +809,56 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, return 0; } +#endif + +#ifdef __QNXNTO__ +static int +alsa_driver_check_format (unsigned int format) +{ +#else +static int +alsa_driver_check_format (snd_pcm_format_t format) +{ +#endif + + switch (format) { +#ifndef __QNXNTO__ + case SND_PCM_FORMAT_FLOAT_LE: + case SND_PCM_FORMAT_S24_3LE: + case SND_PCM_FORMAT_S24_3BE: + case SND_PCM_FORMAT_S24_LE: + case SND_PCM_FORMAT_S24_BE: + case SND_PCM_FORMAT_S32_BE: + case SND_PCM_FORMAT_S16_BE: +#endif + case SND_PCM_FORMAT_S16_LE: + case SND_PCM_FORMAT_S32_LE: + break; + default: + jack_error ("format not supported %d", format); + return -1; + } + + return 0; +} static int alsa_driver_set_parameters (alsa_driver_t *driver, + alsa_device_t *device, + int do_capture, + int do_playback, jack_nframes_t frames_per_cycle, jack_nframes_t user_nperiods, jack_nframes_t rate) { +#ifdef __QNXNTO__ + snd_pcm_channel_setup_t c_setup; + snd_pcm_channel_setup_t p_setup; + jack_nframes_t p_periods = 0; + jack_nframes_t c_periods = 0; +#else int dir; +#endif snd_pcm_uframes_t p_period_size = 0; snd_pcm_uframes_t c_period_size = 0; channel_t chn; @@ -632,103 +870,209 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->frames_per_cycle = frames_per_cycle; driver->user_nperiods = user_nperiods; - jack_info ("configuring for %" PRIu32 "Hz, period = %" + jack_info ("configuring C: '%s' P: '%s' %" PRIu32 "Hz, period = %" PRIu32 " frames (%.1f ms), buffer = %" PRIu32 " periods", + device->capture_name != NULL ? device->capture_name : "-", device->playback_name != NULL ? device->playback_name : "-", rate, frames_per_cycle, (((float)frames_per_cycle / (float) rate) * 1000.0f), user_nperiods); - if (driver->capture_handle) { - if (alsa_driver_configure_stream ( - driver, - driver->alsa_name_capture, - "capture", - driver->capture_handle, - driver->capture_hw_params, - driver->capture_sw_params, - &driver->capture_nperiods, - &driver->capture_nchannels, - driver->capture_sample_bytes)) { + if (do_capture) { + if (!device->capture_handle) { + jack_error ("ALSA: pcm capture handle not available"); + return -1; + } +#ifdef __QNXNTO__ + err = alsa_driver_configure_stream ( + driver, + device, + device->capture_name, + "capture", + device->capture_handle, + &driver->capture_nperiods, + &device->capture_nchannels, + driver->preferred_sample_bytes, + SND_PCM_CHANNEL_CAPTURE); + + if (err) { jack_error ("ALSA: cannot configure capture channel"); return -1; } - } - if (driver->playback_handle) { - if (alsa_driver_configure_stream ( - driver, - driver->alsa_name_playback, - "playback", - driver->playback_handle, - driver->playback_hw_params, - driver->playback_sw_params, - &driver->playback_nperiods, - &driver->playback_nchannels, - driver->playback_sample_bytes)) { - jack_error ("ALSA: cannot configure playback channel"); + err = alsa_driver_get_setup(driver, device, &c_setup, SND_PCM_CHANNEL_CAPTURE); + if(err < 0) { + jack_error ("ALSA: get setup failed"); return -1; } - } - - /* check the rate, since that's rather important */ - if (driver->playback_handle) { - snd_pcm_hw_params_get_rate (driver->playback_hw_params, - &pr, &dir); - } + cr = c_setup.format.rate; + c_periods = c_setup.buf.block.frags; + device->capture_sample_format = c_setup.format.format; + device->capture_interleaved = c_setup.format.interleave; + device->capture_sample_bytes = snd_pcm_format_width (c_setup.format.format) / 8; + c_period_size = c_setup.buf.block.frag_size / device->capture_nchannels / device->capture_sample_bytes; +#else + err = alsa_driver_configure_stream ( + driver, + device, + device->capture_name, + "capture", + device->capture_handle, + driver->capture_hw_params, + driver->capture_sw_params, + &driver->capture_nperiods, + &device->capture_nchannels, + driver->preferred_sample_bytes); + + if (err) { + jack_error ("ALSA: cannot configure capture channel"); + return -1; + } - if (driver->capture_handle) { snd_pcm_hw_params_get_rate (driver->capture_hw_params, - &cr, &dir); + &cr, &dir); + + snd_pcm_access_t access; + + err = snd_pcm_hw_params_get_period_size ( + driver->capture_hw_params, &c_period_size, &dir); + err = snd_pcm_hw_params_get_format ( + driver->capture_hw_params, + &(device->capture_sample_format)); + err = snd_pcm_hw_params_get_access (driver->capture_hw_params, + &access); + device->capture_interleaved = + (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) + || (access == SND_PCM_ACCESS_MMAP_COMPLEX); + device->capture_sample_bytes = snd_pcm_format_physical_width (device->capture_sample_format) / 8; +#endif + if (err) { + jack_error ("ALSA: cannot configure capture channel"); + return -1; + } } - if (driver->capture_handle && driver->playback_handle) { - if (cr != pr) { - jack_error ("playback and capture sample rates do " - "not match (%d vs. %d)", pr, cr); + if (do_playback) { + if (!device->playback_handle) { + jack_error ("ALSA: pcm playback handle not available"); + return -1; + } +#ifdef __QNXNTO__ + err = alsa_driver_configure_stream ( + driver, + device, + device->playback_name, + "playback", + device->playback_handle, + &driver->playback_nperiods, + &device->playback_nchannels, + driver->preferred_sample_bytes, + SND_PCM_CHANNEL_PLAYBACK); + + if (err) { + jack_error ("ALSA: cannot configure playback channel"); + return -1; } - /* only change if *both* capture and playback rates - * don't match requested certain hardware actually - * still works properly in full-duplex with slightly - * different rate values between adc and dac - */ - if (cr != driver->frame_rate && pr != driver->frame_rate) { - jack_error ("sample rate in use (%d Hz) does not " - "match requested rate (%d Hz)", - cr, driver->frame_rate); - driver->frame_rate = cr; + err = alsa_driver_get_setup(driver, device, &p_setup, SND_PCM_CHANNEL_PLAYBACK); + if(err < 0) { + jack_error ("ALSA: get setup failed"); + return -1; } - } - else if (driver->capture_handle && cr != driver->frame_rate) { - jack_error ("capture sample rate in use (%d Hz) does not " - "match requested rate (%d Hz)", - cr, driver->frame_rate); - driver->frame_rate = cr; - } - else if (driver->playback_handle && pr != driver->frame_rate) { - jack_error ("playback sample rate in use (%d Hz) does not " - "match requested rate (%d Hz)", - pr, driver->frame_rate); - driver->frame_rate = pr; - } + pr = p_setup.format.rate; + p_periods = p_setup.buf.block.frags; + device->playback_sample_format = p_setup.format.format; + device->playback_interleaved = p_setup.format.interleave; + device->playback_sample_bytes = snd_pcm_format_width (p_setup.format.format) / 8; + p_period_size = p_setup.buf.block.frag_size / device->playback_nchannels / device->playback_sample_bytes; +#else + err = alsa_driver_configure_stream ( + driver, + device, + device->playback_name, + "playback", + device->playback_handle, + driver->playback_hw_params, + driver->playback_sw_params, + &driver->playback_nperiods, + &device->playback_nchannels, + driver->preferred_sample_bytes); + + if (err) { + jack_error ("ALSA: cannot configure playback channel"); + return -1; + } - /* check the fragment size, since that's non-negotiable */ + /* check the rate, since that's rather important */ + snd_pcm_hw_params_get_rate (driver->playback_hw_params, + &pr, &dir); - if (driver->playback_handle) { - snd_pcm_access_t access; + snd_pcm_access_t access; - err = snd_pcm_hw_params_get_period_size ( - driver->playback_hw_params, &p_period_size, &dir); - err = snd_pcm_hw_params_get_format ( - driver->playback_hw_params, - &(driver->playback_sample_format)); - err = snd_pcm_hw_params_get_access (driver->playback_hw_params, + err = snd_pcm_hw_params_get_period_size ( + driver->playback_hw_params, &p_period_size, &dir); + err = snd_pcm_hw_params_get_format ( + driver->playback_hw_params, + &(device->playback_sample_format)); + err = snd_pcm_hw_params_get_access (driver->playback_hw_params, &access); - driver->playback_interleaved = + device->playback_interleaved = (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) || (access == SND_PCM_ACCESS_MMAP_COMPLEX); + device->playback_sample_bytes = snd_pcm_format_physical_width (device->playback_sample_format) / 8; +#endif + } + + /* original checks done for single device mode */ + if (driver->devices_count == 1) { + if (do_capture && do_playback) { + if (cr != pr) { + jack_error ("playback and capture sample rates do " + "not match (%d vs. %d)", pr, cr); + } + /* only change if *both* capture and playback rates + * don't match requested certain hardware actually + * still works properly in full-duplex with slightly + * different rate values between adc and dac + */ + if (cr != driver->frame_rate && pr != driver->frame_rate) { + jack_error ("sample rate in use (%d Hz) does not " + "match requested rate (%d Hz)", + cr, driver->frame_rate); + driver->frame_rate = cr; + } + } + else if (do_capture && cr != driver->frame_rate) { + jack_error ("capture sample rate in use (%d Hz) does not " + "match requested rate (%d Hz)", + cr, driver->frame_rate); + driver->frame_rate = cr; + } + else if (do_playback && pr != driver->frame_rate) { + jack_error ("playback sample rate in use (%d Hz) does not " + "match requested rate (%d Hz)", + pr, driver->frame_rate); + driver->frame_rate = pr; + } + } else { + if (do_capture && cr != driver->frame_rate) { + jack_error ("capture sample rate in use (%d Hz) does not " + "match requested rate (%d Hz)", + cr, driver->frame_rate); + return -1; + } + if (do_playback && pr != driver->frame_rate) { + jack_error ("playback sample rate in use (%d Hz) does not " + "match requested rate (%d Hz)", + pr, driver->frame_rate); + return -1; + } + } + + /* check the fragment size, since that's non-negotiable */ + + if (do_playback) { if (p_period_size != driver->frames_per_cycle) { jack_error ("alsa_pcm: requested an interrupt every %" PRIu32 @@ -736,22 +1080,22 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->frames_per_cycle, p_period_size); return -1; } +#ifdef __QNXNTO__ + if (p_periods != driver->user_nperiods) { + jack_error ("alsa_pcm: requested %" + PRIu32 + " periods but got %" + PRIu32 + " periods for playback", + driver->user_nperiods, p_periods); + return -1; + } +#endif } - if (driver->capture_handle) { - snd_pcm_access_t access; - - err = snd_pcm_hw_params_get_period_size ( - driver->capture_hw_params, &c_period_size, &dir); - err = snd_pcm_hw_params_get_format ( - driver->capture_hw_params, - &(driver->capture_sample_format)); - err = snd_pcm_hw_params_get_access (driver->capture_hw_params, - &access); - driver->capture_interleaved = - (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) - || (access == SND_PCM_ACCESS_MMAP_COMPLEX); - + if (do_capture) { +#ifndef __QNXNTO__ +#endif if (c_period_size != driver->frames_per_cycle) { jack_error ("alsa_pcm: requested an interrupt every %" PRIu32 @@ -759,144 +1103,139 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->frames_per_cycle, c_period_size); return -1; } +#ifdef __QNXNTO__ + /* capture buffers can be configured bigger but it should fail + * if they are smaller as expected + */ + if (c_periods < driver->user_nperiods) { + jack_error ("alsa_pcm: requested %" + PRIu32 + " periods but got %" + PRIu32 + " periods for capture", + driver->user_nperiods, c_periods); + return -1; + } +#endif } - driver->playback_sample_bytes = - snd_pcm_format_physical_width (driver->playback_sample_format) - / 8; - driver->capture_sample_bytes = - snd_pcm_format_physical_width (driver->capture_sample_format) - / 8; - - if (driver->playback_handle) { - switch (driver->playback_sample_format) { - case SND_PCM_FORMAT_FLOAT_LE: - case SND_PCM_FORMAT_S32_LE: - case SND_PCM_FORMAT_S24_3LE: - case SND_PCM_FORMAT_S24_3BE: - case SND_PCM_FORMAT_S24_LE: - case SND_PCM_FORMAT_S24_BE: - case SND_PCM_FORMAT_S16_LE: - case SND_PCM_FORMAT_S32_BE: - case SND_PCM_FORMAT_S16_BE: - break; - - default: + if (do_playback) { + err = alsa_driver_check_format(device->playback_sample_format); + if(err < 0) { jack_error ("programming error: unhandled format " "type for playback"); - exit (1); + return -1; } } - if (driver->capture_handle) { - switch (driver->capture_sample_format) { - case SND_PCM_FORMAT_FLOAT_LE: - case SND_PCM_FORMAT_S32_LE: - case SND_PCM_FORMAT_S24_3LE: - case SND_PCM_FORMAT_S24_3BE: - case SND_PCM_FORMAT_S24_LE: - case SND_PCM_FORMAT_S24_BE: - case SND_PCM_FORMAT_S16_LE: - case SND_PCM_FORMAT_S32_BE: - case SND_PCM_FORMAT_S16_BE: - break; - - default: + if (do_capture) { + err = alsa_driver_check_format(device->capture_sample_format); + if(err < 0) { jack_error ("programming error: unhandled format " "type for capture"); - exit (1); + return -1; } } - if (driver->playback_interleaved) { + if (device->playback_interleaved && do_playback) { +#ifndef __QNXNTO__ const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames; - if (snd_pcm_mmap_begin(driver->playback_handle, + if (snd_pcm_mmap_begin(device->playback_handle, &my_areas, &offset, &frames) < 0) { jack_error ("ALSA: %s: mmap areas info error", - driver->alsa_name_playback); + device->playback_name); return -1; } - driver->interleave_unit = + + // TODO does not work for capture only + device->interleave_unit = snd_pcm_format_physical_width ( - driver->playback_sample_format) / 8; - } else { - driver->interleave_unit = 0; /* NOT USED */ + device->playback_sample_format) / 8; +#else + device->interleave_unit = snd_pcm_format_width( + device->playback_sample_format) / 8; +#endif + } else if (do_playback) { + device->interleave_unit = 0; /* NOT USED */ } - if (driver->capture_interleaved) { +#ifndef __QNXNTO__ + if (device->capture_interleaved && do_capture) { const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames; - if (snd_pcm_mmap_begin(driver->capture_handle, + if (snd_pcm_mmap_begin(device->capture_handle, &my_areas, &offset, &frames) < 0) { jack_error ("ALSA: %s: mmap areas info error", - driver->alsa_name_capture); + device->capture_name); return -1; } } +#endif - if (driver->playback_nchannels > driver->capture_nchannels) { - driver->max_nchannels = driver->playback_nchannels; - driver->user_nchannels = driver->capture_nchannels; - } else { - driver->max_nchannels = driver->capture_nchannels; - driver->user_nchannels = driver->playback_nchannels; - } + alsa_driver_setup_io_function_pointers (driver, device); - alsa_driver_setup_io_function_pointers (driver); + /* do only on first start */ + if (device->max_nchannels == 0) { - /* Allocate and initialize structures that rely on the - channels counts. + /* Allocate and initialize structures that rely on the + channels counts. - Set up the bit pattern that is used to record which - channels require action on every cycle. any bits that are - not set after the engine's process() call indicate channels - that potentially need to be silenced. - */ + Set up the bit pattern that is used to record which + channels require action on every cycle. any bits that are + not set after the engine's process() call indicate channels + that potentially need to be silenced. + */ - bitset_create (&driver->channels_done, driver->max_nchannels); - bitset_create (&driver->channels_not_done, driver->max_nchannels); + device->max_nchannels = device->playback_nchannels > device->capture_nchannels ? + device->playback_nchannels : device->capture_nchannels; + + /* device local channel offset to offsets in driver, used by Jack2 */ + device->capture_channel_offset = driver->capture_nchannels; + device->playback_channel_offset = driver->playback_nchannels; + + driver->capture_nchannels += device->capture_nchannels; + driver->playback_nchannels += device->playback_nchannels; + + bitset_create (&device->channels_done, device->max_nchannels); + bitset_create (&device->channels_not_done, device->max_nchannels); + + if (device->playback_name) { + device->playback_addr = (char **) + malloc (sizeof (char *) * device->playback_nchannels); + memset (device->playback_addr, 0, + sizeof (char *) * device->playback_nchannels); + device->playback_interleave_skip = (unsigned long *) + malloc (sizeof (unsigned long *) * device->playback_nchannels); + memset (device->playback_interleave_skip, 0, + sizeof (unsigned long *) * device->playback_nchannels); + device->silent = (unsigned long *) + malloc (sizeof (unsigned long) + * device->playback_nchannels); + + for (chn = 0; chn < device->playback_nchannels; chn++) { + device->silent[chn] = 0; + } - if (driver->playback_handle) { - driver->playback_addr = (char **) - malloc (sizeof (char *) * driver->playback_nchannels); - memset (driver->playback_addr, 0, - sizeof (char *) * driver->playback_nchannels); - driver->playback_interleave_skip = (unsigned long *) - malloc (sizeof (unsigned long *) * driver->playback_nchannels); - memset (driver->playback_interleave_skip, 0, - sizeof (unsigned long *) * driver->playback_nchannels); - driver->silent = (unsigned long *) - malloc (sizeof (unsigned long) - * driver->playback_nchannels); + for (chn = 0; chn < device->playback_nchannels; chn++) { + bitset_add (device->channels_done, chn); + } - for (chn = 0; chn < driver->playback_nchannels; chn++) { - driver->silent[chn] = 0; + driver->dither_state = (dither_state_t *) calloc (device->playback_nchannels, sizeof (dither_state_t)); } - for (chn = 0; chn < driver->playback_nchannels; chn++) { - bitset_add (driver->channels_done, chn); + if (device->capture_name) { + device->capture_addr = (char **) + malloc (sizeof (char *) * device->capture_nchannels); + memset (device->capture_addr, 0, + sizeof (char *) * device->capture_nchannels); + device->capture_interleave_skip = (unsigned long *) + malloc (sizeof (unsigned long *) * device->capture_nchannels); + memset (device->capture_interleave_skip, 0, + sizeof (unsigned long *) * device->capture_nchannels); } - - driver->dither_state = (dither_state_t *) - calloc ( driver->playback_nchannels, - sizeof (dither_state_t)); - } - - if (driver->capture_handle) { - driver->capture_addr = (char **) - malloc (sizeof (char *) * driver->capture_nchannels); - memset (driver->capture_addr, 0, - sizeof (char *) * driver->capture_nchannels); - driver->capture_interleave_skip = (unsigned long *) - malloc (sizeof (unsigned long *) * driver->capture_nchannels); - memset (driver->capture_interleave_skip, 0, - sizeof (unsigned long *) * driver->capture_nchannels); } - driver->clock_sync_data = (ClockSyncStatus *) - malloc (sizeof (ClockSyncStatus) * driver->max_nchannels); - driver->period_usecs = (jack_time_t) floor ((((float) driver->frames_per_cycle) / driver->frame_rate) * 1000000.0f); @@ -925,147 +1264,421 @@ alsa_driver_reset_parameters (alsa_driver_t *driver, jack_nframes_t user_nperiods, jack_nframes_t rate) { + int err = 0; + + jack_info ("reset parameters"); + /* XXX unregister old ports ? */ - alsa_driver_release_channel_dependent_memory (driver); - return alsa_driver_set_parameters (driver, - frames_per_cycle, - user_nperiods, rate); + for (int i = 0; i < driver->devices_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + alsa_driver_release_channel_dependent_memory (driver, device); + if ((err = alsa_driver_set_parameters (driver, device, 1, 1, frames_per_cycle, user_nperiods, rate)) != 0) { + return err; + } + } + + return err; +} + +#ifdef __QNXNTO__ +static int +snd_pcm_poll_descriptors_count(snd_pcm_t *pcm) +{ + return 1; +} + +static int +snd_pcm_poll_descriptors_revents(snd_pcm_t *pcm, struct pollfd *pfds, + unsigned int nfds, unsigned short *revents) +{ + *revents = pfds->revents; + + return 0; } +#endif static int alsa_driver_get_channel_addresses (alsa_driver_t *driver, + alsa_device_t *device, snd_pcm_uframes_t *capture_avail, snd_pcm_uframes_t *playback_avail, snd_pcm_uframes_t *capture_offset, snd_pcm_uframes_t *playback_offset) { - int err; channel_t chn; if (capture_avail) { +#ifndef __QNXNTO__ + int err; if ((err = snd_pcm_mmap_begin ( - driver->capture_handle, &driver->capture_areas, + device->capture_handle, &device->capture_areas, (snd_pcm_uframes_t *) capture_offset, (snd_pcm_uframes_t *) capture_avail)) < 0) { jack_error ("ALSA: %s: mmap areas info error", - driver->alsa_name_capture); + device->capture_name); return -1; } - for (chn = 0; chn < driver->capture_nchannels; chn++) { + for (chn = 0; chn < device->capture_nchannels; chn++) { const snd_pcm_channel_area_t *a = - &driver->capture_areas[chn]; - driver->capture_addr[chn] = (char *) a->addr + &device->capture_areas[chn]; + device->capture_addr[chn] = (char *) a->addr + ((a->first + a->step * *capture_offset) / 8); - driver->capture_interleave_skip[chn] = (unsigned long ) (a->step / 8); + device->capture_interleave_skip[chn] = (unsigned long ) (a->step / 8); + } +#else + for (chn = 0; chn < device->capture_nchannels; chn++) { + char* const a = device->capture_areas; + if (device->capture_interleaved) { + device->capture_addr[chn] = &a[chn * device->capture_sample_bytes]; + device->capture_interleave_skip[chn] = device->capture_nchannels * + device->capture_sample_bytes; + } else { + device->capture_addr[chn] = &a[chn * + device->capture_sample_bytes * driver->frames_per_cycle]; + device->capture_interleave_skip[chn] = device->capture_sample_bytes; + } } +#endif } if (playback_avail) { +#ifndef __QNXNTO__ + int err; if ((err = snd_pcm_mmap_begin ( - driver->playback_handle, &driver->playback_areas, + device->playback_handle, &device->playback_areas, (snd_pcm_uframes_t *) playback_offset, (snd_pcm_uframes_t *) playback_avail)) < 0) { jack_error ("ALSA: %s: mmap areas info error ", - driver->alsa_name_playback); + device->playback_name); return -1; } - for (chn = 0; chn < driver->playback_nchannels; chn++) { + for (chn = 0; chn < device->playback_nchannels; chn++) { const snd_pcm_channel_area_t *a = - &driver->playback_areas[chn]; - driver->playback_addr[chn] = (char *) a->addr + &device->playback_areas[chn]; + device->playback_addr[chn] = (char *) a->addr + ((a->first + a->step * *playback_offset) / 8); - driver->playback_interleave_skip[chn] = (unsigned long ) (a->step / 8); + device->playback_interleave_skip[chn] = (unsigned long ) (a->step / 8); + } +#else + for (chn = 0; chn < device->playback_nchannels; chn++) { + char* const a = device->playback_areas; + if (device->playback_interleaved) { + device->playback_addr[chn] = &a[chn * device->playback_sample_bytes]; + device->playback_interleave_skip[chn] = device->playback_nchannels * + device->playback_sample_bytes; + } else { + device->playback_addr[chn] = &a[chn * + device->playback_sample_bytes * driver->frames_per_cycle]; + device->playback_interleave_skip[chn] = device->playback_sample_bytes; + } } +#endif } return 0; } +#ifdef __QNXNTO__ +static int +alsa_driver_stream_start(snd_pcm_t *pcm, bool is_capture) +{ + return snd_pcm_channel_go(pcm, is_capture); +} +#else +static int +alsa_driver_stream_start(snd_pcm_t *pcm, bool is_capture) +{ + return snd_pcm_start(pcm); +} +#endif + int -alsa_driver_start (alsa_driver_t *driver) +alsa_driver_open (alsa_driver_t *driver) { - int err; - snd_pcm_uframes_t poffset, pavail; - channel_t chn; + int err = 0; driver->poll_last = 0; driver->poll_next = 0; - if (driver->playback_handle) { - if ((err = snd_pcm_prepare (driver->playback_handle)) < 0) { - jack_error ("ALSA: prepare error for playback on " - "\"%s\" (%s)", driver->alsa_name_playback, - snd_strerror(err)); - return -1; - } - } + for (int i = 0; i < driver->devices_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + int do_capture = 0, do_playback = 0; - if ((driver->capture_handle && driver->capture_and_playback_not_synced) - || !driver->playback_handle) { - if ((err = snd_pcm_prepare (driver->capture_handle)) < 0) { - jack_error ("ALSA: prepare error for capture on \"%s\"" - " (%s)", driver->alsa_name_capture, - snd_strerror(err)); - return -1; + if (!device->capture_handle && (i < driver->devices_c_count) && (device->capture_target_state != SND_PCM_STATE_NOTREADY)) { + jack_info("open C: %s", device->capture_name); + + err = alsa_driver_open_device (driver, &driver->devices[i], SND_PCM_STREAM_CAPTURE); + if (err < 0) { + jack_error ("\n\nATTENTION: Opening of the capture device \"%s\" failed.", + driver->devices[i].capture_name); + return -1; + } + + do_capture = 1; } - } - if (driver->hw_monitoring) { - if (driver->input_monitor_mask || driver->all_monitor_in) { - if (driver->all_monitor_in) { - driver->hw->set_input_monitor_mask (driver->hw, ~0U); - } else { - driver->hw->set_input_monitor_mask ( - driver->hw, driver->input_monitor_mask); + if (!device->playback_handle && (i < driver->devices_p_count) && (device->playback_target_state != SND_PCM_STATE_NOTREADY)) { + jack_info("open P: %s", device->playback_name); + + err = alsa_driver_open_device (driver, &driver->devices[i], SND_PCM_STREAM_PLAYBACK); + if (err < 0) { + jack_error ("\n\nATTENTION: Opening of the playback device \"%s\" failed.", + driver->devices[i].playback_name); + return -1; } - } else { - driver->hw->set_input_monitor_mask (driver->hw, - driver->input_monitor_mask); + + do_playback = 1; } - } - if (driver->playback_handle) { - driver->playback_nfds = - snd_pcm_poll_descriptors_count (driver->playback_handle); - } else { - driver->playback_nfds = 0; + if (alsa_driver_set_parameters (driver, device, do_capture, do_playback, driver->frames_per_cycle, driver->user_nperiods, driver->frame_rate)) { + jack_error ("ALSA: failed to set parameters"); + return -1; + } } - if (driver->capture_handle) { - driver->capture_nfds = - snd_pcm_poll_descriptors_count (driver->capture_handle); + if (!(driver->features & ALSA_DRIVER_FEAT_UNLINKED_DEVS)) { + jack_info ("alsa driver linking enabled"); + alsa_driver_link(driver); } else { - driver->capture_nfds = 0; + jack_info ("alsa driver linking disabled"); } - if (driver->pfd) { - free (driver->pfd); - } + return 0; +} - driver->pfd = (struct pollfd *) - malloc (sizeof (struct pollfd) * - (driver->playback_nfds + driver->capture_nfds + 2)); +static int +alsa_driver_link (alsa_driver_t *driver) +{ + snd_pcm_t *group_handle = NULL; - if (driver->midi && !driver->xrun_recovery) - (driver->midi->start)(driver->midi); + for (int i = 0; i < driver->devices_c_count; ++i) { + alsa_device_t *device = &driver->devices[i]; - if (driver->playback_handle) { + if (!device->capture_handle) { + continue; + } + + jack_info("link C: %s", device->capture_name); + + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + jack_info("link skipped, device unused"); + continue; + } + + if (group_handle == NULL) { + jack_info("link, device is group master"); + group_handle = device->capture_handle; + device->capture_linked = 1; + continue; + } + + if (device->capture_linked) { + jack_info("link skipped, already done"); + continue; + } + + if (group_handle == device->capture_handle) { + jack_info("link skipped, master already done"); + device->capture_linked = 1; + continue; + } + + if (snd_pcm_link (group_handle, device->capture_handle) != 0) { + jack_error ("failed to add device to link group C: '%s'", device->capture_name); + continue; + } + device->capture_linked = 1; + } + + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + if (!device->playback_handle) { + continue; + } + + jack_info("link P: %s", device->playback_name); + + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + jack_info("link skipped, device unused"); + continue; + } + + if (group_handle == NULL) { + jack_info("link, device is group master"); + group_handle = device->playback_handle; + device->playback_linked = 1; + continue; + } + + if (device->playback_linked) { + jack_info("link skipped, already done"); + continue; + } + + if (group_handle == device->playback_handle) { + jack_info("link skipped, master already done"); + device->playback_linked = 1; + continue; + } + + if (snd_pcm_link (group_handle, device->playback_handle) != 0) { + jack_error ("failed to add device to link group P: '%s'", device->playback_name); + continue; + } + device->playback_linked = 1; + } + + return 0; +} + +int +alsa_driver_start (alsa_driver_t *driver) +{ + int err; + snd_pcm_uframes_t poffset, pavail; + channel_t chn; + + driver->capture_nfds = 0; + driver->playback_nfds = 0; + + int group_done = 0; + + for (int i = 0; i < driver->devices_c_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + if (!device->capture_handle) { + continue; + } + + jack_info("prepare C: %s", device->capture_name); + + if (device->capture_target_state == SND_PCM_STATE_NOTREADY) { + jack_info("prepare skipped, device unused"); + continue; + } + + if (device->capture_target_state == SND_PCM_STATE_RUNNING) { + driver->capture_nfds += snd_pcm_poll_descriptors_count (device->capture_handle); + } + + if (group_done && device->capture_linked) { + jack_info("prepare skipped, already done by link group"); + continue; + } + + if (alsa_driver_get_state(device->capture_handle, 1) == SND_PCM_STATE_PREPARED) { + jack_info("prepare skipped, already prepared"); + continue; + } + + if (device->capture_linked) { + group_done = 1; + } + + if ((err = alsa_driver_prepare (device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { + jack_error ("ALSA: failed to prepare device '%s' (%s)", device->capture_name, snd_strerror(err)); + return -1; + } + } + + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + if (!device->playback_handle) { + continue; + } + + jack_info("prepare P: %s", device->playback_name); + + if (device->playback_target_state == SND_PCM_STATE_NOTREADY) { + jack_info("prepare skipped, device unused"); + continue; + } + + if (device->playback_target_state == SND_PCM_STATE_RUNNING) { + driver->playback_nfds += snd_pcm_poll_descriptors_count (device->playback_handle); + } + + if (group_done && device->playback_linked) { + jack_info("prepare skipped, already done by link group"); + continue; + } + + if (alsa_driver_get_state(device->playback_handle, 0) == SND_PCM_STATE_PREPARED) { + jack_info("prepare skipped, already prepared"); + continue; + } + + if (device->playback_linked) { + group_done = 1; + } + + if ((err = alsa_driver_prepare (device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { + jack_error ("ALSA: failed to prepare device '%s' (%s)", device->playback_name, snd_strerror(err)); + return -1; + } + } + + // TODO amiartus +// if (driver->hw_monitoring) { +// if (driver->input_monitor_mask || driver->all_monitor_in) { +// if (driver->all_monitor_in) { +// driver->hw->set_input_monitor_mask (driver->hw, ~0U); +// } else { +// driver->hw->set_input_monitor_mask ( +// driver->hw, driver->input_monitor_mask); +// } +// } else { +// driver->hw->set_input_monitor_mask (driver->hw, +// driver->input_monitor_mask); +// } +// } + + if (driver->pfd) { + free (driver->pfd); + } + + driver->pfd = (struct pollfd *) + malloc (sizeof (struct pollfd) * + (driver->playback_nfds + driver->capture_nfds + 2)); + + if (driver->midi && !driver->xrun_recovery) + (driver->midi->start)(driver->midi); + + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + if (!device->playback_handle) { + continue; + } + + jack_info("silence P: %s", device->playback_name); + + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + jack_info("silence skipped, device unused"); + continue; + } + + const jack_nframes_t silence_frames = driver->frames_per_cycle * + driver->playback_nperiods; /* fill playback buffer with zeroes, and mark all fragments as having data. */ - pavail = snd_pcm_avail_update (driver->playback_handle); - - if (pavail != - driver->frames_per_cycle * driver->playback_nperiods) { +#ifndef __QNXNTO__ + pavail = snd_pcm_avail_update (device->playback_handle); + if (pavail != silence_frames) { jack_error ("ALSA: full buffer not available at start"); return -1; } +#endif - if (alsa_driver_get_channel_addresses (driver, + if (alsa_driver_get_channel_addresses (driver, device, 0, &pavail, 0, &poffset)) { + jack_error("silence failed, get channel addresses"); return -1; } @@ -1078,29 +1691,83 @@ alsa_driver_start (alsa_driver_t *driver) buffer. */ - for (chn = 0; chn < driver->playback_nchannels; chn++) { + for (chn = 0; chn < device->playback_nchannels; chn++) { alsa_driver_silence_on_channel ( - driver, chn, - driver->user_nperiods - * driver->frames_per_cycle); + driver, device, chn, silence_frames); + } + +#ifdef __QNXNTO__ + const size_t bytes = silence_frames * device->playback_nchannels * + device->playback_sample_bytes; + if ((err = snd_pcm_plugin_write(device->playback_handle, + device->playback_areas, bytes)) < bytes) { + jack_error ("ALSA: could not complete silence %s of %" + PRIu32 " frames: %u, error = %d", device->playback_name, silence_frames, err, errno); + return -1; } +#else + snd_pcm_mmap_commit (device->playback_handle, poffset, silence_frames); +#endif + } + + group_done = 0; - snd_pcm_mmap_commit (driver->playback_handle, poffset, - driver->user_nperiods - * driver->frames_per_cycle); + for (int i = 0; i < driver->devices_c_count; ++i) { + alsa_device_t *device = &driver->devices[i]; - if ((err = snd_pcm_start (driver->playback_handle)) < 0) { - jack_error ("ALSA: could not start playback (%s)", - snd_strerror (err)); + if (!device->capture_handle) { + continue; + } + + jack_info("start C: %s", device->capture_name); + + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + jack_info("start skipped, device unused"); + continue; + } + + if (group_done && device->capture_linked) { + jack_info("start skipped, already done by link group"); + continue; + } + + if (device->capture_linked) { + group_done = 1; + } + + if ((err = alsa_driver_stream_start (device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { + jack_error ("ALSA: failed to start device C: '%s' (%s)", device->capture_name, + snd_strerror(err)); return -1; } } - if ((driver->capture_handle && driver->capture_and_playback_not_synced) - || !driver->playback_handle) { - if ((err = snd_pcm_start (driver->capture_handle)) < 0) { - jack_error ("ALSA: could not start capture (%s)", - snd_strerror (err)); + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + if (!device->playback_handle) { + continue; + } + + jack_info("start P: %s", device->playback_name); + + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + jack_info("start skipped, device unused"); + continue; + } + + if (group_done && device->playback_linked) { + jack_info("start skipped, already done by link group"); + continue; + } + + if (device->playback_linked) { + group_done = 1; + } + + if ((err = alsa_driver_stream_start (device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { + jack_error ("ALSA: failed to start device P: '%s' (%s)", device->playback_name, + snd_strerror(err)); return -1; } } @@ -1137,32 +1804,134 @@ alsa_driver_stop (alsa_driver_t *driver) // JACK2 ClearOutput(); - if (driver->playback_handle) { - if ((err = snd_pcm_drop (driver->playback_handle)) < 0) { - jack_error ("ALSA: channel flush for playback " - "failed (%s)", snd_strerror (err)); + int group_done = 0; + + for (int i = 0; i < driver->devices_c_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (!device->capture_handle) { + continue; + } + + jack_info("stop C: %s", device->capture_name); + + if (group_done && device->capture_linked) { + jack_info("stop skipped, already done by link group"); + continue; + } + + if (alsa_driver_get_state(device->capture_handle, 1) != SND_PCM_STATE_RUNNING) { + jack_info("stop skipped, device not running"); + continue; + } + +#ifdef __QNXNTO__ + /* In case of capture: Flush discards the frames */ + err = snd_pcm_plugin_flush(device->capture_handle, SND_PCM_CHANNEL_CAPTURE); +#else + if (device->capture_linked) { + group_done = 1; + } + + err = snd_pcm_drop (device->capture_handle); +#endif + if (err < 0) { + jack_error ("ALSA: failed to flush device (%s)", snd_strerror (err)); return -1; } } - if (!driver->playback_handle - || driver->capture_and_playback_not_synced) { - if (driver->capture_handle) { - if ((err = snd_pcm_drop (driver->capture_handle)) < 0) { - jack_error ("ALSA: channel flush for " - "capture failed (%s)", - snd_strerror (err)); - return -1; - } + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (!device->playback_handle) { + continue; + } + + jack_info("stop P: %s", device->playback_name); + + if (group_done && device->playback_linked) { + jack_info("stop skipped, already done by link group"); + continue; + } + + if (alsa_driver_get_state(device->playback_handle, 0) != SND_PCM_STATE_RUNNING) { + jack_info("stop skipped, device not running"); + continue; + } + +#ifdef __QNXNTO__ + /* In case of playback: Drain discards the frames */ + err = snd_pcm_plugin_playback_drain(device->playback_handle); +#else + if (device->playback_linked) { + group_done = 1; + } + + err = snd_pcm_drop (device->playback_handle); +#endif + if (err < 0) { + jack_error ("ALSA: failed to flush device (%s)", snd_strerror (err)); + return -1; } } - if (driver->hw_monitoring) { - driver->hw->set_input_monitor_mask (driver->hw, 0); +// TODO: amiartus +// if (driver->hw_monitoring) { +// driver->hw->set_input_monitor_mask (driver->hw, 0); +// } + +// if (driver->midi && !driver->xrun_recovery) +// (driver->midi->stop)(driver->midi); + + return 0; +} + +int +alsa_driver_close (alsa_driver_t *driver) +{ + for (int i = 0; i < driver->devices_c_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (!device->capture_handle) { + continue; + } + + if (device->capture_linked) { + snd_pcm_unlink(device->capture_handle); + device->capture_linked = 0; + } + + if (device->capture_target_state != SND_PCM_STATE_NOTREADY) { + continue; + } + + device->capture_sample_bytes = 0; + device->capture_sample_format = SND_PCM_FORMAT_UNKNOWN; + + snd_pcm_close(device->capture_handle); + device->capture_handle = NULL; } - if (driver->midi && !driver->xrun_recovery) - (driver->midi->stop)(driver->midi); + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (!device->playback_handle) { + continue; + } + + + if (device->playback_linked) { + snd_pcm_unlink(device->playback_handle); + device->playback_linked = 0; + } + + if (device->playback_target_state != SND_PCM_STATE_NOTREADY) { + continue; + } + + device->playback_sample_bytes = 0; + device->playback_sample_format = SND_PCM_FORMAT_UNKNOWN; + + snd_pcm_close(device->playback_handle); + device->playback_handle = NULL; + } return 0; } @@ -1178,7 +1947,7 @@ alsa_driver_restart (alsa_driver_t *driver) if ((res = driver->nt_stop((struct _jack_driver_nt *) driver))==0) res = driver->nt_start((struct _jack_driver_nt *) driver); */ - res = Restart(); + res = Restart(); driver->xrun_recovery = 0; if (res && driver->midi) @@ -1188,163 +1957,264 @@ alsa_driver_restart (alsa_driver_t *driver) } static int -alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs) +alsa_driver_get_state (snd_pcm_t *handle, int is_capture) { - snd_pcm_status_t *status; +#ifdef __QNXNTO__ int res; + snd_pcm_channel_status_t status; - snd_pcm_status_alloca(&status); - - if (driver->capture_handle) { - if ((res = snd_pcm_status(driver->capture_handle, status)) - < 0) { - jack_error("status error: %s", snd_strerror(res)); - } - } else { - if ((res = snd_pcm_status(driver->playback_handle, status)) - < 0) { - jack_error("status error: %s", snd_strerror(res)); - } + memset (&status, 0, sizeof (status)); + status.channel = is_capture; + res = snd_pcm_plugin_status(handle, &status); + if (res < 0) { + jack_error("status error: %s", snd_strerror(res)); + return -1; } + return status.status; +#else + return snd_pcm_state(handle); +#endif +} - if (snd_pcm_status_get_state(status) == SND_PCM_STATE_SUSPENDED) - { - jack_log("**** alsa_pcm: pcm in suspended state, resuming it" ); - if (driver->capture_handle) { - if ((res = snd_pcm_prepare(driver->capture_handle)) - < 0) { - jack_error("error preparing after suspend: %s", snd_strerror(res)); - } - } - if (driver->playback_handle) { - if ((res = snd_pcm_prepare(driver->playback_handle)) - < 0) { - jack_error("error preparing after suspend: %s", snd_strerror(res)); +int +alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs) +{ + int state; + + for (int i = 0; i < driver->devices_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (device->capture_handle) { + state = alsa_driver_get_state(device->capture_handle, SND_PCM_STREAM_CAPTURE); + // TODO overrun + if (state == SND_PCM_STATE_XRUN) { + driver->xrun_count++; +#ifdef __QNXNTO__ + /* Timestamp api's are not available as per QNX Documentation */ + *delayed_usecs = 0; +#else + snd_pcm_status_t *status; + snd_pcm_status_alloca(&status); + snd_pcm_status(device->capture_handle, status); + struct timeval now, diff, tstamp; + snd_pcm_status_get_tstamp(status,&now); + snd_pcm_status_get_trigger_tstamp(status, &tstamp); + timersub(&now, &tstamp, &diff); + *delayed_usecs = diff.tv_sec * 1000000.0 + diff.tv_usec; +#endif + jack_log("**** alsa_pcm: xrun of at least %.3f msecs",*delayed_usecs / 1000.0); } } - } - if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN - && driver->process_count > XRUN_REPORT_DELAY) { - struct timeval now, diff, tstamp; - driver->xrun_count++; - snd_pcm_status_get_tstamp(status,&now); - snd_pcm_status_get_trigger_tstamp(status, &tstamp); - timersub(&now, &tstamp, &diff); - *delayed_usecs = diff.tv_sec * 1000000.0 + diff.tv_usec; - jack_log("**** alsa_pcm: xrun of at least %.3f msecs",*delayed_usecs / 1000.0); - if (driver->capture_handle) { - jack_log("Repreparing capture"); - if ((res = snd_pcm_prepare(driver->capture_handle)) < 0) { - jack_error("error preparing after xrun: %s", snd_strerror(res)); - } - } - if (driver->playback_handle) { - jack_log("Repreparing playback"); - if ((res = snd_pcm_prepare(driver->playback_handle)) < 0) { - jack_error("error preparing after xrun: %s", snd_strerror(res)); + if (device->playback_handle) { + state = alsa_driver_get_state(device->playback_handle, SND_PCM_STREAM_PLAYBACK); + // TODO overrun + if (state == SND_PCM_STATE_XRUN) { + driver->xrun_count++; +#ifdef __QNXNTO__ + /* Timestamp api's are not available as per QNX Documentation */ + *delayed_usecs = 0; +#else + snd_pcm_status_t *status; + snd_pcm_status_alloca(&status); + snd_pcm_status(device->playback_handle, status); + struct timeval now, diff, tstamp; + snd_pcm_status_get_tstamp(status,&now); + snd_pcm_status_get_trigger_tstamp(status, &tstamp); + timersub(&now, &tstamp, &diff); + *delayed_usecs = diff.tv_sec * 1000000.0 + diff.tv_usec; +#endif + jack_log("**** alsa_pcm: xrun of at least %.3f msecs",*delayed_usecs / 1000.0); } } } if (alsa_driver_restart (driver)) { + jack_error("xrun recovery failed to restart driver"); return -1; } return 0; } -void -alsa_driver_silence_untouched_channels (alsa_driver_t *driver, +static void +alsa_driver_silence_untouched_channels (alsa_driver_t *driver, alsa_device_t *device, jack_nframes_t nframes) { channel_t chn; jack_nframes_t buffer_frames = driver->frames_per_cycle * driver->playback_nperiods; - for (chn = 0; chn < driver->playback_nchannels; chn++) { - if (bitset_contains (driver->channels_not_done, chn)) { - if (driver->silent[chn] < buffer_frames) { + for (chn = 0; chn < device->playback_nchannels; chn++) { + if (bitset_contains (device->channels_not_done, chn)) { + if (device->silent[chn] < buffer_frames) { alsa_driver_silence_on_channel_no_mark ( - driver, chn, nframes); - driver->silent[chn] += nframes; + driver, device, chn, nframes); + device->silent[chn] += nframes; } } } } -void -alsa_driver_set_clock_sync_status (alsa_driver_t *driver, channel_t chn, - ClockSyncStatus status) +#ifdef __QNXNTO__ +static int +alsa_driver_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space, bool is_capture) { - driver->clock_sync_data[chn] = status; - alsa_driver_clock_sync_notify (driver, chn, status); + pfds->fd = snd_pcm_file_descriptor (pcm, is_capture); + pfds->events = POLLHUP|POLLNVAL; + pfds->events |= (is_capture == SND_PCM_STREAM_PLAYBACK) ? POLLOUT : POLLIN; + + return snd_pcm_poll_descriptors_count(pcm); } +static snd_pcm_sframes_t +alsa_driver_avail(alsa_driver_t *driver, snd_pcm_t *pcm, bool is_capture) +{ + /* QNX guarantees that after poll() event at least one perido is available */ + return driver->frames_per_cycle; +} +#else +static int +alsa_driver_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space, bool is_capture) +{ + return snd_pcm_poll_descriptors(pcm, pfds, space); +} + +static snd_pcm_sframes_t +alsa_driver_avail(alsa_driver_t *driver, snd_pcm_t *pcm, bool is_capture) +{ + return snd_pcm_avail_update(pcm); +} +#endif + static int under_gdb = FALSE; jack_nframes_t -alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float +alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t *status, float *delayed_usecs) { snd_pcm_sframes_t avail = 0; snd_pcm_sframes_t capture_avail = 0; snd_pcm_sframes_t playback_avail = 0; - int xrun_detected = FALSE; - int need_capture; - int need_playback; - int retry_cnt = 0; - unsigned int i; jack_time_t poll_enter; jack_time_t poll_ret = 0; *status = -1; *delayed_usecs = 0; - need_capture = driver->capture_handle ? 1 : 0; + int cap_revents[driver->devices_c_count]; + memset(cap_revents, 0, sizeof(cap_revents)); + int play_revents[driver->devices_p_count]; + memset(play_revents, 0, sizeof(play_revents)); - if (extra_fd >= 0) { - need_playback = 0; - } else { - need_playback = driver->playback_handle ? 1 : 0; - } + int pfd_cap_count[driver->devices_c_count]; + int pfd_play_count[driver->devices_p_count]; + /* In case if extra_fd is positive number then should be added to pfd_count + * since at present extra_fd is always negative this is not changed now. + */ + int pfd_count = driver->capture_nfds + driver->playback_nfds; + + /* special case where all devices are stopped */ + if (pfd_count == 0) { + + driver->poll_last = jack_get_microseconds (); - again: + if (driver->poll_next > driver->poll_last) { + struct timespec duration, remain; + duration.tv_sec = 0; + duration.tv_nsec = (int64_t) ((driver->poll_next - driver->poll_last) * 1000); + nanosleep(&duration, &remain); - while ((need_playback || need_capture) && !xrun_detected) { + driver->poll_last = jack_get_microseconds (); + } + + SetTime(driver->poll_last); + driver->poll_next = driver->poll_last + driver->period_usecs; + + *status = ALSA_DRIVER_WAIT_OK; + + return driver->frames_per_cycle; + } + while (pfd_count > 0) { int poll_result; - unsigned int ci = 0; - unsigned int nfds; - unsigned short revents; + int pfd_index = 0; - nfds = 0; + /* collect capture poll descriptors */ + for (int i = 0; i < driver->devices_c_count; ++i) { + /* this device already triggered poll event before */ + if (cap_revents[i]) { + continue; + } - if (need_playback) { - snd_pcm_poll_descriptors (driver->playback_handle, - &driver->pfd[0], - driver->playback_nfds); - nfds += driver->playback_nfds; - } + alsa_device_t *device = &driver->devices[i]; + if (!device->capture_handle) { + continue; + } + + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + continue; + } - if (need_capture) { - snd_pcm_poll_descriptors (driver->capture_handle, - &driver->pfd[nfds], - driver->capture_nfds); - ci = nfds; - nfds += driver->capture_nfds; + pfd_cap_count[i] = alsa_driver_poll_descriptors (device->capture_handle, + &driver->pfd[pfd_index], + pfd_count - pfd_index, + SND_PCM_STREAM_CAPTURE); + if (pfd_cap_count[i] < 0) { + /* In case of xrun -EPIPE is returned perform xrun recovery*/ + if (pfd_cap_count[i] == -EPIPE) { + jack_error("poll descriptors xrun C: %s pfd_cap_count[%d]=%d", device->capture_name, i, pfd_cap_count[i]); + *status = ALSA_DRIVER_WAIT_XRUN; + return 0; + } + /* for any other error return negative wait status to caller */ + jack_error("poll descriptors error C: %s pfd_cap_count[%d]=%d", device->capture_name, i, pfd_cap_count[i]); + *status = ALSA_DRIVER_WAIT_ERROR; + return 0; + } else { + pfd_index += pfd_cap_count[i]; + } } - /* ALSA doesn't set POLLERR in some versions of 0.9.X */ + /* collect playback poll descriptors */ + for (int i = 0; i < driver->devices_p_count; ++i) { + /* this device already triggered poll event before */ + if (play_revents[i]) { + continue; + } + + alsa_device_t *device = &driver->devices[i]; + if (!device->playback_handle) { + continue; + } - for (i = 0; i < nfds; i++) { - driver->pfd[i].events |= POLLERR; + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + + pfd_play_count[i] = alsa_driver_poll_descriptors (device->playback_handle, + &driver->pfd[pfd_index], + pfd_count - pfd_index, + SND_PCM_STREAM_PLAYBACK); + if (pfd_play_count[i] < 0) { + /* In case of xrun -EPIPE is returned perform xrun recovery*/ + if (pfd_cap_count[i] == -EPIPE) { + jack_error("poll descriptors xrun P: %s pfd_cap_count[%d]=%d", device->playback_name, i, pfd_play_count[i]); + *status = ALSA_DRIVER_WAIT_XRUN; + return 0; + } + /* for any other error return negative wait status to caller */ + jack_error("poll descriptors error P: %s pfd_cap_count[%d]=%d", device->playback_name, i, pfd_play_count[i]); + *status = ALSA_DRIVER_WAIT_ERROR; + return 0; + } else { + pfd_index += pfd_play_count[i]; + } } if (extra_fd >= 0) { - driver->pfd[nfds].fd = extra_fd; - driver->pfd[nfds].events = + driver->pfd[pfd_index].fd = extra_fd; + driver->pfd[pfd_index].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; - nfds++; + pfd_index++; } poll_enter = jack_get_microseconds (); @@ -1362,7 +2232,7 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float #ifdef __ANDROID__ poll_result = poll (driver->pfd, nfds, -1); //fix for sleep issue #else - poll_result = poll (driver->pfd, nfds, driver->poll_timeout_ms); + poll_result = poll (driver->pfd,(unsigned int) pfd_count, driver->poll_timeout_ms); #endif if (poll_result < 0) { @@ -1372,16 +2242,16 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float // under gdb, or when exiting due to a signal if (under_gdb) { jack_info(poll_log); - goto again; + continue; } jack_error(poll_log); - *status = -2; + *status = ALSA_DRIVER_WAIT_ERROR; return 0; } jack_error ("ALSA: poll call failed (%s)", strerror (errno)); - *status = -3; + *status = ALSA_DRIVER_WAIT_ERROR; return 0; } @@ -1389,22 +2259,19 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float poll_ret = jack_get_microseconds (); if (poll_result == 0) { - retry_cnt++; - if(retry_cnt > MAX_RETRY_COUNT) { - jack_error ("ALSA: poll time out, polled for %" PRIu64 - " usecs, Reached max retry cnt = %d, Exiting", - poll_ret - poll_enter, MAX_RETRY_COUNT); - *status = -5; - return 0; - } jack_error ("ALSA: poll time out, polled for %" PRIu64 - " usecs, Retrying with a recovery, retry cnt = %d", - poll_ret - poll_enter, retry_cnt); - *status = alsa_driver_xrun_recovery (driver, delayed_usecs); - if(*status != 0) { - jack_error ("ALSA: poll time out, recovery failed with status = %d", *status); - return 0; + " usecs, Retrying with a recovery", + poll_ret - poll_enter); + for (int i = 0; i < driver->devices_count; ++i) { + if (driver->devices[i].capture_handle && i < driver->devices_c_count && cap_revents[i] == 0) { + jack_log("device C: %s poll was requested", driver->devices[i].capture_name); + } + if (driver->devices[i].playback_handle && i < driver->devices_p_count && play_revents[i] == 0) { + jack_log("device P: %s poll was requested", driver->devices[i].playback_name); + } } + *status = ALSA_DRIVER_WAIT_XRUN; + return 0; } // JACK2 @@ -1416,132 +2283,184 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float } driver->poll_last = poll_ret; driver->poll_next = poll_ret + driver->period_usecs; -// JACK2 -/* - driver->engine->transport_cycle_start (driver->engine, - poll_ret); -*/ + } else { + /* check to see if it was the extra FD that caused us + * to return from poll */ + if (driver->pfd[pfd_index-1].revents == 0) { + /* we timed out on the extra fd */ + jack_error("extra fd error"); + *status = ALSA_DRIVER_WAIT_ERROR; + return -1; + } + /* if POLLIN was the only bit set, we're OK */ + *status = ALSA_DRIVER_WAIT_OK; + return (driver->pfd[pfd_index-1].revents == POLLIN) ? 0 : -1; } #ifdef DEBUG_WAKEUP fprintf (stderr, "%" PRIu64 ": checked %d fds, started at %" PRIu64 " %" PRIu64 " usecs since poll entered\n", - poll_ret, nfds, poll_enter, poll_ret - poll_enter); + poll_ret, desc_count, poll_enter, poll_ret - poll_enter); #endif - /* check to see if it was the extra FD that caused us - * to return from poll */ - - if (extra_fd >= 0) { - - if (driver->pfd[nfds-1].revents == 0) { - /* we timed out on the extra fd */ + pfd_index = 0; - *status = -4; - return -1; + for (int i = 0; i < driver->devices_c_count; ++i) { + /* this device already triggered poll event before */ + if (cap_revents[i]) { + continue; } - /* if POLLIN was the only bit set, we're OK */ - - *status = 0; - return (driver->pfd[nfds-1].revents == POLLIN) ? 0 : -1; - } + alsa_device_t *device = &driver->devices[i]; + if (!device->capture_handle) { + continue; + } - if (need_playback) { - if (snd_pcm_poll_descriptors_revents - (driver->playback_handle, &driver->pfd[0], - driver->playback_nfds, &revents) < 0) { - jack_error ("ALSA: playback revents failed"); - *status = -6; - return 0; + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + continue; } - if (revents & POLLNVAL) { - jack_error ("ALSA: playback device disconnected"); - *status = -7; + unsigned short collect_revs = 0; + if (snd_pcm_poll_descriptors_revents (device->capture_handle, &driver->pfd[pfd_index], + pfd_cap_count[i], &collect_revs) != 0) { + jack_error ("ALSA: capture revents failed"); + *status = ALSA_DRIVER_WAIT_ERROR; return 0; } - if (revents & POLLERR) { - xrun_detected = TRUE; + pfd_index += pfd_cap_count[i]; + if (collect_revs & (POLLERR | POLLIN)) { + if (collect_revs & POLLERR) { + /* optimization, no point in polling more if we already have xrun on one device */ + jack_error ("xrun C: '%s'", device->capture_name); + *status = ALSA_DRIVER_WAIT_XRUN; + return 0; + } + if (collect_revs & POLLIN) { + } + /* on next poll round skip fds from this device */ + cap_revents[i] = collect_revs; + pfd_count -= pfd_cap_count[i]; } + } - if (revents & POLLOUT) { - need_playback = 0; -#ifdef DEBUG_WAKEUP - fprintf (stderr, "%" PRIu64 - " playback stream ready\n", - poll_ret); -#endif + for (int i = 0; i < driver->devices_p_count; ++i) { + /* this device already triggered poll event before */ + if (play_revents[i]) { + continue; } - } - if (need_capture) { - if (snd_pcm_poll_descriptors_revents - (driver->capture_handle, &driver->pfd[ci], - driver->capture_nfds, &revents) < 0) { - jack_error ("ALSA: capture revents failed"); - *status = -6; - return 0; + alsa_device_t *device = &driver->devices[i]; + if (!device->playback_handle) { + continue; } - if (revents & POLLNVAL) { - jack_error ("ALSA: capture device disconnected"); - *status = -7; - return 0; + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + continue; } - if (revents & POLLERR) { - xrun_detected = TRUE; + unsigned short collect_revs = 0; + if (snd_pcm_poll_descriptors_revents (device->playback_handle, &driver->pfd[pfd_index], + pfd_play_count[i], &collect_revs) != 0) { + jack_error ("ALSA: playback revents failed"); + *status = ALSA_DRIVER_WAIT_ERROR; + return 0; } - if (revents & POLLIN) { - need_capture = 0; -#ifdef DEBUG_WAKEUP - fprintf (stderr, "%" PRIu64 - " capture stream ready\n", - poll_ret); -#endif + pfd_index += pfd_play_count[i]; + if (collect_revs & (POLLERR | POLLOUT)) { + if (collect_revs & POLLERR) { + /* optimization, no point in polling more if we already have xrun on one device */ + jack_error ("xrun P: '%s'", device->playback_name); + *status = ALSA_DRIVER_WAIT_XRUN; + return 0; + } + if (collect_revs & POLLNVAL) { + jack_error ("ALSA: playback device disconnected"); + *status = ALSA_DRIVER_WAIT_ERROR; + return 0; + } + if (collect_revs & POLLOUT) { + } + /* on next poll round skip fds from this device */ + play_revents[i] = collect_revs; + pfd_count -= pfd_play_count[i]; } } } - if (driver->capture_handle) { - if ((capture_avail = snd_pcm_avail_update ( - driver->capture_handle)) < 0) { - if (capture_avail == -EPIPE) { - xrun_detected = TRUE; + /* TODO: amiartus; I assume all devices are snd_pcm_link-ed and running on the same clock source, + * therefore should have the same avail frames, however in practice, this might have to be reworked, + * since we should check carefully for avail frames on each device, make sure it matches and handle corner cases + */ + + capture_avail = INT_MAX; + + for (int i = 0; i < driver->devices_c_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + if (!device->capture_handle) { + continue; + } + + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + + snd_pcm_sframes_t avail = 0; + if ((avail = alsa_driver_avail (driver, device->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { + if (avail == -EPIPE) { + jack_error ("ALSA: avail_update xrun on capture dev '%s'", device->capture_name); + *status = ALSA_DRIVER_WAIT_XRUN; + return 0; } else { - jack_error ("unknown ALSA avail_update return" - " value (%u)", capture_avail); + jack_error ("unknown ALSA avail_update return value (%u)", capture_avail); } } - } else { - /* odd, but see min() computation below */ - capture_avail = INT_MAX; + capture_avail = capture_avail < avail ? capture_avail : avail; } - if (driver->playback_handle) { - if ((playback_avail = snd_pcm_avail_update ( - driver->playback_handle)) < 0) { - if (playback_avail == -EPIPE) { - xrun_detected = TRUE; + playback_avail = INT_MAX; + + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + if (!device->playback_handle) { + continue; + } + + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + + snd_pcm_sframes_t avail = 0; + if ((avail = alsa_driver_avail (driver, device->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { + if (avail == -EPIPE) { + jack_error ("ALSA: avail_update xrun on playback dev '%s'", device->playback_name); + *status = ALSA_DRIVER_WAIT_XRUN; + return 0; } else { - jack_error ("unknown ALSA avail_update return" - " value (%u)", playback_avail); + jack_error ("unknown ALSA avail_update return value (%u)", playback_avail); } } - } else { - /* odd, but see min() computation below */ - playback_avail = INT_MAX; + playback_avail = playback_avail < avail ? playback_avail : avail; } - if (xrun_detected) { - *status = alsa_driver_xrun_recovery (driver, delayed_usecs); - return 0; + /* mark all channels not done for now. read/write will change this */ + for (int i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + + if (!device->playback_handle) { + continue; + } + + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + continue; + } + + bitset_copy (device->channels_not_done, device->channels_done); } - *status = 0; - driver->last_wait_ust = poll_ret; + *status = ALSA_DRIVER_WAIT_OK; avail = capture_avail < playback_avail ? capture_avail : playback_avail; @@ -1551,10 +2470,6 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float avail, playback_avail, capture_avail); #endif - /* mark all channels not done for now. read/write will change this */ - - bitset_copy (driver->channels_not_done, driver->channels_done); - /* constrain the available count to the nearest (round down) number of periods. */ @@ -1562,52 +2477,60 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float return avail - (avail % driver->frames_per_cycle); } - int alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) { snd_pcm_sframes_t contiguous; snd_pcm_sframes_t nread; snd_pcm_uframes_t offset; - jack_nframes_t orig_nframes; -// jack_default_audio_sample_t* buf; -// channel_t chn; -// JSList *node; -// jack_port_t* port; int err; if (nframes > driver->frames_per_cycle) { return -1; } -// JACK2 -/* - if (driver->engine->freewheeling) { - return 0; - } -*/ - if (driver->midi) - (driver->midi->read)(driver->midi, nframes); + for (size_t i = 0; i < driver->devices_c_count; ++i) { + alsa_device_t *device = &driver->devices[i]; - if (!driver->capture_handle) { - return 0; - } + if (!device->capture_handle) { + continue; + } - nread = 0; - contiguous = 0; - orig_nframes = nframes; + if (device->capture_target_state != SND_PCM_STATE_RUNNING) { + continue; + } - while (nframes) { + nread = 0; + contiguous = 0; + jack_nframes_t frames_remain = nframes; - contiguous = nframes; + while (frames_remain) { + + contiguous = frames_remain; + + if (alsa_driver_get_channel_addresses ( + driver, + device, + (snd_pcm_uframes_t *) &contiguous, + (snd_pcm_uframes_t *) 0, + &offset, 0) < 0) { + return -1; + } + +#ifdef __QNXNTO__ + const size_t bytes = contiguous * device->capture_nchannels * device->capture_sample_bytes; + if ((err = snd_pcm_plugin_read(device->capture_handle, + device->capture_areas, bytes)) < bytes) { + jack_error("read C: %s, requested %d, got %d, snd error %s, errno %d", + device->capture_name, + bytes, + err, + alsa_channel_status_error_str(alsa_driver_get_state(device->capture_handle, 1)), + errno); + return -1; + } +#endif - if (alsa_driver_get_channel_addresses ( - driver, - (snd_pcm_uframes_t *) &contiguous, - (snd_pcm_uframes_t *) 0, - &offset, 0) < 0) { - return -1; - } // JACK2 /* for (chn = 0, node = driver->capture_ports; node; @@ -1624,17 +2547,20 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) buf + nread, contiguous); } */ - ReadInput(orig_nframes, contiguous, nread); + ReadInput(device, nframes, contiguous, nread); - if ((err = snd_pcm_mmap_commit (driver->capture_handle, - offset, contiguous)) < 0) { - jack_error ("ALSA: could not complete read of %" - PRIu32 " frames: error = %d", contiguous, err); - return -1; - } +#ifndef __QNXNTO__ + if ((err = snd_pcm_mmap_commit (device->capture_handle, + offset, contiguous)) < 0) { + jack_error ("ALSA: could not complete read commit %s of %" + PRIu32 " frames: error = %d", device->capture_name, contiguous, err); + return -1; + } +#endif - nframes -= contiguous; - nread += contiguous; + frames_remain -= contiguous; + nread += contiguous; + } } return 0; @@ -1643,77 +2569,61 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) int alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) { -// channel_t chn; -// JSList *node; -// JSList *mon_node; -// jack_default_audio_sample_t* buf; -// jack_default_audio_sample_t* monbuf; - jack_nframes_t orig_nframes; - snd_pcm_sframes_t nwritten; snd_pcm_sframes_t contiguous; + snd_pcm_sframes_t nwritten; snd_pcm_uframes_t offset; -// jack_port_t *port; int err; driver->process_count++; -// JACK2 -/* - if (!driver->playback_handle || driver->engine->freewheeling) { - return 0; - } -*/ - if (!driver->playback_handle) { - return 0; - } - if (nframes > driver->frames_per_cycle) { return -1; } - if (driver->midi) - (driver->midi->write)(driver->midi, nframes); - - nwritten = 0; - contiguous = 0; - orig_nframes = nframes; - - /* check current input monitor request status */ + for (size_t i = 0; i < driver->devices_p_count; ++i) { + alsa_device_t *device = &driver->devices[i]; - driver->input_monitor_mask = 0; - -// JACK2 -/* - for (chn = 0, node = driver->capture_ports; node; - node = jack_slist_next (node), chn++) { - if (((jack_port_t *) node->data)->shared->monitor_requests) { - driver->input_monitor_mask |= (1<playback_handle) { + continue; } - } -*/ - MonitorInput(); - if (driver->hw_monitoring) { - if ((driver->hw->input_monitor_mask - != driver->input_monitor_mask) - && !driver->all_monitor_in) { - driver->hw->set_input_monitor_mask ( - driver->hw, driver->input_monitor_mask); + if (device->playback_target_state != SND_PCM_STATE_RUNNING) { + continue; } - } - while (nframes) { + if (driver->midi) + (driver->midi->write)(driver->midi, nframes); - contiguous = nframes; + nwritten = 0; + contiguous = 0; + jack_nframes_t frames_remain = nframes; - if (alsa_driver_get_channel_addresses ( - driver, - (snd_pcm_uframes_t *) 0, - (snd_pcm_uframes_t *) &contiguous, - 0, &offset) < 0) { - return -1; + /* check current input monitor request status */ + driver->input_monitor_mask = 0; + + MonitorInput(); + + if (driver->hw_monitoring) { + if ((device->hw->input_monitor_mask + != driver->input_monitor_mask) + && !driver->all_monitor_in) { + device->hw->set_input_monitor_mask ( + device->hw, driver->input_monitor_mask); + } } + while (frames_remain) { + + contiguous = frames_remain; + + if (alsa_driver_get_channel_addresses ( + driver, + device, + (snd_pcm_uframes_t *) 0, + (snd_pcm_uframes_t *) &contiguous, + 0, &offset) < 0) { + return -1; + } // JACK2 /* for (chn = 0, node = driver->playback_ports, mon_node=driver->monitor_ports; @@ -1741,24 +2651,38 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) } */ - // JACK2 - WriteOutput(orig_nframes, contiguous, nwritten); + // JACK2 + WriteOutput(device, nframes, contiguous, nwritten); - if (!bitset_empty (driver->channels_not_done)) { - alsa_driver_silence_untouched_channels (driver, - contiguous); - } + if (!bitset_empty (device->channels_not_done)) { + alsa_driver_silence_untouched_channels (driver, device, contiguous); + } - if ((err = snd_pcm_mmap_commit (driver->playback_handle, - offset, contiguous)) < 0) { - jack_error ("ALSA: could not complete playback of %" - PRIu32 " frames: error = %d", contiguous, err); - if (err != -EPIPE && err != -ESTRPIPE) +#ifdef __QNXNTO__ + const size_t bytes = contiguous * device->playback_nchannels * device->playback_sample_bytes; + if ((err = snd_pcm_plugin_write(device->playback_handle, + device->playback_areas, bytes)) < bytes) { + jack_error("write P: %s, requested %d, got %d, snd error %s, errno %d", + device->playback_name, + bytes, + err, + alsa_channel_status_error_str(alsa_driver_get_state(device->playback_handle, 0)), + errno); return -1; - } + } +#else + if ((err = snd_pcm_mmap_commit (device->playback_handle, + offset, contiguous)) < 0) { + jack_error ("ALSA: could not complete playback commit %s of %" + PRIu32 " frames: error = %d", device->playback_name, contiguous, err); + if (err != -EPIPE && err != -ESTRPIPE) + return -1; + } +#endif - nframes -= contiguous; - nwritten += contiguous; + frames_remain -= contiguous; + nwritten += contiguous; + } } return 0; @@ -1815,32 +2739,44 @@ alsa_driver_clock_sync_status (channel_t chn) void alsa_driver_delete (alsa_driver_t *driver) { - JSList *node; - if (driver->midi) (driver->midi->destroy)(driver->midi); - for (node = driver->clock_sync_listeners; node; - node = jack_slist_next (node)) { - free (node->data); - } - jack_slist_free (driver->clock_sync_listeners); + for (int i = 0; i < driver->devices_count; ++i) { + if (driver->devices[i].capture_handle) { + snd_pcm_close (driver->devices[i].capture_handle); + driver->devices[i].capture_handle = 0; + } - if (driver->ctl_handle) { - snd_ctl_close (driver->ctl_handle); - driver->ctl_handle = 0; - } + if (driver->devices[i].playback_handle) { + snd_pcm_close (driver->devices[i].playback_handle); + driver->devices[i].playback_handle = 0; +#ifndef __QNXNTO__ + for (JSList *node = driver->devices[i].clock_sync_listeners; node; node = jack_slist_next (node)) { + free (node->data); + } + jack_slist_free (driver->devices[i].clock_sync_listeners); +#endif + } - if (driver->capture_handle) { - snd_pcm_close (driver->capture_handle); - driver->capture_handle = 0; - } + free(driver->devices[i].capture_name); + free(driver->devices[i].playback_name); + free(driver->devices[i].alsa_driver); + + alsa_driver_release_channel_dependent_memory (driver, &driver->devices[i]); + + if (driver->devices[i].hw) { + driver->devices[i].hw->release (driver->devices[i].hw); + driver->devices[i].hw = 0; + } - if (driver->playback_handle) { - snd_pcm_close (driver->playback_handle); - driver->capture_handle = 0; + if (driver->devices[i].ctl_handle) { + snd_ctl_close (driver->devices[i].ctl_handle); + driver->devices[i].ctl_handle = 0; + } } +#ifndef __QNXNTO__ if (driver->capture_hw_params) { snd_pcm_hw_params_free (driver->capture_hw_params); driver->capture_hw_params = 0; @@ -1860,20 +2796,11 @@ alsa_driver_delete (alsa_driver_t *driver) snd_pcm_sw_params_free (driver->playback_sw_params); driver->playback_sw_params = 0; } +#endif if (driver->pfd) { free (driver->pfd); } - - if (driver->hw) { - driver->hw->release (driver->hw); - driver->hw = 0; - } - free(driver->alsa_name_playback); - free(driver->alsa_name_capture); - free(driver->alsa_driver); - - alsa_driver_release_channel_dependent_memory (driver); //JACK2 //jack_driver_nt_finish ((jack_driver_nt_t *) driver); free (driver); @@ -1980,42 +2907,139 @@ discover_alsa_using_apps () } } +static int +alsa_driver_open_device (alsa_driver_t *driver, alsa_device_t *device, bool is_capture) +{ + int err = 0; + char* current_apps; + + if(is_capture) { +#ifdef __QNXNTO__ + err = snd_pcm_open_name (&device->capture_handle, + device->capture_name, + SND_PCM_OPEN_CAPTURE | SND_PCM_OPEN_NONBLOCK); +#else + err = snd_pcm_open (&device->capture_handle, + device->capture_name, + SND_PCM_STREAM_CAPTURE, + SND_PCM_NONBLOCK); +#endif + } else { +#ifdef __QNXNTO__ + err = snd_pcm_open_name (&device->playback_handle, + device->playback_name, + SND_PCM_OPEN_PLAYBACK | SND_PCM_OPEN_NONBLOCK); +#else + err = snd_pcm_open (&device->playback_handle, + device->playback_name, + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK); +#endif + } + if (err < 0) { + switch (errno) { + case EBUSY: +#ifdef __ANDROID__ + jack_error ("\n\nATTENTION: The device \"%s\" is " + "already in use. Please stop the" + " application using it and " + "run JACK again", + is_capture ? device->alsa_name_capture : device->alsa_name_playback); +#else + current_apps = discover_alsa_using_apps (); + if (current_apps) { + jack_error ("\n\nATTENTION: The device \"%s\" is " + "already in use. The following applications " + " are using your soundcard(s) so you should " + " check them and stop them as necessary before " + " trying to start JACK again:\n\n%s", + is_capture ? device->capture_name : device->playback_name, + current_apps); + free (current_apps); + } else { + jack_error ("\n\nATTENTION: The device \"%s\" is " + "already in use. Please stop the" + " application using it and " + "run JACK again", + is_capture ? device->capture_name : device->playback_name); + } +#endif + break; + + case EPERM: + jack_error ("you do not have permission to open " + "the audio device \"%s\" for %s", + is_capture ? device->capture_name : device->playback_name, + is_capture ? "capture" : "playback"); + break; + + case EINVAL: + jack_error ("the state of handle or the mode is invalid " + "or invalid state change occured \"%s\" for %s", + is_capture ? device->capture_name : device->playback_name, + is_capture ? "capture" : "playback"); + break; + + case ENOENT: + jack_error ("device \"%s\" does not exist for %s", + is_capture ? device->capture_name : device->playback_name, + is_capture ? "capture" : "playback"); + break; + + case ENOMEM: + jack_error ("Not enough memory available for allocation for \"%s\" for %s", + is_capture ? device->capture_name : device->playback_name, + is_capture ? "capture" : "playback"); + break; + + case SND_ERROR_INCOMPATIBLE_VERSION: + jack_error ("Version mismatch \"%s\" for %s", + is_capture ? device->capture_name : device->playback_name, + is_capture ? "capture" : "playback"); + break; + } + if(is_capture) { + device->capture_handle = NULL; + } else { + device->playback_handle = NULL; + } + } + + if (is_capture && device->capture_handle) { +#ifdef __QNXNTO__ + snd_pcm_nonblock_mode (device->capture_handle, 0); +#else + snd_pcm_nonblock (device->capture_handle, 0); +#endif + } else if(!is_capture && device->playback_handle) { +#ifdef __QNXNTO__ + snd_pcm_nonblock_mode (device->playback_handle, 0); +#else + snd_pcm_nonblock (device->playback_handle, 0); +#endif + } + + return err; +} + jack_driver_t * -alsa_driver_new (char *name, char *playback_alsa_device, - char *capture_alsa_device, - jack_client_t *client, - jack_nframes_t frames_per_cycle, - jack_nframes_t user_nperiods, - jack_nframes_t rate, - int hw_monitoring, - int hw_metering, - int capturing, - int playing, - DitherAlgorithm dither, - int soft_mode, - int monitor, - int user_capture_nchnls, - int user_playback_nchnls, - int shorts_first, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency, - alsa_midi_t *midi_driver - ) +alsa_driver_new (char *name, alsa_driver_info_t info, jack_client_t *client) { int err; - char* current_apps; alsa_driver_t *driver; - jack_info ("creating alsa driver ... %s|%s|%" PRIu32 "|%" PRIu32 + jack_info ("creating alsa driver ... %s|%" PRIu32 "|%s|%" PRIu32 "|%" PRIu32 "|%" PRIu32 "|%" PRIu32"|%" PRIu32"|%" PRIu32 "|%s|%s|%s|%s", - playing ? playback_alsa_device : "-", - capturing ? capture_alsa_device : "-", - frames_per_cycle, user_nperiods, rate, - user_capture_nchnls,user_playback_nchnls, - hw_monitoring ? "hwmon": "nomon", - hw_metering ? "hwmeter":"swmeter", - soft_mode ? "soft-mode":"-", - shorts_first ? "16bit":"32bit"); + info.devices_capture_size > 0 ? info.devices[0].capture_name : "-", + info.devices_capture_size, + info.devices_playback_size > 0 ? info.devices[0].playback_name : "-", + info.devices_playback_size, + info.frames_per_period, info.periods_n, info.frame_rate, + info.devices[0].capture_channels, info.devices[0].playback_channels, + info.hw_monitoring ? "hwmon": "nomon", + info.hw_metering ? "hwmeter":"swmeter", + info.soft_mode ? "soft-mode":"-", + info.shorts_first ? "16bit":"32bit"); driver = (alsa_driver_t *) calloc (1, sizeof (alsa_driver_t)); @@ -2034,213 +3058,65 @@ alsa_driver_new (char *name, char *playback_alsa_device, driver->nt_run_cycle = (JackDriverNTRunCycleFunction) alsa_driver_run_cycle; */ - driver->playback_handle = NULL; - driver->capture_handle = NULL; - driver->ctl_handle = 0; - driver->hw = 0; - driver->capture_and_playback_not_synced = FALSE; - driver->max_nchannels = 0; - driver->user_nchannels = 0; - driver->playback_nchannels = user_playback_nchnls; - driver->capture_nchannels = user_capture_nchnls; - driver->playback_sample_bytes = (shorts_first ? 2:4); - driver->capture_sample_bytes = (shorts_first ? 2:4); - driver->capture_frame_latency = capture_latency; - driver->playback_frame_latency = playback_latency; - - driver->playback_addr = 0; - driver->capture_addr = 0; - driver->playback_interleave_skip = NULL; - driver->capture_interleave_skip = NULL; - - - driver->silent = 0; + driver->capture_frame_latency = info.capture_latency; + driver->playback_frame_latency = info.playback_latency; + driver->all_monitor_in = FALSE; - driver->with_monitor_ports = monitor; + driver->with_monitor_ports = info.monitor; driver->clock_mode = ClockMaster; /* XXX is it? */ driver->input_monitor_mask = 0; /* XXX is it? */ - driver->capture_ports = 0; - driver->playback_ports = 0; - driver->monitor_ports = 0; - driver->pfd = 0; driver->playback_nfds = 0; driver->capture_nfds = 0; - driver->dither = dither; - driver->soft_mode = soft_mode; - - driver->quirk_bswap = 0; - - pthread_mutex_init (&driver->clock_sync_lock, 0); - driver->clock_sync_listeners = 0; + driver->dither = info.dither; + driver->soft_mode = info.soft_mode; driver->poll_late = 0; driver->xrun_count = 0; driver->process_count = 0; - driver->alsa_name_playback = strdup (playback_alsa_device); - driver->alsa_name_capture = strdup (capture_alsa_device); - - driver->midi = midi_driver; + driver->midi = info.midi_driver; driver->xrun_recovery = 0; - if (alsa_driver_check_card_type (driver)) { - alsa_driver_delete (driver); - return NULL; - } - - alsa_driver_hw_specific (driver, hw_monitoring, hw_metering); - - if (playing) { - if (snd_pcm_open (&driver->playback_handle, - playback_alsa_device, - SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK) < 0) { - switch (errno) { - case EBUSY: -#ifdef __ANDROID__ - jack_error ("\n\nATTENTION: The playback device \"%s\" is " - "already in use. Please stop the" - " application using it and " - "run JACK again", - playback_alsa_device); -#else - current_apps = discover_alsa_using_apps (); - if (current_apps) { - jack_error ("\n\nATTENTION: The playback device \"%s\" is " - "already in use. The following applications " - " are using your soundcard(s) so you should " - " check them and stop them as necessary before " - " trying to start JACK again:\n\n%s", - playback_alsa_device, - current_apps); - free (current_apps); - } else { - jack_error ("\n\nATTENTION: The playback device \"%s\" is " - "already in use. Please stop the" - " application using it and " - "run JACK again", - playback_alsa_device); - } -#endif - alsa_driver_delete (driver); - return NULL; - - case EPERM: - jack_error ("you do not have permission to open " - "the audio device \"%s\" for playback", - playback_alsa_device); - alsa_driver_delete (driver); - return NULL; - break; - } - - driver->playback_handle = NULL; - } - - if (driver->playback_handle) { - snd_pcm_nonblock (driver->playback_handle, 0); - } - } - - if (capturing) { - if (snd_pcm_open (&driver->capture_handle, - capture_alsa_device, - SND_PCM_STREAM_CAPTURE, - SND_PCM_NONBLOCK) < 0) { - switch (errno) { - case EBUSY: -#ifdef __ANDROID__ - jack_error ("\n\nATTENTION: The capture (recording) device \"%s\" is " - "already in use", - capture_alsa_device); -#else - current_apps = discover_alsa_using_apps (); - if (current_apps) { - jack_error ("\n\nATTENTION: The capture device \"%s\" is " - "already in use. The following applications " - " are using your soundcard(s) so you should " - " check them and stop them as necessary before " - " trying to start JACK again:\n\n%s", - capture_alsa_device, - current_apps); - free (current_apps); - } else { - jack_error ("\n\nATTENTION: The capture (recording) device \"%s\" is " - "already in use. Please stop the" - " application using it and " - "run JACK again", - capture_alsa_device); - } - alsa_driver_delete (driver); - return NULL; -#endif - break; - - case EPERM: - jack_error ("you do not have permission to open " - "the audio device \"%s\" for capture", - capture_alsa_device); - alsa_driver_delete (driver); - return NULL; - break; - } - - driver->capture_handle = NULL; - } - - if (driver->capture_handle) { - snd_pcm_nonblock (driver->capture_handle, 0); - } - } - - if (driver->playback_handle == NULL) { - if (playing) { + driver->devices_c_count = info.devices_capture_size; + driver->devices_p_count = info.devices_playback_size; + driver->devices_count = info.devices_capture_size > info.devices_playback_size ? info.devices_capture_size : info.devices_playback_size; + driver->devices = (alsa_device_t*) calloc(driver->devices_count, sizeof(*driver->devices)); - /* they asked for playback, but we can't do it */ + driver->frame_rate = info.frame_rate; + driver->frames_per_cycle = info.frames_per_period; + driver->user_nperiods = info.periods_n; - jack_error ("ALSA: Cannot open PCM device %s for " - "playback. Falling back to capture-only" - " mode", name); + driver->preferred_sample_bytes = info.shorts_first ? 2 : 4; - if (driver->capture_handle == NULL) { - /* can't do anything */ - alsa_driver_delete (driver); - return NULL; - } + driver->features = info.features; - playing = FALSE; + for (int i = 0; i < driver->devices_count; ++i) { + alsa_device_t *device = &driver->devices[i]; + if (i < driver->devices_c_count) { + device->capture_sample_bytes = 0; + device->capture_sample_format = SND_PCM_FORMAT_UNKNOWN; + device->capture_name = strdup(info.devices[i].capture_name); + device->capture_nchannels = info.devices[i].capture_channels; } - } - - if (driver->capture_handle == NULL) { - if (capturing) { - - /* they asked for capture, but we can't do it */ - - jack_error ("ALSA: Cannot open PCM device %s for " - "capture. Falling back to playback-only" - " mode", name); - - if (driver->playback_handle == NULL) { - /* can't do anything */ - alsa_driver_delete (driver); - return NULL; - } - - capturing = FALSE; + if (i < driver->devices_p_count) { + device->playback_sample_bytes = 0; + device->playback_sample_format = SND_PCM_FORMAT_UNKNOWN; + device->playback_name = strdup(info.devices[i].playback_name); + device->playback_nchannels = info.devices[i].playback_channels; } } +#ifndef __QNXNTO__ driver->playback_hw_params = 0; driver->capture_hw_params = 0; driver->playback_sw_params = 0; driver->capture_sw_params = 0; - if (driver->playback_handle) { + if (driver->devices_p_count) { if ((err = snd_pcm_hw_params_malloc ( &driver->playback_hw_params)) < 0) { jack_error ("ALSA: could not allocate playback hw" @@ -2258,7 +3134,7 @@ alsa_driver_new (char *name, char *playback_alsa_device, } } - if (driver->capture_handle) { + if (driver->devices_c_count) { if ((err = snd_pcm_hw_params_malloc ( &driver->capture_hw_params)) < 0) { jack_error ("ALSA: could not allocate capture hw" @@ -2275,29 +3151,30 @@ alsa_driver_new (char *name, char *playback_alsa_device, return NULL; } } +#endif + + driver->client = client; - if (alsa_driver_set_parameters (driver, frames_per_cycle, - user_nperiods, rate)) { - alsa_driver_delete (driver); - return NULL; - } - driver->capture_and_playback_not_synced = FALSE; +#ifndef __QNXNTO__ + for (int i = 0; i < driver->devices_p_count; ++i) { + pthread_mutex_init (&driver->devices[i].clock_sync_lock, 0); + driver->devices[i].clock_sync_listeners = 0; - if (driver->capture_handle && driver->playback_handle) { - if (snd_pcm_link (driver->playback_handle, - driver->capture_handle) != 0) { - driver->capture_and_playback_not_synced = TRUE; + if (alsa_driver_check_card_type (driver, &driver->devices[i])) { + alsa_driver_delete(driver); + return NULL; } - } - driver->client = client; + alsa_driver_hw_specific (driver, &driver->devices[i], info.hw_monitoring, info.hw_metering); + } +#endif return (jack_driver_t *) driver; } int -alsa_driver_listen_for_clock_sync_status (alsa_driver_t *driver, +alsa_driver_listen_for_clock_sync_status (alsa_device_t *device, ClockSyncListenerFunction func, void *arg) { @@ -2306,53 +3183,51 @@ alsa_driver_listen_for_clock_sync_status (alsa_driver_t *driver, csl = (ClockSyncListener *) malloc (sizeof (ClockSyncListener)); csl->function = func; csl->arg = arg; - csl->id = driver->next_clock_sync_listener_id++; + csl->id = device->next_clock_sync_listener_id++; - pthread_mutex_lock (&driver->clock_sync_lock); - driver->clock_sync_listeners = - jack_slist_prepend (driver->clock_sync_listeners, csl); - pthread_mutex_unlock (&driver->clock_sync_lock); + pthread_mutex_lock (&device->clock_sync_lock); + device->clock_sync_listeners = + jack_slist_prepend (device->clock_sync_listeners, csl); + pthread_mutex_unlock (&device->clock_sync_lock); return csl->id; } int -alsa_driver_stop_listening_to_clock_sync_status (alsa_driver_t *driver, +alsa_driver_stop_listening_to_clock_sync_status (alsa_device_t *device, unsigned int which) - { JSList *node; int ret = -1; - pthread_mutex_lock (&driver->clock_sync_lock); - for (node = driver->clock_sync_listeners; node; + pthread_mutex_lock (&device->clock_sync_lock); + for (node = device->clock_sync_listeners; node; node = jack_slist_next (node)) { if (((ClockSyncListener *) node->data)->id == which) { - driver->clock_sync_listeners = + device->clock_sync_listeners = jack_slist_remove_link ( - driver->clock_sync_listeners, node); + device->clock_sync_listeners, node); free (node->data); jack_slist_free_1 (node); ret = 0; break; } } - pthread_mutex_unlock (&driver->clock_sync_lock); + pthread_mutex_unlock (&device->clock_sync_lock); return ret; } void -alsa_driver_clock_sync_notify (alsa_driver_t *driver, channel_t chn, +alsa_device_clock_sync_notify (alsa_device_t *device, channel_t chn, ClockSyncStatus status) { JSList *node; - pthread_mutex_lock (&driver->clock_sync_lock); - for (node = driver->clock_sync_listeners; node; + pthread_mutex_lock (&device->clock_sync_lock); + for (node = device->clock_sync_listeners; node; node = jack_slist_next (node)) { ClockSyncListener *csl = (ClockSyncListener *) node->data; csl->function (chn, status, csl->arg); } - pthread_mutex_unlock (&driver->clock_sync_lock); - + pthread_mutex_unlock (&device->clock_sync_lock); } /* DRIVER "PLUGIN" INTERFACE */ diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h index f01c08ea..b9c1fe64 100644 --- a/linux/alsa/alsa_driver.h +++ b/linux/alsa/alsa_driver.h @@ -21,7 +21,12 @@ #ifndef __jack_alsa_driver_h__ #define __jack_alsa_driver_h__ +#ifdef __QNXNTO__ +#include +#else #include +#endif + #include "bitset.h" #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -41,6 +46,34 @@ #include "memops.h" #include "alsa_midi.h" +#ifdef __QNXNTO__ +#define SND_PCM_FORMAT_S16_LE SND_PCM_SFMT_S16_LE +#define SND_PCM_FORMAT_S16_BE SND_PCM_SFMT_S16_BE +#define SND_PCM_FORMAT_S24_LE SND_PCM_SFMT_S24_LE +#define SND_PCM_FORMAT_S24_BE SND_PCM_SFMT_S24_BE +#define SND_PCM_FORMAT_S32_LE SND_PCM_SFMT_S32_LE +#define SND_PCM_FORMAT_S32_BE SND_PCM_SFMT_S32_BE +#define SND_PCM_FORMAT_FLOAT_LE SND_PCM_SFMT_FLOAT_LE +#define SND_PCM_FORMAT_UNKNOWN SND_PCM_SFMT_SPECIAL +#define SND_PCM_STATE_PREPARED SND_PCM_STATUS_PREPARED +#define SND_PCM_STATE_SUSPENDED SND_PCM_STATUS_SUSPENDED +#define SND_PCM_STATE_XRUN SND_PCM_STATUS_UNDERRUN +#define SND_PCM_STATE_RUNNING SND_PCM_STATUS_RUNNING +#define SND_PCM_STATE_NOTREADY SND_PCM_STATUS_NOTREADY +#define SND_PCM_STREAM_PLAYBACK SND_PCM_CHANNEL_PLAYBACK +#define SND_PCM_STREAM_CAPTURE SND_PCM_CHANNEL_CAPTURE + +typedef unsigned long snd_pcm_uframes_t; +typedef signed long snd_pcm_sframes_t; +typedef int32_t alsa_driver_default_format_t; +#else +#define SND_PCM_STATE_NOTREADY (SND_PCM_STATE_LAST + 1) +#endif + +#define ALSA_DRIVER_FEAT_BACKEND_EVAL_ON_INIT (1 << 0) +#define ALSA_DRIVER_FEAT_BACKEND_CLOSE_IDLE (1 << 1) +#define ALSA_DRIVER_FEAT_UNLINKED_DEVS (1 << 2) + #ifdef __cplusplus extern "C" { @@ -54,61 +87,105 @@ typedef void (*WriteCopyFunction) (char *dst, jack_default_audio_sample_t *src, unsigned long dst_skip_bytes, dither_state_t *state); +typedef struct _alsa_device { +#ifdef __QNXNTO__ + unsigned int playback_sample_format; + unsigned int capture_sample_format; + void *capture_areas; + void *playback_areas; + void *capture_areas_ptr; + void *playback_areas_ptr; +#else + snd_pcm_format_t playback_sample_format; + snd_pcm_format_t capture_sample_format; + const snd_pcm_channel_area_t *capture_areas; + const snd_pcm_channel_area_t *playback_areas; +#endif + snd_pcm_t *playback_handle; + snd_pcm_t *capture_handle; + + char *playback_name; + char *capture_name; + + char **playback_addr; + char **capture_addr; + + channel_t playback_channel_offset; + channel_t capture_channel_offset; + + channel_t playback_nchannels; + channel_t capture_nchannels; + channel_t max_nchannels; + channel_t user_nchannels; + + bitset_t channels_not_done; + bitset_t channels_done; + + char quirk_bswap; + + ReadCopyFunction read_via_copy; + WriteCopyFunction write_via_copy; + + unsigned long interleave_unit; + unsigned long *capture_interleave_skip; + unsigned long *playback_interleave_skip; + + char playback_interleaved; + char capture_interleaved; + + unsigned long *silent; + + unsigned long playback_sample_bytes; + unsigned long capture_sample_bytes; + + /* device is 'snd_pcm_link' to a group, only 1 group of linked devices is allowed */ + int capture_linked; + int playback_linked; + + int capture_xrun_count; + int playback_xrun_count; + + /* desired state of device, decided by JackAlsaDriver */ + int capture_target_state; + int playback_target_state; + + jack_hardware_t *hw; + snd_ctl_t *ctl_handle; + char *alsa_driver; + + JSList *clock_sync_listeners; + pthread_mutex_t clock_sync_lock; + unsigned long next_clock_sync_listener_id; +} alsa_device_t; + typedef struct _alsa_driver { JACK_DRIVER_NT_DECL +#ifndef __QNXNTO__ + snd_pcm_hw_params_t *playback_hw_params; + snd_pcm_sw_params_t *playback_sw_params; + snd_pcm_hw_params_t *capture_hw_params; + snd_pcm_sw_params_t *capture_sw_params; +#endif int poll_timeout_ms; jack_time_t poll_last; jack_time_t poll_next; - char **playback_addr; - char **capture_addr; - const snd_pcm_channel_area_t *capture_areas; - const snd_pcm_channel_area_t *playback_areas; struct pollfd *pfd; unsigned int playback_nfds; unsigned int capture_nfds; - unsigned long interleave_unit; - unsigned long *capture_interleave_skip; - unsigned long *playback_interleave_skip; - channel_t max_nchannels; - channel_t user_nchannels; channel_t playback_nchannels; channel_t capture_nchannels; - unsigned long playback_sample_bytes; - unsigned long capture_sample_bytes; jack_nframes_t frame_rate; jack_nframes_t frames_per_cycle; jack_nframes_t capture_frame_latency; jack_nframes_t playback_frame_latency; - unsigned long *silent; - char *alsa_name_playback; - char *alsa_name_capture; - char *alsa_driver; - bitset_t channels_not_done; - bitset_t channels_done; - snd_pcm_format_t playback_sample_format; - snd_pcm_format_t capture_sample_format; - float max_sample_val; unsigned long user_nperiods; unsigned int playback_nperiods; unsigned int capture_nperiods; - unsigned long last_mask; - snd_ctl_t *ctl_handle; - snd_pcm_t *playback_handle; - snd_pcm_t *capture_handle; - snd_pcm_hw_params_t *playback_hw_params; - snd_pcm_sw_params_t *playback_sw_params; - snd_pcm_hw_params_t *capture_hw_params; - snd_pcm_sw_params_t *capture_sw_params; - jack_hardware_t *hw; - ClockSyncStatus *clock_sync_data; jack_client_t *client; - JSList *capture_ports; - JSList *playback_ports; - JSList *monitor_ports; unsigned long input_monitor_mask; @@ -116,28 +193,17 @@ typedef struct _alsa_driver { char hw_monitoring; char hw_metering; char all_monitor_in; - char capture_and_playback_not_synced; - char playback_interleaved; - char capture_interleaved; char with_monitor_ports; char has_clock_sync_reporting; char has_hw_monitoring; char has_hw_metering; - char quirk_bswap; - ReadCopyFunction read_via_copy; - WriteCopyFunction write_via_copy; + int preferred_sample_bytes; int dither; dither_state_t *dither_state; SampleClockMode clock_mode; - JSList *clock_sync_listeners; - pthread_mutex_t clock_sync_lock; - unsigned long next_clock_sync_listener_id; - - int running; - int run; int poll_late; int xrun_count; @@ -146,83 +212,115 @@ typedef struct _alsa_driver { alsa_midi_t *midi; int xrun_recovery; + alsa_device_t *devices; + int devices_count; + int devices_c_count; + int devices_p_count; + + int features; } alsa_driver_t; +typedef struct _alsa_device_info { + char *capture_name; + char *playback_name; + + int capture_channels; + int playback_channels; +} alsa_device_info_t; + +typedef struct _alsa_driver_info { + alsa_device_info_t *devices; + uint32_t devices_capture_size; + uint32_t devices_playback_size; + + char *midi_name; + alsa_midi_t *midi_driver; + + jack_nframes_t frame_rate; + jack_nframes_t frames_per_period; + int periods_n; + + DitherAlgorithm dither; + + int shorts_first; + + jack_nframes_t capture_latency; + jack_nframes_t playback_latency; + + // these 4 should be reworked as struct.features + int hw_monitoring; + int hw_metering; + int monitor; + int soft_mode; + + int features; +} alsa_driver_info_t; + static inline void -alsa_driver_mark_channel_done (alsa_driver_t *driver, channel_t chn) { - bitset_remove (driver->channels_not_done, chn); - driver->silent[chn] = 0; +alsa_driver_mark_channel_done (alsa_driver_t *driver, alsa_device_t *device, channel_t chn) { + bitset_remove (device->channels_not_done, chn); + device->silent[chn] = 0; } static inline void -alsa_driver_silence_on_channel (alsa_driver_t *driver, channel_t chn, +alsa_driver_silence_on_channel (alsa_driver_t *driver, alsa_device_t *device, channel_t chn, jack_nframes_t nframes) { - if (driver->playback_interleaved) { + if (device->playback_interleaved) { memset_interleave - (driver->playback_addr[chn], - 0, nframes * driver->playback_sample_bytes, - driver->interleave_unit, - driver->playback_interleave_skip[chn]); + (device->playback_addr[chn], + 0, nframes * device->playback_sample_bytes, + device->interleave_unit, + device->playback_interleave_skip[chn]); } else { - memset (driver->playback_addr[chn], 0, - nframes * driver->playback_sample_bytes); + memset (device->playback_addr[chn], 0, + nframes * device->playback_sample_bytes); } - alsa_driver_mark_channel_done (driver,chn); + alsa_driver_mark_channel_done (driver, device, chn); } static inline void -alsa_driver_silence_on_channel_no_mark (alsa_driver_t *driver, channel_t chn, +alsa_driver_silence_on_channel_no_mark (alsa_driver_t *driver, alsa_device_t *device, channel_t chn, jack_nframes_t nframes) { - if (driver->playback_interleaved) { + if (device->playback_interleaved) { memset_interleave - (driver->playback_addr[chn], - 0, nframes * driver->playback_sample_bytes, - driver->interleave_unit, - driver->playback_interleave_skip[chn]); + (device->playback_addr[chn], + 0, nframes * device->playback_sample_bytes, + device->interleave_unit, + device->playback_interleave_skip[chn]); } else { - memset (driver->playback_addr[chn], 0, - nframes * driver->playback_sample_bytes); + memset (device->playback_addr[chn], 0, + nframes * device->playback_sample_bytes); } } static inline void alsa_driver_read_from_channel (alsa_driver_t *driver, + alsa_device_t *device, channel_t channel, jack_default_audio_sample_t *buf, jack_nframes_t nsamples) { - driver->read_via_copy (buf, - driver->capture_addr[channel], + device->read_via_copy (buf, + device->capture_addr[channel], nsamples, - driver->capture_interleave_skip[channel]); + device->capture_interleave_skip[channel]); } static inline void alsa_driver_write_to_channel (alsa_driver_t *driver, + alsa_device_t *device, channel_t channel, jack_default_audio_sample_t *buf, jack_nframes_t nsamples) { - driver->write_via_copy (driver->playback_addr[channel], + device->write_via_copy (device->playback_addr[channel], buf, nsamples, - driver->playback_interleave_skip[channel], + device->playback_interleave_skip[channel], driver->dither_state+channel); - alsa_driver_mark_channel_done (driver, channel); + alsa_driver_mark_channel_done (driver, device, channel); } -void alsa_driver_silence_untouched_channels (alsa_driver_t *driver, - jack_nframes_t nframes); -void alsa_driver_set_clock_sync_status (alsa_driver_t *driver, channel_t chn, - ClockSyncStatus status); -int alsa_driver_listen_for_clock_sync_status (alsa_driver_t *, - ClockSyncListenerFunction, - void *arg); -int alsa_driver_stop_listen_for_clock_sync_status (alsa_driver_t *, - unsigned int); -void alsa_driver_clock_sync_notify (alsa_driver_t *, channel_t chn, - ClockSyncStatus); - int alsa_driver_reset_parameters (alsa_driver_t *driver, jack_nframes_t frames_per_cycle, @@ -230,37 +328,31 @@ alsa_driver_reset_parameters (alsa_driver_t *driver, jack_nframes_t rate); jack_driver_t * -alsa_driver_new (char *name, char *playback_alsa_device, - char *capture_alsa_device, - jack_client_t *client, - jack_nframes_t frames_per_cycle, - jack_nframes_t user_nperiods, - jack_nframes_t rate, - int hw_monitoring, - int hw_metering, - int capturing, - int playing, - DitherAlgorithm dither, - int soft_mode, - int monitor, - int user_capture_nchnls, - int user_playback_nchnls, - int shorts_first, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency, - alsa_midi_t *midi_driver - ); +alsa_driver_new (char *name, alsa_driver_info_t info, jack_client_t *client); + void alsa_driver_delete (alsa_driver_t *driver); +int +alsa_driver_open (alsa_driver_t *driver); + int alsa_driver_start (alsa_driver_t *driver); int alsa_driver_stop (alsa_driver_t *driver); +int +alsa_driver_close (alsa_driver_t *driver); + +typedef enum { + ALSA_DRIVER_WAIT_OK = 0, + ALSA_DRIVER_WAIT_ERROR = -1, + ALSA_DRIVER_WAIT_XRUN = -2, +} alsa_driver_wait_status_t; + jack_nframes_t -alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float +alsa_driver_wait (alsa_driver_t *driver, int extra_fd, alsa_driver_wait_status_t *status, float *delayed_usecs); int @@ -269,15 +361,17 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes); int alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes); -jack_time_t jack_get_microseconds(void); +int +alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs); // Code implemented in JackAlsaDriver.cpp -void ReadInput(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread); +void ReadInput(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread); void MonitorInput(); void ClearOutput(); -void WriteOutput(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten); +void WriteOutput(alsa_device_t *device, jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nwritten); void SetTime(jack_time_t time); + int Restart(); #ifdef __cplusplus diff --git a/linux/alsa/generic.h b/linux/alsa/generic.h index 4458aea4..d228d86f 100644 --- a/linux/alsa/generic.h +++ b/linux/alsa/generic.h @@ -27,7 +27,7 @@ extern "C" #endif jack_hardware_t * - jack_alsa_generic_hw_new (alsa_driver_t *driver); + jack_alsa_generic_hw_new (alsa_device_t *device); #ifdef __cplusplus } diff --git a/linux/alsa/generic_hw.c b/linux/alsa/generic_hw.c index 30e7f340..ccf24688 100644 --- a/linux/alsa/generic_hw.c +++ b/linux/alsa/generic_hw.c @@ -38,7 +38,7 @@ generic_release (jack_hardware_t *hw) } jack_hardware_t * -jack_alsa_generic_hw_new (alsa_driver_t *driver) +jack_alsa_generic_hw_new (alsa_device_t *device) { jack_hardware_t *hw; diff --git a/linux/alsa/hammerfall.c b/linux/alsa/hammerfall.c index 52354070..fe862d18 100644 --- a/linux/alsa/hammerfall.c +++ b/linux/alsa/hammerfall.c @@ -62,7 +62,7 @@ hammerfall_broadcast_channel_status_change (hammerfall_t *h, int lock, int sync, } for (chn = lowchn; chn < highchn; chn++) { - alsa_driver_set_clock_sync_status (h->driver, chn, status); + alsa_driver_set_clock_sync_status (h->device, chn, status); } } @@ -87,8 +87,8 @@ hammerfall_check_sync_state (hammerfall_t *h, int val, int adat_id) /* XXX broken! fix for hammerfall light ! */ - alsa_driver_set_clock_sync_status (h->driver, 24, status); - alsa_driver_set_clock_sync_status (h->driver, 25, status); + alsa_driver_set_clock_sync_status (h->device, 24, status); + alsa_driver_set_clock_sync_status (h->device, 25, status); h->said_that_spdif_is_fine = TRUE; } @@ -153,7 +153,7 @@ hammerfall_set_input_monitor_mask (jack_hardware_t *hw, unsigned long mask) snd_ctl_elem_value_set_integer (ctl, i, (mask & (1<driver->ctl_handle, ctl)) != 0) { + if ((err = snd_ctl_elem_write (h->device->ctl_handle, ctl)) != 0) { jack_error ("ALSA/Hammerfall: cannot set input monitoring (%s)", snd_strerror (err)); return -1; } @@ -188,7 +188,7 @@ hammerfall_change_sample_clock (jack_hardware_t *hw, SampleClockMode mode) break; } - if ((err = snd_ctl_elem_write (h->driver->ctl_handle, ctl)) < 0) { + if ((err = snd_ctl_elem_write (h->device->ctl_handle, ctl)) < 0) { jack_error ("ALSA-Hammerfall: cannot set clock mode"); } @@ -244,17 +244,17 @@ hammerfall_monitor_controls (void *arg) snd_ctl_elem_value_set_id (sw[2], switch_id[2]); while (1) { - if (snd_ctl_elem_read (h->driver->ctl_handle, sw[0])) { + if (snd_ctl_elem_read (h->device->ctl_handle, sw[0])) { jack_error ("cannot read control switch 0 ..."); } hammerfall_check_sync (h, sw[0]); - if (snd_ctl_elem_read (h->driver->ctl_handle, sw[1])) { + if (snd_ctl_elem_read (h->device->ctl_handle, sw[1])) { jack_error ("cannot read control switch 0 ..."); } hammerfall_check_sync (h, sw[1]); - if (snd_ctl_elem_read (h->driver->ctl_handle, sw[2])) { + if (snd_ctl_elem_read (h->device->ctl_handle, sw[2])) { jack_error ("cannot read control switch 0 ..."); } hammerfall_check_sync (h, sw[2]); @@ -269,7 +269,7 @@ hammerfall_monitor_controls (void *arg) #endif /* HAMMERFALL_MONITOR_CONTROLS */ jack_hardware_t * -jack_alsa_hammerfall_hw_new (alsa_driver_t *driver) +jack_alsa_hammerfall_hw_new (alsa_device_t *device) { jack_hardware_t *hw; hammerfall_t *h; @@ -293,7 +293,7 @@ jack_alsa_hammerfall_hw_new (alsa_driver_t *driver) h->lock_status[2] = FALSE; h->sync_status[2] = FALSE; h->said_that_spdif_is_fine = FALSE; - h->driver = driver; + h->device = device; h->monitor_interval.tv_sec = 1; h->monitor_interval.tv_nsec = 0; diff --git a/linux/alsa/hammerfall.h b/linux/alsa/hammerfall.h index aeb6eb76..8b8b4eab 100644 --- a/linux/alsa/hammerfall.h +++ b/linux/alsa/hammerfall.h @@ -29,7 +29,7 @@ typedef struct int sync_status[3]; int said_that_spdif_is_fine; pthread_t monitor_thread; - alsa_driver_t *driver; + alsa_device_t *device; struct timespec monitor_interval; } hammerfall_t; @@ -39,7 +39,7 @@ extern "C" { #endif - jack_hardware_t *jack_alsa_hammerfall_hw_new (alsa_driver_t *driver); + jack_hardware_t *jack_alsa_hammerfall_hw_new (alsa_device_t *device); #ifdef __cplusplus } diff --git a/linux/alsa/hdsp.c b/linux/alsa/hdsp.c index b399e17f..eb222d29 100644 --- a/linux/alsa/hdsp.c +++ b/linux/alsa/hdsp.c @@ -115,7 +115,7 @@ static int hdsp_set_mixer_gain(jack_hardware_t *hw, int input_channel, snd_ctl_elem_value_set_integer (ctl, 2, gain); /* Commit the mixer value and check for errors */ - if ((err = snd_ctl_elem_write (h->driver->ctl_handle, ctl)) != 0) { + if ((err = snd_ctl_elem_write (h->device->ctl_handle, ctl)) != 0) { jack_error ("ALSA/HDSP: cannot set mixer gain (%s)", snd_strerror (err)); return -1; } @@ -206,7 +206,7 @@ hdsp_release (jack_hardware_t *hw) /* Mostly copied directly from hammerfall.c */ jack_hardware_t * -jack_alsa_hdsp_hw_new (alsa_driver_t *driver) +jack_alsa_hdsp_hw_new (alsa_device_t *device) { jack_hardware_t *hw; hdsp_t *h; @@ -227,7 +227,7 @@ jack_alsa_hdsp_hw_new (alsa_driver_t *driver) hw->get_hardware_power = hdsp_get_hardware_power; h = (hdsp_t *) malloc (sizeof (hdsp_t)); - h->driver = driver; + h->device = device; hw->private_hw = h; return hw; diff --git a/linux/alsa/hdsp.h b/linux/alsa/hdsp.h index fe5df84a..c083fca0 100644 --- a/linux/alsa/hdsp.h +++ b/linux/alsa/hdsp.h @@ -25,7 +25,7 @@ typedef struct { - alsa_driver_t *driver; + alsa_device_t *device; } hdsp_t; @@ -35,7 +35,7 @@ extern "C" #endif jack_hardware_t * - jack_alsa_hdsp_hw_new (alsa_driver_t *driver); + jack_alsa_hdsp_hw_new (alsa_device_t *device); #ifdef __cplusplus } diff --git a/linux/alsa/ice1712.c b/linux/alsa/ice1712.c index 96892210..297da659 100644 --- a/linux/alsa/ice1712.c +++ b/linux/alsa/ice1712.c @@ -47,7 +47,7 @@ ice1712_hw_monitor_toggle(jack_hardware_t *hw, int idx, int onoff) } else { snd_ctl_elem_value_set_enumerated (val, 0, 0); } - if ((err = snd_ctl_elem_write (h->driver->ctl_handle, val)) != 0) { + if ((err = snd_ctl_elem_write (h->device->ctl_handle, val)) != 0) { jack_error ("ALSA/ICE1712: (%d) cannot set input monitoring (%s)", idx,snd_strerror (err)); return -1; @@ -94,7 +94,7 @@ ice1712_release (jack_hardware_t *hw) jack_hardware_t * -jack_alsa_ice1712_hw_new (alsa_driver_t *driver) +jack_alsa_ice1712_hw_new (alsa_device_t *device) { jack_hardware_t *hw; ice1712_t *h; @@ -113,14 +113,14 @@ jack_alsa_ice1712_hw_new (alsa_driver_t *driver) h = (ice1712_t *) malloc (sizeof (ice1712_t)); - h->driver = driver; + h->device = device; /* Get the EEPROM (adopted from envy24control) */ h->eeprom = (ice1712_eeprom_t *) malloc (sizeof (ice1712_eeprom_t)); snd_ctl_elem_value_alloca (&val); snd_ctl_elem_value_set_interface (val, SND_CTL_ELEM_IFACE_CARD); snd_ctl_elem_value_set_name (val, "ICE1712 EEPROM"); - if ((err = snd_ctl_elem_read (driver->ctl_handle, val)) < 0) { + if ((err = snd_ctl_elem_read (device->ctl_handle, val)) < 0) { jack_error( "ALSA/ICE1712: Unable to read EEPROM contents (%s)\n", snd_strerror (err)); /* Recover? */ } diff --git a/linux/alsa/ice1712.h b/linux/alsa/ice1712.h index fb422012..7b67e490 100644 --- a/linux/alsa/ice1712.h +++ b/linux/alsa/ice1712.h @@ -59,7 +59,7 @@ ice1712_eeprom_t; typedef struct { - alsa_driver_t *driver; + alsa_device_t *device; ice1712_eeprom_t *eeprom; unsigned long active_channels; } @@ -70,7 +70,7 @@ extern "C" { #endif - jack_hardware_t *jack_alsa_ice1712_hw_new (alsa_driver_t *driver); + jack_hardware_t *jack_alsa_ice1712_hw_new (alsa_device_t *device); #ifdef __cplusplus } diff --git a/linux/alsa/usx2y.h b/linux/alsa/usx2y.h index 522d19a8..51a82a0f 100644 --- a/linux/alsa/usx2y.h +++ b/linux/alsa/usx2y.h @@ -22,6 +22,8 @@ #ifndef __jack_usx2y_h__ #define __jack_usx2y_h__ + #include + #define USX2Y_MAXPACK 50 #define USX2Y_MAXBUFFERMS 100 #define USX2Y_MAXSTRIDE 3 @@ -51,7 +53,9 @@ typedef struct snd_usX2Y_hwdep_pcm_shm snd_usX2Y_hwdep_pcm_shm_t; typedef struct { alsa_driver_t *driver; +#ifndef __QNXNTO__ snd_hwdep_t *hwdep_handle; +#endif struct pollfd pfds; struct snd_usX2Y_hwdep_pcm_shm *hwdep_pcm_shm; int playback_iso_start; diff --git a/posix/JackFifo.h b/posix/JackFifo.h index 8ac72591..8b0a8d78 100644 --- a/posix/JackFifo.h +++ b/posix/JackFifo.h @@ -44,7 +44,7 @@ class SERVER_EXPORT JackFifo : public detail::JackSynchro protected: - void BuildName(const char* name, const char* server_name, char* res); + void BuildName(const char* name, const char* server_name, char* res, int size); public: diff --git a/qnx/JackAtomic_os.h b/qnx/JackAtomic_os.h new file mode 100755 index 00000000..e201d0d6 --- /dev/null +++ b/qnx/JackAtomic_os.h @@ -0,0 +1,86 @@ +/* +Copyright (C) 2004-2008 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackAtomic_linux__ +#define __JackAtomic_linux__ + +#include "JackTypes.h" + +#ifdef __PPC__ + +static inline int CAS(register UInt32 value, register UInt32 newvalue, register volatile void* addr) +{ + register int result; + register UInt32 tmp; + asm volatile ( + "# CAS \n" + " lwarx %4, 0, %1 \n" // creates a reservation on addr + " cmpw %4, %2 \n" // test value at addr + " bne- 1f \n" + " sync \n" // synchronize instructions + " stwcx. %3, 0, %1 \n" // if the reservation is not altered + // stores the new value at addr + " bne- 1f \n" + " li %0, 1 \n" + " b 2f \n" + "1: \n" + " li %0, 0 \n" + "2: \n" + : "=r" (result) + : "r" (addr), "r" (value), "r" (newvalue), "r" (tmp) + ); + return result; +} + +#endif + +#if defined(__i386__) || defined(__x86_64__) + +#define LOCK "lock ; " + +static inline char CAS(volatile UInt32 value, UInt32 newvalue, volatile void* addr) +{ + register char ret; + __asm__ __volatile__ ( + "# CAS \n\t" + LOCK "cmpxchg %2, (%1) \n\t" + "sete %0 \n\t" + : "=a" (ret) + : "c" (addr), "d" (newvalue), "a" (value) + ); + return ret; +} + +#endif + + + + +#if !defined(__i386__) && !defined(__x86_64__) && !defined(__PPC__) + + +static inline char CAS(volatile UInt32 value, UInt32 newvalue, volatile void* addr) +{ + return __sync_bool_compare_and_swap ((UInt32*)addr, value, newvalue); +} +#endif + + +#endif + diff --git a/qnx/JackLinuxTime.c b/qnx/JackLinuxTime.c new file mode 100755 index 00000000..a5ece765 --- /dev/null +++ b/qnx/JackLinuxTime.c @@ -0,0 +1,215 @@ +/* +Copyright (C) 2001-2003 Paul Davis +Copyright (C) 2005 Jussi Laako +Copyright (C) 2004-2008 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "JackConstants.h" +#include "JackTime.h" +#include "JackTypes.h" +#include "JackError.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +jack_time_t (*_jack_get_microseconds)(void) = 0; + +#if defined(__gnu_linux__) && (defined(__i386__) || defined(__x86_64__)) +#define HPET_SUPPORT +#define HPET_MMAP_SIZE 1024 +#define HPET_CAPS 0x000 +#define HPET_PERIOD 0x004 +#define HPET_COUNTER 0x0f0 +#define HPET_CAPS_COUNTER_64BIT (1 << 13) +#if defined(__x86_64__) +typedef uint64_t hpet_counter_t; +#else +typedef uint32_t hpet_counter_t; +#endif +static int hpet_fd; +static unsigned char *hpet_ptr; +static uint32_t hpet_period; /* period length in femto secs */ +static uint64_t hpet_offset = 0; +static uint64_t hpet_wrap; +static hpet_counter_t hpet_previous = 0; +#endif /* defined(__gnu_linux__) && (__i386__ || __x86_64__) */ + +#ifdef HPET_SUPPORT + +static int jack_hpet_init () +{ + uint32_t hpet_caps; + + hpet_fd = open("/dev/hpet", O_RDONLY); + if (hpet_fd < 0) { + jack_error ("This system has no accessible HPET device (%s)", strerror (errno)); + return -1; + } + + hpet_ptr = (unsigned char *) mmap(NULL, HPET_MMAP_SIZE, + PROT_READ, MAP_SHARED, hpet_fd, 0); + if (hpet_ptr == MAP_FAILED) { + jack_error ("This system has no mappable HPET device (%s)", strerror (errno)); + close (hpet_fd); + return -1; + } + + /* this assumes period to be constant. if needed, + it can be moved to the clock access function + */ + hpet_period = *((uint32_t *) (hpet_ptr + HPET_PERIOD)); + hpet_caps = *((uint32_t *) (hpet_ptr + HPET_CAPS)); + hpet_wrap = ((hpet_caps & HPET_CAPS_COUNTER_64BIT) && + (sizeof(hpet_counter_t) == sizeof(uint64_t))) ? + 0 : ((uint64_t) 1 << 32); + + return 0; +} + +static jack_time_t jack_get_microseconds_from_hpet (void) +{ + hpet_counter_t hpet_counter; + long double hpet_time; + + hpet_counter = *((hpet_counter_t *) (hpet_ptr + HPET_COUNTER)); + if (hpet_counter < hpet_previous) + hpet_offset += hpet_wrap; + hpet_previous = hpet_counter; + hpet_time = (long double) (hpet_offset + hpet_counter) * + (long double) hpet_period * (long double) 1e-9; + return ((jack_time_t) (hpet_time + 0.5)); +} + +#else + +static int jack_hpet_init () +{ + jack_error ("This version of JACK or this computer does not have HPET support.\n" + "Please choose a different clock source."); + return -1; +} + +static jack_time_t jack_get_microseconds_from_hpet (void) +{ + /* never called */ + return 0; +} + +#endif /* HPET_SUPPORT */ + +#define HAVE_CLOCK_GETTIME 1 + +#ifndef HAVE_CLOCK_GETTIME + +static jack_time_t jack_get_microseconds_from_system (void) +{ + jack_time_t jackTime; + struct timeval tv; + + gettimeofday (&tv, NULL); + jackTime = (jack_time_t) tv.tv_sec * 1000000 + (jack_time_t) tv.tv_usec; + return jackTime; +} + +#else + +static jack_time_t jack_get_microseconds_from_system (void) +{ + jack_time_t jackTime; + struct timespec time; + + clock_gettime(CLOCK_MONOTONIC, &time); + jackTime = (jack_time_t) time.tv_sec * 1e6 + + (jack_time_t) time.tv_nsec / 1e3; + return jackTime; +} + +#endif /* HAVE_CLOCK_GETTIME */ + + +SERVER_EXPORT void JackSleep(long usec) +{ + usleep(usec); +} + +SERVER_EXPORT void InitTime() +{ + /* nothing to do on a generic system - we use the system clock */ +} + +SERVER_EXPORT void EndTime() +{} + +void SetClockSource(jack_timer_type_t source) +{ + jack_log("Clock source : %s", ClockSourceName(source)); + + switch (source) + { + case JACK_TIMER_HPET: + if (jack_hpet_init () == 0) { + _jack_get_microseconds = jack_get_microseconds_from_hpet; + } else { + _jack_get_microseconds = jack_get_microseconds_from_system; + } + break; + + case JACK_TIMER_SYSTEM_CLOCK: + default: + _jack_get_microseconds = jack_get_microseconds_from_system; + break; + } +} + +const char* ClockSourceName(jack_timer_type_t source) +{ + switch (source) { + case JACK_TIMER_HPET: + return "hpet"; + case JACK_TIMER_SYSTEM_CLOCK: + #ifdef HAVE_CLOCK_GETTIME + return "system clock via clock_gettime"; + #else + return "system clock via gettimeofday"; + #endif + } + + /* what is wrong with gcc ? */ + return "unknown"; +} + +SERVER_EXPORT jack_time_t GetMicroSeconds() +{ + return _jack_get_microseconds(); +} + +SERVER_EXPORT jack_time_t jack_get_microseconds() +{ + return _jack_get_microseconds(); +} + diff --git a/qnx/JackPlatformPlug_os.h b/qnx/JackPlatformPlug_os.h new file mode 100755 index 00000000..c845dca8 --- /dev/null +++ b/qnx/JackPlatformPlug_os.h @@ -0,0 +1,85 @@ +/* +Copyright (C) 2004-2008 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackPlatformPlug_linux__ +#define __JackPlatformPlug_linux__ + +#define jack_server_dir "/dev/shm" +#define jack_client_dir "/dev/shm" +#define JACK_DEFAULT_DRIVER "alsa" + +namespace Jack +{ + struct JackRequest; + struct JackResult; + + class JackPosixMutex; + class JackPosixThread; + class JackFifo; + class JackSocketServerChannel; + class JackSocketClientChannel; + class JackSocketServerNotifyChannel; + class JackSocketNotifyChannel; + class JackClientSocket; + class JackNetUnixSocket; +} + +/* __JackPlatformMutex__ */ +#include "JackPosixMutex.h" +namespace Jack {typedef JackPosixMutex JackMutex; } + +/* __JackPlatformThread__ */ +#include "JackPosixThread.h" +namespace Jack { typedef JackPosixThread JackThread; } + +#include "JackFifo.h" +namespace Jack { typedef JackFifo JackSynchro; } + + +/* __JackPlatformChannelTransaction__ */ +/* +#include "JackSocket.h" +namespace Jack { typedef JackClientSocket JackChannelTransaction; } +*/ + +/* __JackPlatformProcessSync__ */ +#include "JackPosixProcessSync.h" +namespace Jack { typedef JackPosixProcessSync JackProcessSync; } + +/* __JackPlatformServerChannel__ */ +#include "JackSocketServerChannel.h" +namespace Jack { typedef JackSocketServerChannel JackServerChannel; } + +/* __JackPlatformClientChannel__ */ +#include "JackSocketClientChannel.h" +namespace Jack { typedef JackSocketClientChannel JackClientChannel; } + +/* __JackPlatformServerNotifyChannel__ */ +#include "JackSocketServerNotifyChannel.h" +namespace Jack { typedef JackSocketServerNotifyChannel JackServerNotifyChannel; } + +/* __JackPlatformNotifyChannel__ */ +#include "JackSocketNotifyChannel.h" +namespace Jack { typedef JackSocketNotifyChannel JackNotifyChannel; } + +/* __JackPlatformNetSocket__ */ +#include "JackNetUnixSocket.h" +namespace Jack { typedef JackNetUnixSocket JackNetSocket; } + +#endif diff --git a/qnx/driver.h b/qnx/driver.h new file mode 100755 index 00000000..6e1466f4 --- /dev/null +++ b/qnx/driver.h @@ -0,0 +1,300 @@ +/* + Copyright (C) 2001 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: driver.h,v 1.2 2005/11/23 11:24:29 letz Exp $ +*/ + +#ifndef __jack_driver_h__ +#define __jack_driver_h__ + +#include +#include "types.h" +#include "jslist.h" +#include "driver_interface.h" + +typedef float gain_t; +typedef long channel_t; + +typedef enum { + Lock = 0x1, + NoLock = 0x2, + Sync = 0x4, + NoSync = 0x8 +} ClockSyncStatus; + +typedef void (*ClockSyncListenerFunction)(channel_t, ClockSyncStatus, void*); + +typedef struct +{ + unsigned long id; + ClockSyncListenerFunction function; + void *arg; +} +ClockSyncListener; + +struct _jack_engine; +struct _jack_driver; + +typedef int (*JackDriverAttachFunction)(struct _jack_driver *, + struct _jack_engine *); +typedef int (*JackDriverDetachFunction)(struct _jack_driver *, + struct _jack_engine *); +typedef int (*JackDriverReadFunction)(struct _jack_driver *, + jack_nframes_t nframes); +typedef int (*JackDriverWriteFunction)(struct _jack_driver *, + jack_nframes_t nframes); +typedef int (*JackDriverNullCycleFunction)(struct _jack_driver *, + jack_nframes_t nframes); +typedef int (*JackDriverStopFunction)(struct _jack_driver *); +typedef int (*JackDriverStartFunction)(struct _jack_driver *); +typedef int (*JackDriverBufSizeFunction)(struct _jack_driver *, + jack_nframes_t nframes); +/* + Call sequence summary: + + 1) engine loads driver via runtime dynamic linking + - calls jack_driver_load + - we call dlsym for "driver_initialize" and execute it + 2) engine attaches to driver + 3) engine starts driver + 4) driver runs its own thread, calling + while () { + driver->wait (); + driver->engine->run_cycle () + } + 5) engine stops driver + 6) engine detaches from driver + 7) engine calls driver `finish' routine + + Note that stop/start may be called multiple times in the event of an + error return from the `wait' function. +*/ + +typedef struct _jack_driver +{ + + /* The _jack_driver structure fields are included at the beginning of + each driver-specific structure using the JACK_DRIVER_DECL macro, + which is defined below. The comments that follow describe each + common field. + + The driver should set this to be the interval it expects to elapse + between returning from the `wait' function. if set to zero, it + implies that the driver does not expect regular periodic wakeups. + + jack_time_t period_usecs; + + + The driver should set this within its "wait" function to indicate + the UST of the most recent determination that the engine cycle + should run. it should not be set if the "extra_fd" argument of + the wait function is set to a non-zero value. + + jack_time_t last_wait_ust; + + + These are not used by the driver. They should not be written to or + modified in any way + + void *handle; + struct _jack_internal_client *internal_client; + + This should perform any cleanup associated with the driver. it will + be called when jack server process decides to get rid of the + driver. in some systems, it may not be called at all, so the driver + should never rely on a call to this. it can set it to NULL if + it has nothing do do. + + void (*finish)(struct _jack_driver *); + + + The JACK engine will call this when it wishes to attach itself to + the driver. the engine will pass a pointer to itself, which the driver + may use in anyway it wishes to. the driver may assume that this + is the same engine object that will make `wait' calls until a + `detach' call is made. + + JackDriverAttachFunction attach; + + + The JACK engine will call this when it is finished using a driver. + + JackDriverDetachFunction detach; + + + The JACK engine will call this when it wants to wait until the + driver decides that its time to process some data. the driver returns + a count of the number of audioframes that can be processed. + + it should set the variable pointed to by `status' as follows: + + zero: the wait completed normally, processing may begin + negative: the wait failed, and recovery is not possible + positive: the wait failed, and the driver stopped itself. + a call to `start' will return the driver to + a correct and known state. + + the driver should also fill out the `delayed_usecs' variable to + indicate any delay in its expected periodic execution. for example, + if it discovers that its return from poll(2) is later than it + expects it to be, it would place an estimate of the delay + in this variable. the engine will use this to decide if it + plans to continue execution. + + JackDriverWaitFunction wait; + + + The JACK engine will call this to ask the driver to move + data from its inputs to its output port buffers. it should + return 0 to indicate successful completion, negative otherwise. + + This function will always be called after the wait function (above). + + JackDriverReadFunction read; + + + The JACK engine will call this to ask the driver to move + data from its input port buffers to its outputs. it should + return 0 to indicate successful completion, negative otherwise. + + this function will always be called after the read function (above). + + JackDriverWriteFunction write; + + + The JACK engine will call this after the wait function (above) has + been called, but for some reason the engine is unable to execute + a full "cycle". the driver should do whatever is necessary to + keep itself running correctly, but cannot reference ports + or other JACK data structures in any way. + + JackDriverNullCycleFunction null_cycle; + + + The engine will call this when it plans to stop calling the `wait' + function for some period of time. the driver should take + appropriate steps to handle this (possibly no steps at all). + NOTE: the driver must silence its capture buffers (if any) + from within this function or the function that actually + implements the change in state. + + JackDriverStopFunction stop; + + + The engine will call this to let the driver know that it plans + to start calling the `wait' function on a regular basis. the driver + should take any appropriate steps to handle this (possibly no steps + at all). NOTE: The driver may wish to silence its playback buffers + (if any) from within this function or the function that actually + implements the change in state. + + JackDriverStartFunction start; + + The engine will call this to let the driver know that some client + has requested a new buffer size. The stop function will be called + prior to this, and the start function after this one has returned. + + JackDriverBufSizeFunction bufsize; + */ + + /* define the fields here... */ +#define JACK_DRIVER_DECL \ + jack_time_t period_usecs; \ + jack_time_t last_wait_ust; \ + void *handle; \ + struct _jack_client_internal * internal_client; \ + void (*finish)(struct _jack_driver *);\ + JackDriverAttachFunction attach; \ + JackDriverDetachFunction detach; \ + JackDriverReadFunction read; \ + JackDriverWriteFunction write; \ + JackDriverNullCycleFunction null_cycle; \ + JackDriverStopFunction stop; \ + JackDriverStartFunction start; \ + JackDriverBufSizeFunction bufsize; + + JACK_DRIVER_DECL /* expand the macro */ +} +jack_driver_t; + +void jack_driver_init (jack_driver_t *); +void jack_driver_release (jack_driver_t *); + +jack_driver_t *jack_driver_load (int argc, char **argv); +void jack_driver_unload (jack_driver_t *); + +/**************************** + *** Non-Threaded Drivers *** + ****************************/ + +/* + Call sequence summary: + + 1) engine loads driver via runtime dynamic linking + - calls jack_driver_load + - we call dlsym for "driver_initialize" and execute it + - driver_initialize calls jack_driver_nt_init + 2) nt layer attaches to driver + 3) nt layer starts driver + 4) nt layer runs a thread, calling + while () { + driver->nt_run_ctcle(); + } + 5) nt layer stops driver + 6) nt layer detaches driver + 7) engine calls driver `finish' routine which calls jack_driver_nt_finish + + Note that stop/start may be called multiple times in the event of an + error return from the `wait' function. +*/ + +struct _jack_driver_nt; + +typedef int (*JackDriverNTAttachFunction)(struct _jack_driver_nt *); +typedef int (*JackDriverNTDetachFunction)(struct _jack_driver_nt *); +typedef int (*JackDriverNTStopFunction)(struct _jack_driver_nt *); +typedef int (*JackDriverNTStartFunction)(struct _jack_driver_nt *); +typedef int (*JackDriverNTBufSizeFunction)(struct _jack_driver_nt *, + jack_nframes_t nframes); +typedef int (*JackDriverNTRunCycleFunction)(struct _jack_driver_nt *); + +typedef struct _jack_driver_nt +{ +#define JACK_DRIVER_NT_DECL \ + JACK_DRIVER_DECL \ + struct _jack_engine * engine; \ + volatile int nt_run; \ + pthread_t nt_thread; \ + pthread_mutex_t nt_run_lock; \ + JackDriverNTAttachFunction nt_attach; \ + JackDriverNTDetachFunction nt_detach; \ + JackDriverNTStopFunction nt_stop; \ + JackDriverNTStartFunction nt_start; \ + JackDriverNTBufSizeFunction nt_bufsize; \ + JackDriverNTRunCycleFunction nt_run_cycle; +#define nt_read read +#define nt_write write +#define nt_null_cycle null_cycle + + JACK_DRIVER_NT_DECL +} +jack_driver_nt_t; + +void jack_driver_nt_init (jack_driver_nt_t * driver); +void jack_driver_nt_finish (jack_driver_nt_t * driver); + +#endif /* __jack_driver_h__ */ diff --git a/tests/wscript b/tests/wscript index 96a63bc9..0dcae8c3 100644 --- a/tests/wscript +++ b/tests/wscript @@ -20,6 +20,8 @@ def build(bld): prog.includes = ['..','../macosx', '../posix', '../common/jack', '../common'] if bld.env['IS_LINUX']: prog.includes = ['..','../linux', '../posix', '../common/jack', '../common'] + if bld.env['IS_QNX']: + prog.includes = ['..','../qnx', '../posix', '../common/jack', '../common'] if bld.env['IS_SUN']: prog.includes = ['..','../solaris', '../posix', '../common/jack', '../common'] prog.source = test_program_sources diff --git a/wscript b/wscript old mode 100644 new mode 100755 index 14e561a3..7d46528f --- a/wscript +++ b/wscript @@ -95,6 +95,15 @@ def options(opt): alsa.check_cfg( package='alsa >= 1.0.18', args='--cflags --libs') + # Check for QNX sound headers + qsa = opt.add_auto_option( + 'qsa', + help='Enable QNX sound driver', + conf_dest='BUILD_DRIVER_ALSA') + qsa.check(lib='asound', uselib_store='ALSA') + qsa.check( + header_name=['sys/asoundlib.h'], + msg='Checking for header sys/asoundlib.h') firewire = opt.add_auto_option( 'firewire', help='Enable FireWire driver (FFADO)', @@ -185,6 +194,7 @@ def detect_platform(conf): platforms = [ # ('KEY, 'Human readable name', ['strings', 'to', 'check', 'for']) ('IS_LINUX', 'Linux', ['gnu0', 'gnukfreebsd', 'linux', 'posix']), + ('IS_QNX', 'QNX', ['qnx']), ('IS_MACOSX', 'MacOS X', ['darwin']), ('IS_SUN', 'SunOS', ['sunos']), ('IS_WINDOWS', 'Windows', ['cygwin', 'msys', 'win32']) @@ -304,7 +314,11 @@ def configure(conf): conf.check_cc(fragment=fragment, define_name='HAVE_NGREG', mandatory=False, msg='Checking for NGREG') - conf.env['LIB_PTHREAD'] = ['pthread'] + if conf.env['IS_QNX']: + conf.env['LIB_PTHREAD'] = ['c'] + conf.env['LIB_SOCKET'] = ['socket'] + else: + conf.env['LIB_PTHREAD'] = ['pthread'] conf.env['LIB_DL'] = ['dl'] conf.env['LIB_RT'] = ['rt'] conf.env['LIB_M'] = ['m'] @@ -512,6 +526,9 @@ def obj_add_includes(bld, obj): if bld.env['IS_MACOSX']: obj.includes += ['macosx', 'posix'] + if bld.env['IS_QNX']: + obj.includes += ['qnx', 'posix'] + if bld.env['IS_SUN']: obj.includes += ['posix', 'solaris'] @@ -540,6 +557,10 @@ def build_jackd(bld): jackd.use += ['DL', 'PTHREAD'] jackd.framework = ['CoreFoundation'] + if bld.env['IS_QNX']: + jackd.use += ['M', 'PTHREAD'] + + if bld.env['IS_SUN']: jackd.use += ['DL', 'PTHREAD'] @@ -598,16 +619,20 @@ def build_drivers(bld): alsa_src = [ 'common/memops.c', 'linux/alsa/JackAlsaDriver.cpp', - 'linux/alsa/alsa_rawmidi.c', - 'linux/alsa/alsa_seqmidi.c', - 'linux/alsa/alsa_midi_jackmp.cpp', - 'linux/alsa/generic_hw.c', - 'linux/alsa/hdsp.c', - 'linux/alsa/alsa_driver.c', - 'linux/alsa/hammerfall.c', - 'linux/alsa/ice1712.c' + 'linux/alsa/alsa_driver.c' ] + if not bld.env['IS_QNX']: + alsa_src += [ + 'linux/alsa/alsa_rawmidi.c', + 'linux/alsa/alsa_seqmidi.c', + 'linux/alsa/alsa_midi_jackmp.cpp', + 'linux/alsa/generic_hw.c', + 'linux/alsa/hdsp.c', + 'linux/alsa/hammerfall.c', + 'linux/alsa/ice1712.c' + ] + alsarawmidi_src = [ 'linux/alsarawmidi/JackALSARawMidiDriver.cpp', 'linux/alsarawmidi/JackALSARawMidiInputPort.cpp', @@ -704,11 +729,12 @@ def build_drivers(bld): target = 'alsa', source = alsa_src, use = ['ALSA']) - create_driver_obj( - bld, - target = 'alsarawmidi', - source = alsarawmidi_src, - use = ['ALSA']) + if not bld.env['IS_QNX']: + create_driver_obj( + bld, + target = 'alsarawmidi', + source = alsarawmidi_src, + use = ['ALSA']) if bld.env['BUILD_DRIVER_FFADO']: create_driver_obj(