| @@ -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 | |||
| @@ -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(); | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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); | |||
| @@ -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) | |||
| @@ -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; | |||
| } | |||
| @@ -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); | |||
| @@ -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++) | |||
| { | |||
| @@ -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; | |||
| @@ -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(); | |||
| @@ -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); | |||
| @@ -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); | |||
| @@ -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 | |||
| @@ -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; | |||
| @@ -430,6 +430,11 @@ error: | |||
| return -1; | |||
| } | |||
| int JackServer::ReloadMaster() | |||
| { | |||
| return fAudioDriver->Reload(); | |||
| } | |||
| //---------------------- | |||
| // Transport management | |||
| //---------------------- | |||
| @@ -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(); | |||
| @@ -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); | |||
| @@ -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); | |||
| @@ -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 | |||
| @@ -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 <stdint.h> | |||
| @@ -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)) | |||
| @@ -49,11 +49,16 @@ | |||
| #include <sys/mman.h> | |||
| #include <sys/types.h> | |||
| #include <sys/stat.h> | |||
| #include <sys/shm.h> | |||
| #include <sys/sem.h> | |||
| #include <stdlib.h> | |||
| #include "promiscuous.h" | |||
| #ifdef __QNXNTO__ | |||
| #include <sys/mman.h> | |||
| #else | |||
| #include <sys/shm.h> | |||
| #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 <psapi.h> | |||
| @@ -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 <semaphore.h> | |||
| 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 */ | |||
| @@ -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') | |||
| @@ -0,0 +1,32 @@ | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <jack/jack.h> | |||
| 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; | |||
| } | |||
| @@ -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'] | |||
| @@ -32,6 +32,11 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
| #include <sys/types.h> | |||
| #include <sys/time.h> | |||
| #include <string.h> | |||
| #include <sstream> | |||
| #include <fstream> | |||
| #include <algorithm> | |||
| #include <cctype> | |||
| #include <vector> | |||
| #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<char*> 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; | |||
| } | |||
| @@ -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); | |||
| @@ -21,7 +21,12 @@ | |||
| #ifndef __jack_alsa_driver_h__ | |||
| #define __jack_alsa_driver_h__ | |||
| #ifdef __QNXNTO__ | |||
| #include <sys/asoundlib.h> | |||
| #else | |||
| #include <alsa/asoundlib.h> | |||
| #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 | |||
| @@ -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 | |||
| } | |||
| @@ -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; | |||
| @@ -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<<i)) ? 1 : 0); | |||
| } | |||
| 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 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; | |||
| @@ -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 | |||
| } | |||
| @@ -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; | |||
| @@ -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 | |||
| } | |||
| @@ -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? */ | |||
| } | |||
| @@ -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 | |||
| } | |||
| @@ -22,6 +22,8 @@ | |||
| #ifndef __jack_usx2y_h__ | |||
| #define __jack_usx2y_h__ | |||
| #include <poll.h> | |||
| #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; | |||
| @@ -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: | |||
| @@ -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 | |||
| @@ -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 <stdint.h> | |||
| #include <stdio.h> | |||
| #include <sys/mman.h> | |||
| #include <sys/time.h> | |||
| #include <sys/types.h> | |||
| #include <sys/stat.h> | |||
| #include <fcntl.h> | |||
| #include <errno.h> | |||
| #include <string.h> | |||
| #include <unistd.h> | |||
| #include <stdlib.h> | |||
| #include <inttypes.h> | |||
| 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(); | |||
| } | |||
| @@ -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 | |||
| @@ -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 <pthread.h> | |||
| #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__ */ | |||
| @@ -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 | |||
| @@ -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( | |||