diff --git a/common/JackAudioDriver.cpp b/common/JackAudioDriver.cpp index 52ab00a8..b38aed16 100644 --- a/common/JackAudioDriver.cpp +++ b/common/JackAudioDriver.cpp @@ -48,7 +48,7 @@ int JackAudioDriver::SetBufferSize(jack_nframes_t buffer_size) fGraphManager->SetBufferSize(buffer_size); fEngineControl->UpdateTimeOut(); - UpdateLatencies(); + update_latencies(); // Redirected on slaves drivers... return JackDriver::SetBufferSize(buffer_size); @@ -85,7 +85,7 @@ int JackAudioDriver::Open(jack_nframes_t buffer_size, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency); } -void JackAudioDriver::UpdateLatencies() +void JackAudioDriver::update_latencies() { jack_latency_range_t input_range; jack_latency_range_t output_range; @@ -159,7 +159,7 @@ int JackAudioDriver::Attach() } } - UpdateLatencies(); + update_latencies(); return 0; } diff --git a/common/JackAudioDriver.h b/common/JackAudioDriver.h index e6f332f0..5b86e1b7 100644 --- a/common/JackAudioDriver.h +++ b/common/JackAudioDriver.h @@ -49,7 +49,7 @@ class SERVER_EXPORT JackAudioDriver : public JackDriver jack_default_audio_sample_t* GetMonitorBuffer(int port_index); void HandleLatencyCallback(int status); - virtual void UpdateLatencies(); + virtual void update_latencies(); int ProcessAsync(); void ProcessGraphAsync(); diff --git a/qnx/JackQnxTime.c b/qnx/JackQnxTime.c index 371e9f0d..0eddd510 100644 --- a/qnx/JackQnxTime.c +++ b/qnx/JackQnxTime.c @@ -208,9 +208,3 @@ SERVER_EXPORT jack_time_t GetMicroSeconds() { return _jack_get_microseconds(); } - -SERVER_EXPORT jack_time_t jack_get_microseconds() -{ - return _jack_get_microseconds(); -} - diff --git a/qnx/driver.h b/qnx/driver.h new file mode 100644 index 00000000..f4bdb878 --- /dev/null +++ b/qnx/driver.h @@ -0,0 +1,309 @@ +/* + Copyright (C) 2001 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: driver.h,v 1.2 2005/11/23 11:24:29 letz Exp $ +*/ + +#ifndef __jack_driver_h__ +#define __jack_driver_h__ + +#include +#include "types.h" +#include "jslist.h" +#include "driver_interface.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +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); + +#ifdef __cplusplus +} +#endif + +#endif /* __jack_driver_h__ */ diff --git a/qnx/ioaudio/JackIoAudioDriver.cpp b/qnx/ioaudio/JackIoAudioDriver.cpp new file mode 100644 index 00000000..d6c203f8 --- /dev/null +++ b/qnx/ioaudio/JackIoAudioDriver.cpp @@ -0,0 +1,2323 @@ +/* + Copyright (C) 2001 Paul Davis + Copyright (C) 2004 Grame + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#define __STDC_FORMAT_MACROS // For inttypes.h to work in C++ +#define _GNU_SOURCE /* for strcasestr() from string.h */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "bitset.h" + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define IS_LE 0 +#define IS_BE 1 +#elif __BYTE_ORDER == __BIG_ENDIAN +#define IS_LE 1 +#define IS_BE 0 +#endif + +#include "types.h" +#include "hardware.h" +#include "memops.h" + +#include "JackIoAudioDriver.h" +#include "JackEngineControl.h" +#include "JackClientControl.h" +#include "JackPort.h" +#include "JackGraphManager.h" +#include "JackLockedEngine.h" +#include "JackPosixThread.h" +#include "JackCompilerDeps.h" +#include "JackServerGlobals.h" + +#include "JackError.h" + +static struct jack_constraint_enum_char_descriptor dither_constraint_descr_array[] = + { { 'n', "none" }, { 'r', "rectangular" }, { 's', "shaped" }, + { 't', "triangular" }, { 0 } }; + +namespace Jack +{ + +/////////////////////////////////////////////////////////////////////////////// +// CONSTANTS +/////////////////////////////////////////////////////////////////////////////// + +#undef DEBUG_WAKEUP + + /* Delay (in process calls) before jackd will report an xrun */ +#define XRUN_REPORT_DELAY 0 + +/////////////////////////////////////////////////////////////////////////////// +// TYPES +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// PROTOTYPES +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATIONS +/////////////////////////////////////////////////////////////////////////////// + + class generic_hardware: public jack_hardware_t + { + virtual ~generic_hardware() + { + } + + virtual double get_hardware_peak( + jack_port_t *port, + jack_nframes_t frames ) + { + return -1; + } + + virtual double get_hardware_power( + jack_port_t *port, + jack_nframes_t frames ) + { + return -1; + } + + virtual int set_input_monitor_mask( + unsigned long mask ) + { + return -1; + } + + virtual int change_sample_clock( + SampleClockMode mode ) + { + return -1; + } + + virtual void release() + { + return; + } + + }; + + JackIoAudioDriver::JackIoAudioDriver( + Args args, + JackLockedEngine* engine, + JackSynchro* table ) : + JackAudioDriver( args.jack_name, + args.jack_alias, + engine, + table ), + fArgs( args ) + { + + jack_log( "creating ioaudio driver ... %s|%s|%" PRIu32 "|%" PRIu32 + "|%" PRIu32"|%" PRIu32"|%" PRIu32 "|%s|%s|%s|%s", + fArgs.playback ? fArgs.playback_pcm_name : "-", + fArgs.capture ? fArgs.capture_pcm_name : "-", + fArgs.frames_per_interrupt, + fArgs.user_nperiods, + fArgs.srate, + fArgs.user_capture_nchnls, + fArgs.user_playback_nchnls, + fArgs.hw_monitoring ? "hwmon" : "nomon", + fArgs.hw_metering ? "hwmeter" : "swmeter", + fArgs.soft_mode ? "soft-mode" : "-", + fArgs.shorts_first ? "16bit" : "32bit" ); + } + + JackIoAudioDriver::~JackIoAudioDriver() + { + if( capture.handle ) + { + snd_pcm_close( capture.handle ); + capture.handle = 0; + } + + if( playback.handle ) + { + snd_pcm_close( playback.handle ); + capture.handle = 0; + } + + if( hw ) + { + hw->release(); + delete hw; + hw = 0; + } + + delete[] playback.voices; + delete[] capture.voices; + } + + int JackIoAudioDriver::Attach() + { + JackPort* port; + jack_port_id_t port_index; + unsigned long port_flags = (unsigned long) CaptureDriverFlags; + char name[REAL_JACK_PORT_NAME_SIZE]; + char alias[REAL_JACK_PORT_NAME_SIZE]; + + assert( fCaptureChannels < DRIVER_PORT_NUM ); + assert( fPlaybackChannels < DRIVER_PORT_NUM ); + + if( has_hw_monitoring ) + port_flags |= JackPortCanMonitor; + + // io-audio driver may have changed the values + JackAudioDriver::SetBufferSize( fArgs.frames_per_interrupt ); + JackAudioDriver::SetSampleRate( frame_rate ); + + jack_log( "JackIoAudioDriver::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 ) + { + jack_error( "driver: cannot register port for %s", + name ); + return -1; + } + port = fGraphManager->GetPort( port_index ); + port->SetAlias( alias ); + fCapturePortList[i] = port_index; + jack_log( "JackIoAudioDriver::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( "JackIoAudioDriver::Attach fPlaybackPortList[i] %ld ", + port_index ); + + // 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("io-audio: cannot register monitor port for %s", name); + } + else + { + fMonitorPortList[i] = port_index; + } + } + } + + update_latencies(); + + return 0; + } + + int JackIoAudioDriver::Close() + { + // Generic audio driver close + int res = JackAudioDriver::Close(); + + if( JackServerGlobals::on_device_release != NULL ) + { + char audio_name[32]; + int capture_card = snd_card_name( fCaptureDriverName ); + if( capture_card >= 0 ) + { + snprintf( audio_name, + sizeof( audio_name ), + "Audio%d", + capture_card ); + JackServerGlobals::on_device_release( audio_name ); + } + + int playback_card = snd_card_name( fPlaybackDriverName ); + if( playback_card >= 0 && playback_card != capture_card ) + { + snprintf( audio_name, + sizeof( audio_name ), + "Audio%d", + playback_card ); + JackServerGlobals::on_device_release( audio_name ); + } + } + + return res; + } + + int JackIoAudioDriver::Detach() + { + return JackAudioDriver::Detach(); + } + + int JackIoAudioDriver::Open() + { + int err; + + // Generic JackAudioDriver Open + if( JackAudioDriver::Open( fArgs.frames_per_interrupt, + fArgs.srate, + fArgs.capture, + fArgs.playback, + fArgs.user_capture_nchnls, + fArgs.user_playback_nchnls, + fArgs.monitor, + fArgs.capture_pcm_name, + fArgs.playback_pcm_name, + fArgs.systemic_input_latency, + fArgs.systemic_output_latency ) != 0 ) + { + return -1; + } + + err = check_card_type(); + if( err ) + { + return err; + } + + if( JackServerGlobals::on_device_acquire != NULL ) + { + if( fArgs.capture ) + { + if( !JackServerGlobals::on_device_acquire( fArgs.capture_pcm_name ) ) + { + jack_error( "Audio device %s cannot be acquired...", + fArgs.capture_pcm_name ); + fArgs.capture = false; + } + } + + if( fArgs.playback ) + { + if( !JackServerGlobals::on_device_acquire( fArgs.playback_pcm_name ) ) + { + jack_error( "Audio device %s cannot be acquired...", + fArgs.playback_pcm_name ); + fArgs.playback = false; + if( fArgs.capture ) + { + JackServerGlobals::on_device_release( fArgs.capture_pcm_name ); + } + } + } + } + + hw_specific(); + + if( fArgs.playback ) + { + err = + snd_pcm_open_name( &playback.handle, + fArgs.playback_pcm_name, + SND_PCM_OPEN_PLAYBACK | SND_PCM_OPEN_NONBLOCK ); + if( err < 0 ) + { + switch( errno ) + { + case EBUSY: + jack_error( "\n\nATTENTION: The playback device \"%s\" is " + "already in use. Please stop the" + " application using it and " + "run JACK again", + fArgs.playback_pcm_name ); + return EBUSY; + + case EPERM: + jack_error( "you do not have permission to open " + "the audio device \"%s\" for playback", + fArgs.playback_pcm_name ); + return EPERM; + } + + playback.handle = NULL; + } + + if( playback.handle ) + { + snd_pcm_nonblock_mode( playback.handle, + 0 ); + } + else + { + + /* they asked for playback, but we can't do it */ + + jack_error( "io-audio: Cannot open PCM device %s for " + "playback. Falling back to capture-only" + " mode", + fArgs.playback_pcm_name ); + fArgs.playback = false; + } + + } + + if( fArgs.capture ) + { + err = snd_pcm_open_name( &capture.handle, + fArgs.capture_pcm_name, + SND_PCM_OPEN_CAPTURE | SND_PCM_OPEN_NONBLOCK ); + if( err < 0 ) + { + switch( errno ) + { + case EBUSY: + jack_error( "\n\nATTENTION: The capture (recording) device \"%s\" is " + "already in use. Please stop the" + " application using it and " + "run JACK again", + fArgs.capture_pcm_name ); + break; + + case EPERM: + jack_error( "you do not have permission to open " + "the audio device \"%s\" for capture", + fArgs.capture_pcm_name ); + break; + } + + capture.handle = NULL; + } + + if( capture.handle ) + { + snd_pcm_nonblock_mode( capture.handle, + 0 ); + } + else + { + + /* they asked for capture, but we can't do it */ + + jack_error( "io-audio: Cannot open PCM device %s for " + "capture. Falling back to playback-only" + " mode", + fArgs.capture_pcm_name ); + + fArgs.capture = false; + } + } + + if( playback.handle == NULL && capture.handle == NULL ) + { + /* can't do anything */ + return err; + } + + err = set_parameters(); + if( 0 != err ) + { + return err; + } + + capture_and_playback_not_synced = false; + + if( capture.handle && playback.handle ) + { + if( snd_pcm_link( playback.handle, + capture.handle ) != 0 ) + { + capture_and_playback_not_synced = true; + } + } + + if( err ) + { + JackAudioDriver::Close(); + return -1; + } + else + { + // io-audio driver may have changed the in/out values + fCaptureChannels = capture.setup.format.voices; + fPlaybackChannels = playback.setup.format.voices; + return 0; + } + + } + + int JackIoAudioDriver::Read() + { + /* Taken from ioaudio_driver_run_cycle */ + int wait_status; + jack_nframes_t nframes; + fDelayedUsecs = 0.f; + + retry: + + nframes = wait( &wait_status, + &fDelayedUsecs ); + + if( wait_status < 0 ) + return -1; /* driver failed */ + + if( nframes == 0 ) + { + /* we detected an xrun and restarted: notify + * clients about the delay. + */ + jack_log( "io-audio XRun wait_status = %d", + wait_status ); + NotifyXRun( fBeginDateUst, + fDelayedUsecs ); + goto retry; + /* recoverable error*/ + } + + if( nframes != fEngineControl->fBufferSize ) + jack_log( "JackIoAudioDriver::Read warning fBufferSize = %ld nframes = %ld", + fEngineControl->fBufferSize, + nframes ); + + // Has to be done before read + JackDriver::CycleIncTime(); + + return read( fEngineControl->fBufferSize ); + } + + int JackIoAudioDriver::SetBufferSize( + jack_nframes_t buffer_size ) + { + jack_log( "JackIoAudioDriver::SetBufferSize %ld", + buffer_size ); + fArgs.frames_per_interrupt = buffer_size; + int res = set_parameters(); + + if( res == 0 ) + { + // update fEngineControl and fGraphManager + JackAudioDriver::SetBufferSize( buffer_size ); // Generic change, never fails + // io-audio specific + update_latencies(); + } + else + { + // Restore old values + fArgs.frames_per_interrupt = fEngineControl->fBufferSize; + set_parameters(); + } + + return res; + } + + int JackIoAudioDriver::Start() + { + int err; + int res = 0; + + res = JackAudioDriver::Start(); + if( res < 0 ) + { + return res; + } + + poll_last = 0; + poll_next = 0; + + if( playback.handle ) + { + if( ( err = snd_pcm_playback_prepare( playback.handle ) ) < 0 ) + { + jack_error( "io-audio: prepare error for playback on " + "\"%s\" (%s)", + fArgs.playback_pcm_name, + snd_strerror( err ) ); + res = -1; + } + } + + if( ( capture.handle && capture_and_playback_not_synced ) + || !playback.handle ) + { + if( ( err = snd_pcm_capture_prepare( capture.handle ) ) < 0 ) + { + jack_error( "io-audio: prepare error for capture on \"%s\"" + " (%s)", + fArgs.capture_pcm_name, + snd_strerror( err ) ); + res = -1; + } + } + + res = JackIoAudioDriver::Write(); + + if( res < 0 ) + { + JackAudioDriver::Stop(); + } + + return res; + } + + int JackIoAudioDriver::Stop() + { + int err; + int res = 0; + + /* silence all capture port buffers, because we might + be entering offline mode. + */ + for( int chn = 0; chn < fPlaybackChannels; chn++ ) + { + jack_default_audio_sample_t* buf = + (jack_default_audio_sample_t*)fGraphManager->GetBuffer( fPlaybackPortList[chn], + fEngineControl->fBufferSize ); + memset( buf, + 0, + sizeof(jack_default_audio_sample_t) + * fEngineControl->fBufferSize ); + } + + if( playback.handle ) + { + err = snd_pcm_playback_flush( playback.handle ); + if( err < 0 ) + { + jack_error( "io-audio: channel flush for playback " + "failed (%s)", + snd_strerror( err ) ); + res = -1; + } + } + + if( !playback.handle || capture_and_playback_not_synced ) + { + if( capture.handle ) + { + err = snd_pcm_capture_flush( capture.handle ); + if( err < 0 ) + { + jack_error( "io-audio: channel flush for " + "capture failed (%s)", + snd_strerror( err ) ); + res = -1; + } + } + } + + if( do_hw_monitoring ) + { + hw->set_input_monitor_mask( 0 ); + } + + res = JackAudioDriver::Stop(); + + return res; + } + + int JackIoAudioDriver::Write() + { + jack_nframes_t nframes = fEngineControl->fBufferSize; + + jack_nframes_t orig_nframes; + ssize_t nwritten; + ssize_t contiguous; + size_t offset = 0; + int err; + + process_count++; + + if( !playback.handle ) + { + return 0; + } + + if( nframes > fArgs.frames_per_interrupt ) + { + return -1; + } + + nwritten = 0; + contiguous = 0; + orig_nframes = nframes; + + /* check current input monitor request status */ + + input_monitor_mask = 0; + + for( int chn = 0; chn < fCaptureChannels; chn++ ) + { + JackPort* port = fGraphManager->GetPort( fCapturePortList[chn] ); + if( port->MonitoringInput() ) + { + input_monitor_mask |= ( 1 << chn ); + } + } + + if( do_hw_monitoring ) + { + if( hw->input_monitor_mask != input_monitor_mask ) + { + hw->set_input_monitor_mask( input_monitor_mask ); + } + } + + while( nframes ) + { + + contiguous = nframes; + + if( get_channel_addresses( (size_t *)0, + (size_t *)&contiguous, + 0, + &offset ) < 0 ) + { + return -1; + } + + for( int chn = 0; chn < fPlaybackChannels; chn++ ) + { + // 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 ); + playback.write( playback.voices[chn].addr, + buf + nwritten, + contiguous, + playback.voices[chn].interleave_skip, + &playback.voices[chn].dither_state ); + + // 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 ); + memcpy( monbuf + nwritten, + buf + nwritten, + contiguous * sizeof(jack_default_audio_sample_t) ); + } + } + else + { + memset_interleave( playback.voices[chn].addr, + 0, + playback.sample_bytes() * contiguous, + interleave_unit, + playback.voices[chn].interleave_skip ); + } + + } + + err = snd_pcm_write( playback.handle, + playback.buffer, + playback.frag_bytes() ); + if( err < 0 ) + { + jack_error( "io-audio: could not complete playback of %" + PRIu32 " frames: error = %d", + contiguous, + err ); + if( err != -EPIPE && err != -ESTRPIPE ) + return -1; + } + + nframes -= contiguous; + nwritten += contiguous; + } + + return 0; + } + + int JackIoAudioDriver::check_capabilities( + const char *devicename, + int mode ) + { + int ret = 0; + int err; + snd_pcm_t* device; + snd_pcm_info_t info; + snd_ctl_t* ctl; + snd_ctl_hw_info_t hw_info; + + err = snd_pcm_open_name( &device, + devicename, + mode ); + if( err ) + { + jack_error( "device open name:(%s) error:(%s)", + devicename, + snd_strerror( err ) ); + return err; + } + + err = snd_pcm_info( device, + &info ); + if( err ) + { + jack_error( "device info error:(%s)", + devicename, + snd_strerror( err ) ); + snd_pcm_close( device ); + return err; + } + + err = snd_ctl_open( &ctl, + info.card ); + if( err ) + { + jack_error( "control open card:(%d) error:(%s)", + info.card, + snd_strerror( err ) ); + snd_pcm_close( device ); + return err; + } + + err = snd_ctl_hw_info( ctl, + &hw_info ); + if( err ) + { + jack_error( "control hardware error:(%s)", + snd_strerror( err ) ); + snd_ctl_close( ctl ); + snd_pcm_close( device ); + return err; + } + + if( ( SND_PCM_OPEN_CAPTURE == mode ) + && !( info.flags & SND_PCM_INFO_CAPTURE ) ) + { + jack_error( "Capture channels required but not available" ); + ret = -EINVAL; + } + if( ( SND_PCM_OPEN_PLAYBACK == mode ) + && !( info.flags & SND_PCM_INFO_PLAYBACK ) ) + { + jack_error( "Playback channels required but not available" ); + ret = -EINVAL; + } + + snd_ctl_close( ctl ); + snd_pcm_close( device ); + return ret; + } + + int JackIoAudioDriver::check_card_type() + { + int err; + + // Check capture device can be opened + if( fArgs.capture ) + { + err = check_capabilities( fArgs.capture_pcm_name, + SND_PCM_OPEN_CAPTURE ); + if( err ) + return err; + } + + // Check playback device can be opened + if( fArgs.playback ) + { + err = check_capabilities( fArgs.playback_pcm_name, + SND_PCM_OPEN_PLAYBACK ); + if( err ) + return err; + } + + return 0; + } + + int JackIoAudioDriver::configure_stream( + const char *device_name, + const char *stream_name, + snd_pcm_t *handle, + snd_pcm_channel_params_t *params, + unsigned int *nperiodsp ) + { + int err; + size_t format; + snd_pcm_channel_info_t info; + int32_t formats[] = { + SND_PCM_SFMT_FLOAT_LE, + SND_PCM_SFMT_S32_LE, + SND_PCM_SFMT_S32_BE, + SND_PCM_SFMT_S24_LE, + SND_PCM_SFMT_S24_BE, + SND_PCM_SFMT_S16_LE, + SND_PCM_SFMT_S16_BE }; +#define NUMFORMATS (sizeof(formats)/sizeof(formats[0])) + + info.channel = params->channel; + + err = snd_pcm_channel_info( handle, + &info ); + + if( SND_PCM_CHNINFO_NONINTERLEAVE & info.flags ) + { + params->format.interleave = 0; + } + else if( SND_PCM_CHNINFO_INTERLEAVE & info.flags ) + { + params->format.interleave = 1; + } + + if( !( SND_PCM_CHNINFO_MMAP & info.flags ) ) + { + jack_error( "io-audio: mmap-based access is not possible" + " for the %s " + "stream of this audio interface", + stream_name ); + return -1; + } + + params->mode = SND_PCM_MODE_BLOCK; + + for( format = 0; format < NUMFORMATS; ++format ) + { + if( info.formats & ( 1 << formats[format] ) ) + { + jack_log( "io-audio: final selected sample format for %s: %s", + stream_name, + snd_pcm_get_format_name( formats[format] ) ); + params->format.format = formats[format]; + break; + } + } + if( NUMFORMATS == format ) + { + jack_error( "Sorry. The audio interface \"%s\"" + " doesn't support any of the" + " hardware sample formats that" + " JACK's ioaudio-driver can use.", + device_name ); + return -1; + } + +#if defined(SND_LITTLE_ENDIAN) + quirk_bswap = snd_pcm_format_big_endian(formats[format]); +#elif defined(SND_BIG_ENDIAN) + quirk_bswap = snd_pcm_format_little_endian( formats[format] ); +#else + quirk_bswap = 0; +#endif + + params->format.rate = frame_rate; + + if( 0 == params->format.voices ) + { + /*if not user-specified, try to find the maximum + * number of channels */ + params->format.voices = info.max_voices; + } + + params->start_mode = SND_PCM_START_DATA; + + params->stop_mode = SND_PCM_STOP_STOP; + + params->time = 1; + + params->buf.block.frag_size = + snd_pcm_format_size( formats[format], + params->format.voices + * fArgs.frames_per_interrupt ); + + *nperiodsp = fArgs.user_nperiods; + params->buf.block.frags_min = fArgs.user_nperiods; + params->buf.block.frags_max = fArgs.user_nperiods; + + jack_log( "io-audio: use %d periods for %s", + *nperiodsp, + stream_name ); + + int subchn_len = sizeof( params->sw_mixer_subchn_name ); + strncpy( params->sw_mixer_subchn_name, + stream_name, + subchn_len ); + params->sw_mixer_subchn_name[subchn_len - 1] = '\0'; + + if( ( err = snd_pcm_channel_params( handle, + params ) ) < 0 ) + { + jack_error( "io-audio: cannot set hardware parameters for %s, why_failed=%d", + stream_name, + params->why_failed ); + return -1; + } + + return 0; + } + + int JackIoAudioDriver::get_channel_addresses( + size_t *capture_avail, + size_t *playback_avail, + size_t *capture_offset, + size_t *playback_offset ) + { + int chn; + + if( capture_avail ) + { + ssize_t samp_bytes = capture.sample_bytes(); + ssize_t frame_bytes = capture.frame_bytes(); + for( chn = 0; chn < capture.setup.format.voices; chn++ ) + { + capture.voices[chn].addr = (char*)capture.buffer + chn * samp_bytes; + capture.voices[chn].interleave_skip = frame_bytes; + } + } + + if( playback_avail ) + { + ssize_t samp_bytes = playback.sample_bytes(); + ssize_t frame_bytes = playback.frame_bytes(); + for( chn = 0; chn < playback.setup.format.voices; chn++ ) + { + playback.voices[chn].addr = (char*)playback.buffer + + chn * samp_bytes; + playback.voices[chn].interleave_skip = frame_bytes; + + } + } + + return 0; + } + + int JackIoAudioDriver::hw_specific() + { + hw = new Jack::generic_hardware; + if( NULL == hw ) + { + return -ENOMEM; + } + + if( hw->capabilities & Cap_HardwareMonitoring ) + { + has_hw_monitoring = true; + /* XXX need to ensure that this is really false or + * true or whatever*/ + do_hw_monitoring = fArgs.hw_monitoring; + } + else + { + has_hw_monitoring = false; + do_hw_monitoring = false; + } + + if( hw->capabilities & Cap_ClockLockReporting ) + { + has_clock_sync_reporting = true; + } + else + { + has_clock_sync_reporting = false; + } + + if( hw->capabilities & Cap_HardwareMetering ) + { + has_hw_metering = true; + do_hw_metering = fArgs.hw_metering; + } + else + { + has_hw_metering = false; + do_hw_metering = false; + } + + return 0; + } + + int JackIoAudioDriver::read( + jack_nframes_t nframes ) + { + ssize_t contiguous; + ssize_t nread; + size_t offset = 0; + jack_nframes_t orig_nframes; + int err; + + if( nframes > fArgs.frames_per_interrupt ) + { + return -1; + } + + if( !capture.handle ) + { + return 0; + } + + nread = 0; + contiguous = 0; + orig_nframes = nframes; + + while( nframes ) + { + + contiguous = nframes; + + if( get_channel_addresses( (size_t *)&contiguous, + (size_t *)0, + &offset, + 0 ) < 0 ) + { + return -1; + } + + err = snd_pcm_read( capture.handle, + capture.buffer, + capture.frag_bytes() ); + if( err < 0 ) + { + jack_error( "io-audio: could not complete read of %" + PRIu32 " frames: error = %d", + contiguous, + err ); + return -1; + } + + 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 ); + capture.read( buf + nread, + capture.voices[chn].addr, + contiguous, + capture.voices[chn].interleave_skip ); + } + } + + nframes -= contiguous; + nread += contiguous; + } + + return 0; + } + + int JackIoAudioDriver::set_parameters() + { + ssize_t p_period_size = 0; + size_t c_period_size = 0; + unsigned int pr = 0; + unsigned int cr = 0; + int err; + + frame_rate = fArgs.srate; + + jack_log( "configuring for %" PRIu32 "Hz, period = %" + PRIu32 " frames (%.1f ms), buffer = %" PRIu32 " periods", + fArgs.srate, + fArgs.frames_per_interrupt, + ( ( (float)fArgs.frames_per_interrupt / (float)fArgs.srate ) + * 1000.0f ), + fArgs.user_nperiods ); + + if( capture.handle ) + { + memset( &capture.params, + 0, + sizeof(snd_pcm_channel_params_t) ); + capture.params.channel = SND_PCM_CHANNEL_CAPTURE; + if( configure_stream( fArgs.capture_pcm_name, + "capture", + capture.handle, + &capture.params, + &capture.nperiods ) ) + { + jack_error( "io-audio: cannot configure capture channel" ); + return -1; + } + capture.setup.channel = SND_PCM_CHANNEL_CAPTURE; + snd_pcm_channel_setup( capture.handle, + &capture.setup ); + cr = capture.setup.format.rate; + + /* check the fragment size, since thats non-negotiable */ + c_period_size = capture.setup.buf.block.frag_size; + + if( c_period_size != fArgs.frames_per_interrupt ) + { + jack_error( "ioaudio_pcm: requested an interrupt every %" + PRIu32 + " frames but got %uc frames for capture", + fArgs.frames_per_interrupt, + p_period_size ); + return -1; + } + + /* check the sample format */ + switch( capture.setup.format.format ) + { + case SND_PCM_SFMT_FLOAT_LE: + case SND_PCM_SFMT_S32_LE: + case SND_PCM_SFMT_S24_LE: + case SND_PCM_SFMT_S24_BE: + case SND_PCM_SFMT_S16_LE: + case SND_PCM_SFMT_S32_BE: + case SND_PCM_SFMT_S16_BE: + break; + + default: + jack_error( "programming error: unhandled format " + "type for capture" ); + return -1; + } + + if( capture.setup.format.interleave ) + { + if( ( err = snd_pcm_mmap( capture.handle, + SND_PCM_CHANNEL_CAPTURE, + &capture.mmap, + &capture.buffer ) ) < 0 ) + { + jack_error( "io-audio: %s: mmap areas info error", + fArgs.capture_pcm_name ); + return -1; + } + } + } + + if( playback.handle ) + { + memset( &playback.params, + 0, + sizeof(snd_pcm_channel_params_t) ); + playback.params.channel = SND_PCM_CHANNEL_PLAYBACK; + + if( configure_stream( fArgs.playback_pcm_name, + "playback", + playback.handle, + &playback.params, + &playback.nperiods ) ) + { + jack_error( "io-audio: cannot configure playback channel" ); + return -1; + } + + playback.setup.channel = SND_PCM_CHANNEL_PLAYBACK; + snd_pcm_channel_setup( playback.handle, + &playback.setup ); + pr = playback.setup.format.rate; + + /* check the fragment size, since thats non-negotiable */ + p_period_size = playback.setup.buf.block.frag_size; + + if( p_period_size != playback.frag_bytes() ) + { + jack_error( "ioaudio_pcm: requested an interrupt every %" + PRIu32 + " frames but got %u frames for playback", + fArgs.frames_per_interrupt, + p_period_size ); + return -1; + } + + /* check the sample format */ + switch( playback.setup.format.format ) + { + case SND_PCM_SFMT_FLOAT_LE: + case SND_PCM_SFMT_S32_LE: + case SND_PCM_SFMT_S24_LE: + case SND_PCM_SFMT_S24_BE: + case SND_PCM_SFMT_S16_LE: + case SND_PCM_SFMT_S32_BE: + case SND_PCM_SFMT_S16_BE: + break; + + default: + jack_error( "programming error: unhandled format " + "type for playback" ); + return -1; + } + + playback.buffer = malloc( fArgs.user_nperiods * p_period_size ); + if( playback.setup.format.interleave ) + { + interleave_unit = playback.sample_bytes(); + } + else + { + interleave_unit = 0; /* NOT USED */ + } + } + + /* check the rate, since thats rather important */ + if( capture.handle && playback.handle ) + { + if( cr != pr ) + { + jack_error( "playback and capture sample rates do " + "not match (%d vs. %d)", + pr, + cr ); + } + + /* only change if *both* capture and playback rates + * don't match requested certain hardware actually + * still works properly in full-duplex with slightly + * different rate values between adc and dac + */ + if( cr != frame_rate && pr != frame_rate ) + { + jack_error( "sample rate in use (%d Hz) does not " + "match requested rate (%d Hz)", + cr, + frame_rate ); + frame_rate = cr; + } + + } + else if( capture.handle && cr != frame_rate ) + { + jack_error( "capture sample rate in use (%d Hz) does not " + "match requested rate (%d Hz)", + cr, + frame_rate ); + frame_rate = cr; + } + else if( playback.handle && pr != frame_rate ) + { + jack_error( "playback sample rate in use (%d Hz) does not " + "match requested rate (%d Hz)", + pr, + frame_rate ); + frame_rate = pr; + } + + max_nchannels = std::max( playback.setup.format.voices, + capture.setup.format.voices ); + user_nchannels = std::min( playback.setup.format.voices, + capture.setup.format.voices ); + + setup_io_function_pointers(); + + if( playback.handle ) + { + if( playback.voices ) + delete[] playback.voices; + playback.voices = new Voice[max_nchannels]; + } + + if( capture.handle ) + { + if( capture.voices ) + delete[] capture.voices; + capture.voices = new Voice[max_nchannels]; + } + + float period_usecs = ( ( (float)fArgs.frames_per_interrupt ) / frame_rate ) + * 1000000.0f; + + poll_timeout_msecs = (int)floor( 1.5f * period_usecs / 1000.0f ); + + return 0; + } + + void JackIoAudioDriver::setup_io_function_pointers() + { + if( playback.handle ) + { + if( SND_PCM_FMT_FLOAT_LE == playback.setup.format.format ) + { + playback.write = sample_move_dS_floatLE; + } + else + { + ssize_t bytes = playback.sample_bytes(); + switch( bytes ) + { + case 2: + switch( fArgs.dither ) + { + case Rectangular: + jack_log( "Rectangular dithering at 16 bits" ); + playback.write = + quirk_bswap ? + sample_move_dither_rect_d16_sSs : + sample_move_dither_rect_d16_sS; + break; + + case Triangular: + jack_log( "Triangular dithering at 16 bits" ); + playback.write = + quirk_bswap ? + sample_move_dither_tri_d16_sSs : + sample_move_dither_tri_d16_sS; + break; + + case Shaped: + jack_log( "Noise-shaped dithering at 16 bits" ); + playback.write = + quirk_bswap ? + sample_move_dither_shaped_d16_sSs : + sample_move_dither_shaped_d16_sS; + break; + + default: + playback.write = + quirk_bswap ? + sample_move_d16_sSs : sample_move_d16_sS; + break; + } + break; + + case 3: /* NO DITHER */ + playback.write = + quirk_bswap ? sample_move_d24_sSs : sample_move_d24_sS; + + break; + + case 4: /* NO DITHER */ + playback.write = + quirk_bswap ? + sample_move_d32u24_sSs : sample_move_d32u24_sS; + break; + + default: + jack_error( "impossible sample width (%d) discovered!", + bytes ); + exit( 1 ); + } + } + } + + if( capture.handle ) + { + if( SND_PCM_FMT_FLOAT_LE == capture.setup.format.format ) + { + capture.read = sample_move_floatLE_sSs; + } + else + { + ssize_t bytes = capture.sample_bytes(); + switch( bytes ) + { + case 2: + capture.read = + quirk_bswap ? sample_move_dS_s16s : sample_move_dS_s16; + break; + case 3: + capture.read = + quirk_bswap ? sample_move_dS_s24s : sample_move_dS_s24; + break; + case 4: + capture.read = + quirk_bswap ? + sample_move_dS_s32u24s : sample_move_dS_s32u24; + break; + } + } + } + } + + void JackIoAudioDriver::update_latencies() + { + jack_latency_range_t range; + + for( int i = 0; i < fCaptureChannels; i++ ) + { + range.min = range.max = fArgs.frames_per_interrupt + + fArgs.systemic_input_latency; + fGraphManager->GetPort( fCapturePortList[i] )->SetLatencyRange( JackCaptureLatency, + &range ); + } + + for( int i = 0; i < fPlaybackChannels; i++ ) + { + // Add one buffer more latency if "async" mode is used... + range.min = range.max = + ( fArgs.frames_per_interrupt * ( fArgs.user_nperiods - 1 ) ) + + ( ( fEngineControl->fSyncMode ) ? + 0 : fEngineControl->fBufferSize ) + + fArgs.systemic_output_latency; + fGraphManager->GetPort( fPlaybackPortList[i] )->SetLatencyRange( JackPlaybackLatency, + &range ); + // Monitor port + if( fWithMonitorPorts ) + { + range.min = range.max = fArgs.frames_per_interrupt; + fGraphManager->GetPort( fMonitorPortList[i] )->SetLatencyRange( JackCaptureLatency, + &range ); + } + } + } + + jack_nframes_t JackIoAudioDriver::wait( + int *status, + float *delayed_usecs ) + { + static int under_gdb = 0; + ssize_t avail = 0; + ssize_t capture_avail = 0; + ssize_t playback_avail = 0; + bool xrun_detected = false; + bool need_capture; + bool need_playback; + jack_time_t poll_enter; + jack_time_t poll_ret = 0; + + *status = -1; + *delayed_usecs = 0; + + need_capture = capture.handle ? true : false; + + need_playback = playback.handle ? true : false; + + again: + + while( need_playback || need_capture ) + { + + int poll_result; + unsigned short revents; + + if( need_playback ) + { + int fd = snd_pcm_file_descriptor( playback.handle, + SND_PCM_CHANNEL_PLAYBACK ); + + pfd[Playback].fd = fd; + pfd[Playback].events = POLLOUT; + } + else + { + pfd[Playback].fd = -1; + } + + if( need_capture ) + { + int fd = snd_pcm_file_descriptor( capture.handle, + SND_PCM_CHANNEL_CAPTURE ); + + pfd[Capture].fd = fd; + pfd[Capture].events = POLLIN; + } + else + { + pfd[Capture].fd = -1; + } + + poll_enter = GetMicroSeconds(); + + if( poll_enter > poll_next ) + { + /* + * This processing cycle was delayed past the + * next due interrupt! Do not account this as + * a wakeup delay: + */ + poll_next = 0; + poll_late++; + } + + poll_result = poll( pfd, + SND_PCM_CHANNEL_MAX, + poll_timeout_msecs ); + + if( poll_result < 0 ) + { + + if( errno == EINTR ) + { + jack_log( "poll interrupt" ); + // this happens mostly when run + // under gdb, or when exiting due to a signal + if( under_gdb ) + { + goto again; + } + *status = -2; + return 0; + } + + jack_error( "io-audio: poll call failed (%s)", + strerror( errno ) ); + *status = -3; + return 0; + + } + + poll_ret = GetMicroSeconds(); + + // JACK2 + JackDriver::CycleTakeBeginTime(); + + if( need_playback ) + { + snd_pcm_channel_status_t chstatus; + chstatus.channel = SND_PCM_CHANNEL_PLAYBACK; + snd_pcm_channel_status( playback.handle, + &chstatus ); + + revents = pfd[Playback].revents; + + if( revents & POLLERR ) + { + xrun_detected = true; + } + + if( revents & POLLOUT ) + { + need_playback = 0; +#ifdef DEBUG_WAKEUP + fprintf (stderr, "%" PRIu64 + " playback stream ready\n", + poll_ret); +#endif + } + } + + if( need_capture ) + { + snd_pcm_channel_status_t chstatus; + chstatus.channel = SND_PCM_CHANNEL_CAPTURE; + snd_pcm_channel_status( playback.handle, + &chstatus ); + + revents = pfd[Capture].revents; + + if( revents & POLLERR ) + { + xrun_detected = true; + } + + if( revents & POLLIN ) + { + need_capture = 0; +#ifdef DEBUG_WAKEUP + fprintf (stderr, "%" PRIu64 + " capture stream ready\n", + poll_ret); +#endif + } + } + + if( poll_result == 0 ) + { + jack_error( "io-audio: poll time out, polled for %" PRIu64 + " usecs", + poll_ret - poll_enter ); + *status = -5; + return 0; + } + + } + + if( capture.handle ) + { + capture_avail = capture.frag_bytes() / capture.frame_bytes(); + } + else + { + capture_avail = std::numeric_limits < ssize_t > ::max(); + } + + if( playback.handle ) + { + playback_avail = playback.frag_bytes() / playback.frame_bytes(); + } + else + { + /* odd, but see min() computation below */ + playback_avail = std::numeric_limits < ssize_t > ::max(); + } + + if( xrun_detected ) + { + *status = xrun_recovery( delayed_usecs ); + return 0; + } + + *status = 0; + + avail = capture_avail < playback_avail ? capture_avail : playback_avail; + +#ifdef DEBUG_WAKEUP + fprintf (stderr, "wakeup complete, avail = %lu, pavail = %lu " + "cavail = %lu\n", + avail, playback_avail, capture_avail); +#endif + + /* constrain the available count to the nearest (round down) number of + periods. + */ + + return avail - ( avail % fArgs.frames_per_interrupt ); + } + + int JackIoAudioDriver::xrun_recovery( + float *delayed_usecs ) + { + snd_pcm_channel_status_t status; + int res; + + if( capture.handle ) + { + status.channel = SND_PCM_CHANNEL_CAPTURE; + if( ( res = snd_pcm_channel_status( capture.handle, + &status ) ) < 0 ) + { + jack_error( "status error: %s", + snd_strerror( res ) ); + } + } + else + { + status.channel = SND_PCM_CHANNEL_PLAYBACK; + if( ( res = snd_pcm_channel_status( playback.handle, + &status ) ) < 0 ) + { + jack_error( "status error: %s", + snd_strerror( res ) ); + } + } + + if( status.status == SND_PCM_STATUS_READY ) + { + jack_log( "**** ioaudio_pcm: pcm in suspended state, resuming it" ); + if( capture.handle ) + { + if( ( res = snd_pcm_capture_prepare( capture.handle ) ) < 0 ) + { + jack_error( "error preparing after suspend: %s", + snd_strerror( res ) ); + } + } + else + { + if( ( res = snd_pcm_playback_prepare( playback.handle ) ) < 0 ) + { + jack_error( "error preparing after suspend: %s", + snd_strerror( res ) ); + } + } + } + + if( status.status == SND_PCM_STATUS_OVERRUN + && process_count > XRUN_REPORT_DELAY ) + { + struct timeval now, diff, tstamp; + xrun_count++; + gettimeofday( &now, + NULL ); + tstamp = status.stop_time; + timersub( &now, + &tstamp, + &diff ); + *delayed_usecs = diff.tv_sec * 1000000.0 + diff.tv_usec; + jack_log( "**** ioaudio_pcm: xrun of at least %.3f msecs", + *delayed_usecs / 1000.0 ); + } + + in_xrun_recovery = 1; + if( ( res = Stop() ) == 0 ) + { + res = Start(); + } + in_xrun_recovery = 0; + + if( 0 != res ) + { + return -1; + } + return 0; + } + +#ifdef __cplusplus + extern "C" + { +#endif + + static int dither_opt( + char c, + DitherAlgorithm* dither ) + { + switch( c ) + { + case '-': + case 'n': + *dither = None; + break; + + case 'r': + *dither = Rectangular; + break; + + case 's': + *dither = Shaped; + break; + + case 't': + *dither = Triangular; + break; + + default: + fprintf( stderr, + "io-audio driver: illegal dithering mode %c\n", + c ); + return -1; + } + return 0; + } + + static jack_driver_param_constraint_desc_t * + enum_ioaudio_devices() + { + snd_ctl_t * handle; + snd_ctl_hw_info_t hwinfo; + snd_pcm_info_t pcminfo; + int card_no = -1; + jack_driver_param_value_t card_id; + jack_driver_param_value_t device_id; + char description[64]; + uint32_t device_no; + bool has_capture; + bool has_playback; + jack_driver_param_constraint_desc_t * constraint_ptr; + uint32_t array_size = 0; + + constraint_ptr = NULL; + + int cards_over = 0; + int numcards = snd_cards_list( NULL, + 0, + &cards_over ); + int* cards = new int[cards_over]; + numcards = snd_cards_list( cards, + cards_over, + &cards_over ); + + for( int c = 0; c < numcards; ++c ) + { + card_no = cards[c]; + + if( snd_ctl_open( &handle, + card_no ) >= 0 + && snd_ctl_hw_info( handle, + &hwinfo ) >= 0 ) + { + strncpy( card_id.str, + hwinfo.id, + sizeof( card_id.str ) ); + strncpy( description, + hwinfo.longname, + sizeof( description ) ); + if( !jack_constraint_add_enum( &constraint_ptr, + &array_size, + &card_id, + description ) ) + goto fail; + + device_no = -1; + + for( device_no = 0; device_no < hwinfo.pcmdevs; ++device_no ) + { + snprintf( device_id.str, + sizeof( device_id.str ), + "%s,%d", + card_id.str, + device_no ); + + snd_ctl_pcm_info( handle, + device_no, + &pcminfo ); + has_capture = pcminfo.flags & SND_PCM_INFO_CAPTURE; + has_playback = pcminfo.flags & SND_PCM_INFO_PLAYBACK; + + if( has_capture && has_playback ) + { + snprintf( description, + sizeof( description ), + "%s (duplex)", + pcminfo.name ); + } + else if( has_capture ) + { + snprintf( description, + sizeof( description ), + "%s (capture)", + pcminfo.name ); + } + else if( has_playback ) + { + snprintf( description, + sizeof( description ), + "%s (playback)", + pcminfo.name ); + } + else + { + continue; + } + + if( !jack_constraint_add_enum( &constraint_ptr, + &array_size, + &device_id, + description ) ) + goto fail; + } + + snd_ctl_close( handle ); + } + } + + delete[] cards; + return constraint_ptr; + + fail: jack_constraint_free( constraint_ptr ); + delete[] cards; + return NULL; + } + + SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor() + { + jack_driver_desc_t * desc; + jack_driver_desc_filler_t filler; + jack_driver_param_value_t value; + + desc = + jack_driver_descriptor_construct( "io-audio", + JackDriverMaster, + "QNX io-audio API based audio backend", + &filler ); + + strcpy( value.str, + "pcmPreferredp" ); + jack_driver_descriptor_add_parameter( desc, + &filler, + "device", + 'd', + JackDriverParamString, + &value, + enum_ioaudio_devices(), + "io-audio device name", + NULL ); + + strcpy( value.str, + "none" ); + jack_driver_descriptor_add_parameter( desc, + &filler, + "capture", + 'C', + JackDriverParamString, + &value, + NULL, + "Provide capture ports. Optionally set device", + NULL ); + jack_driver_descriptor_add_parameter( desc, + &filler, + "playback", + 'P', + JackDriverParamString, + &value, + NULL, + "Provide playback ports. Optionally set device", + NULL ); + + value.ui = 48000U; + jack_driver_descriptor_add_parameter( desc, + &filler, + "rate", + 'r', + JackDriverParamUInt, + &value, + NULL, + "Sample rate", + NULL ); + + value.ui = 1024U; + jack_driver_descriptor_add_parameter( desc, + &filler, + "period", + 'p', + JackDriverParamUInt, + &value, + NULL, + "Frames per period", + NULL ); + + value.ui = 2U; + jack_driver_descriptor_add_parameter( desc, + &filler, + "nperiods", + 'n', + JackDriverParamUInt, + &value, + NULL, + "Number of periods of playback latency", + NULL ); + + value.i = 0; + jack_driver_descriptor_add_parameter( desc, + &filler, + "hwmon", + 'H', + JackDriverParamBool, + &value, + NULL, + "Hardware monitoring, if available", + NULL ); + + value.i = 0; + jack_driver_descriptor_add_parameter( desc, + &filler, + "hwmeter", + 'M', + JackDriverParamBool, + &value, + NULL, + "Hardware metering, if available", + NULL ); + + value.i = 1; + jack_driver_descriptor_add_parameter( desc, + &filler, + "duplex", + 'D', + JackDriverParamBool, + &value, + NULL, + "Provide both capture and playback ports", + NULL ); + + value.i = 0; + jack_driver_descriptor_add_parameter( desc, + &filler, + "softmode", + 's', + JackDriverParamBool, + &value, + NULL, + "Soft-mode, no xrun handling", + NULL ); + + value.i = 0; + jack_driver_descriptor_add_parameter( desc, + &filler, + "monitor", + 'm', + JackDriverParamBool, + &value, + NULL, + "Provide monitor ports for the output", + NULL ); + + value.c = 'n'; + jack_driver_descriptor_add_parameter( desc, + &filler, + "dither", + 'z', + JackDriverParamChar, + &value, + jack_constraint_compose_enum_char( + JACK_CONSTRAINT_FLAG_STRICT + | JACK_CONSTRAINT_FLAG_FAKE_VALUE, + dither_constraint_descr_array ), + "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 ); + + value.i = false; + jack_driver_descriptor_add_parameter( desc, + &filler, + "shorts", + 'S', + JackDriverParamBool, + &value, + NULL, + "Try 16-bit samples before 32-bit", + NULL ); + + value.ui = 0; + jack_driver_descriptor_add_parameter( desc, + &filler, + "input-latency", + 'I', + JackDriverParamUInt, + &value, + NULL, + "Extra input latency (frames)", + NULL ); + jack_driver_descriptor_add_parameter( desc, + &filler, + "output-latency", + 'O', + JackDriverParamUInt, + &value, + NULL, + "Extra output latency (frames)", + NULL ); + + return desc; + } + + SERVER_EXPORT JackDriverClientInterface* driver_initialize( + JackLockedEngine* engine, + JackSynchro* table, + const JSList* params ) + { + JackIoAudioDriver::Args args; + args.jack_name = "system"; + args.jack_alias = "ioaudio_pcm"; + args.srate = 48000; + args.frames_per_interrupt = 1024; + args.user_nperiods = 2; + args.playback_pcm_name = "pcmPreferredp"; + args.capture_pcm_name = "pcmPreferredc"; + args.hw_monitoring = false; + args.hw_metering = false; + args.capture = false; + args.playback = false; + args.soft_mode = false; + args.monitor = false; + args.dither = None; + args.user_capture_nchnls = 0; + args.user_playback_nchnls = 0; + args.shorts_first = false; + args.systemic_input_latency = 0; + args.systemic_output_latency = 0; + + const JSList * node; + const jack_driver_param_t * param; + + for( node = params; node; node = jack_slist_next( node ) ) + { + param = (const jack_driver_param_t *)node->data; + + switch( param->character ) + { + + case 'C': + if( strcmp( param->value.str, + "none" ) != 0 ) + { + args.capture = true; + args.capture_pcm_name = strdup( param->value.str ); + jack_log( "capture device %s", + args.capture_pcm_name ); + } + break; + + case 'P': + if( strcmp( param->value.str, + "none" ) != 0 ) + { + args.playback = true; + args.playback_pcm_name = strdup( param->value.str ); + jack_log( "playback device %s", + args.playback_pcm_name ); + } + break; + + case 'D': + args.playback = true; + args.capture = true; + break; + + case 'd': + if( strcmp( param->value.str, + "none" ) != 0 ) + { + args.playback = true; + args.playback_pcm_name = strdup( param->value.str ); + args.capture = true; + args.capture_pcm_name = strdup( param->value.str ); + jack_log( "playback device %s", + args.playback_pcm_name ); + jack_log( "capture device %s", + args.capture_pcm_name ); + } + break; + + case 'H': + args.hw_monitoring = param->value.i; + break; + + case 'm': + args.monitor = param->value.i; + break; + + case 'M': + args.hw_metering = param->value.i; + break; + + case 'r': + args.srate = param->value.ui; + jack_log( "apparent rate = %d", + args.srate ); + break; + + case 'p': + args.frames_per_interrupt = param->value.ui; + jack_log( "frames per period = %d", + args.frames_per_interrupt ); + break; + + case 'n': + args.user_nperiods = param->value.ui; + if( args.user_nperiods < 2 ) + { /* enforce minimum value */ + args.user_nperiods = 2; + } + break; + + case 's': + args.soft_mode = param->value.i; + break; + + case 'z': + if( dither_opt( param->value.c, + &args.dither ) ) + { + return NULL; + } + break; + + case 'i': + args.user_capture_nchnls = param->value.ui; + break; + + case 'o': + args.user_playback_nchnls = param->value.ui; + break; + + case 'S': + args.shorts_first = param->value.i; + break; + + case 'I': + args.systemic_input_latency = param->value.ui; + break; + + case 'O': + args.systemic_output_latency = param->value.ui; + break; + } + } + + /* duplex is the default */ + if( !args.capture && !args.playback ) + { + args.capture = true; + args.playback = true; + } + + JackIoAudioDriver* ioaudio_driver = new JackIoAudioDriver( args, + engine, + table ); + JackDriverClientInterface* threaded_driver = + new JackThreadedDriver( ioaudio_driver ); + // Special open for io-audio driver + if( ioaudio_driver->Open() == 0 ) + { + return threaded_driver; + } + else + { + delete threaded_driver; // Delete the decorated driver + return NULL; + } + } + +#ifdef __cplusplus + } +#endif + +} +// end of namespace diff --git a/qnx/ioaudio/JackIoAudioDriver.h b/qnx/ioaudio/JackIoAudioDriver.h new file mode 100644 index 00000000..16aea909 --- /dev/null +++ b/qnx/ioaudio/JackIoAudioDriver.h @@ -0,0 +1,277 @@ +/* + Copyright (C) 2001 Paul Davis + Copyright (C) 2004 Grame + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#ifndef __JackIoAudioDriver__ +#define __JackIoAudioDriver__ + +#include "JackAudioDriver.h" +#include "JackThreadedDriver.h" +#include "JackTime.h" +#include "driver.h" +#include "memops.h" +#include + +namespace Jack +{ + +/////////////////////////////////////////////////////////////////////////////// +// CONSTANTS +/////////////////////////////////////////////////////////////////////////////// + + enum IoAudioDriverChannels + { + Playback = SND_PCM_CHANNEL_PLAYBACK, + Capture = SND_PCM_CHANNEL_CAPTURE, + Extra = SND_PCM_CHANNEL_MAX, + IoAudioDriverChannels_COUNT + }; + +/////////////////////////////////////////////////////////////////////////////// +// TYPES +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATIONS +/////////////////////////////////////////////////////////////////////////////// + + /*! + \brief The IoAudio driver. + */ + + class JackIoAudioDriver: public JackAudioDriver + { + public: + typedef void (*ReadCopyFunction)( + jack_default_audio_sample_t *dst, + char *src, + unsigned long src_bytes, + unsigned long src_skip_bytes ); + typedef void (*WriteCopyFunction)( + char *dst, + jack_default_audio_sample_t *src, + unsigned long src_bytes, + unsigned long dst_skip_bytes, + dither_state_t *state ); + + struct Args + { + const char* jack_name; + const char* jack_alias; + + char* device; + + bool capture; + const char* capture_pcm_name; + size_t user_capture_nchnls; + + bool playback; + const char* playback_pcm_name; + size_t user_playback_nchnls; + + size_t srate; + size_t frames_per_interrupt; + size_t user_nperiods; + DitherAlgorithm dither; + bool hw_monitoring; + bool hw_metering; + bool duplex; + bool soft_mode; + bool monitor; + bool shorts_first; + size_t systemic_input_latency; + size_t systemic_output_latency; + }; + + struct Voice + { + unsigned long interleave_skip; + char* addr; + unsigned long silent; + dither_state_t dither_state; + }; + + struct Channel + { + Voice* voices; + unsigned int nperiods; + snd_pcm_t *handle; + snd_pcm_channel_params_t params; + snd_pcm_channel_setup_t setup; + snd_pcm_mmap_control_t *mmap; + void *mmap_buf; + void *buffer; + + ReadCopyFunction read; + WriteCopyFunction write; + + ssize_t sample_bytes() + { + return snd_pcm_format_size( setup.format.format, + 1 ); + } + ssize_t frame_bytes() + { + return snd_pcm_format_size( setup.format.format, + setup.format.voices ); + } + + ssize_t frag_bytes() + { + return setup.buf.block.frag_size; + } + + ssize_t frag_frames() + { + return frag_bytes() / frame_bytes(); + } + + }; + + public: + + JackIoAudioDriver( + Args args, + JackLockedEngine* engine, + JackSynchro* table ); + + virtual ~JackIoAudioDriver(); + + int Attach(); + + int Close(); + + int Detach(); + + // BufferSize can be changed + bool IsFixedBufferSize() + { + return false; + } + + int Open(); + + int Read(); + + int SetBufferSize( + jack_nframes_t buffer_size ); + + int Start(); + + int Stop(); + + int Write(); + + private: + Args fArgs; + + int poll_timeout_msecs; + jack_time_t poll_last; + jack_time_t poll_next; + int poll_late; + + struct pollfd pfd[IoAudioDriverChannels_COUNT]; + unsigned long interleave_unit; + unsigned int max_nchannels; + unsigned int user_nchannels; + + jack_nframes_t frame_rate; + + Channel playback; + + Channel capture; + + jack_hardware_t *hw; + + bool capture_and_playback_not_synced; + bool has_clock_sync_reporting; + + bool has_hw_monitoring; + bool do_hw_monitoring; + unsigned long input_monitor_mask; + + bool has_hw_metering; + bool do_hw_metering; + bool quirk_bswap; + + int xrun_count; + int process_count; + + int in_xrun_recovery; + + int check_capabilities( + const char *devicename, + int mode ); + + int check_card_type(); + + void clear_output_aux(); + + int configure_stream( + const char *device_name, + const char *stream_name, + snd_pcm_t *handle, + snd_pcm_channel_params_t *params, + unsigned int *nperiodsp ); + + int create( + jack_client_t *client ); + + int generic_hardware(); + + int get_channel_addresses( + size_t *capture_avail, + size_t *playback_avail, + size_t *capture_offset, + size_t *playback_offset ); + + int hw_specific(); + + void monitor_input_aux(); + + int read( + jack_nframes_t nframes ); + + void read_input_aux( + jack_nframes_t orig_nframes, + ssize_t contiguous, + ssize_t nread ); + + int set_parameters(); + + void setup_io_function_pointers(); + + void update_latencies(); + + jack_nframes_t wait( + int *status, + float *delayed_usecs ); + + void write_output_aux( + jack_nframes_t orig_nframes, + ssize_t contiguous, + ssize_t nwritten ); + + int xrun_recovery( + float *delayed_usecs ); + }; + +} // end of namespace + +#endif diff --git a/qnx/ioaudio/bitset.h b/qnx/ioaudio/bitset.h new file mode 100644 index 00000000..434f7f60 --- /dev/null +++ b/qnx/ioaudio/bitset.h @@ -0,0 +1,134 @@ +/* +* bitset.h -- some simple bit vector set operations. +* +* This is useful for sets of small non-negative integers. There are +* some obvious set operations that are not implemented because I +* don't need them right now. +* +* These functions represent sets as arrays of unsigned 32-bit +* integers allocated on the heap. The first entry contains the set +* cardinality (number of elements allowed), followed by one or more +* words containing bit vectors. +* +* $Id: bitset.h,v 1.2 2005/11/23 11:24:29 letz Exp $ +*/ + +/* + * Copyright (C) 2005 Jack O'Quin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __bitset_h__ +#define __bitset_h__ + +#include +#include +#include /* POSIX standard fixed-size types */ +#include /* `#define NDEBUG' to disable */ + +/* On some 64-bit machines, this implementation may be slightly + * inefficient, depending on how compilers allocate space for + * uint32_t. For the set sizes I currently need, this is acceptable. + * It should not be hard to pack the bits better, if that becomes + * worthwhile. + */ +typedef uint32_t _bitset_word_t; +typedef _bitset_word_t *bitset_t; + +#define WORD_SIZE(cardinality) (1+((cardinality)+31)/32) +#define BYTE_SIZE(cardinality) (WORD_SIZE(cardinality)*sizeof(_bitset_word_t)) +#define WORD_INDEX(element) (1+(element)/32) +#define BIT_INDEX(element) ((element)&037) + +static inline void +bitset_add(bitset_t set + , unsigned int element) +{ + assert(element < set + [0]); + set + [WORD_INDEX(element)] |= (1 << BIT_INDEX(element)); +} + +static inline void +bitset_copy(bitset_t to_set, bitset_t from_set) +{ + assert(to_set[0] == from_set[0]); + memcpy(to_set, from_set, BYTE_SIZE(to_set[0])); +} + +static inline void +bitset_create(bitset_t *set + , unsigned int cardinality) +{ + *set + = (bitset_t) calloc(WORD_SIZE(cardinality), + sizeof(_bitset_word_t)); + assert(*set + ); + *set + [0] = cardinality; +} + +static inline void +bitset_destroy(bitset_t *set + ) +{ + if (*set + ) { + free(*set + ); + *set + = (bitset_t) 0; + } +} + +static inline int +bitset_empty(bitset_t set + ) +{ + int i; + _bitset_word_t result = 0; + int nwords = WORD_SIZE(set + [0]); + for (i = 1; i < nwords; i++) { + result |= set + [i]; + } + return (result == 0); +} + +static inline int +bitset_contains(bitset_t set + , unsigned int element) +{ + assert(element < set + [0]); + return (0 != (set + [WORD_INDEX(element)] & (1 << BIT_INDEX(element)))); +} + +static inline void +bitset_remove(bitset_t set + , unsigned int element) +{ + assert(element < set + [0]); + set + [WORD_INDEX(element)] &= ~(1 << BIT_INDEX(element)); +} + +#endif /* __bitset_h__ */ diff --git a/qnx/ioaudio/hardware.h b/qnx/ioaudio/hardware.h new file mode 100644 index 00000000..6b4a96dd --- /dev/null +++ b/qnx/ioaudio/hardware.h @@ -0,0 +1,76 @@ +/* + 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: hardware.h,v 1.3 2005/11/23 11:24:29 letz Exp $ + */ + +#ifndef __jack_hardware_h__ +#define __jack_hardware_h__ + +#include "types.h" + +enum SampleClockMode +{ + AutoSync, + WordClock, + ClockMaster +}; + +enum Capabilities +{ + Cap_None = 0x0, + Cap_HardwareMonitoring = 0x1, + Cap_AutoSync = 0x2, + Cap_WordClock = 0x4, + Cap_ClockMaster = 0x8, + Cap_ClockLockReporting = 0x10, + Cap_HardwareMetering = 0x20 +}; + +struct jack_hardware_t +{ + Capabilities capabilities; + unsigned long input_monitor_mask; + void *private_hw; + + jack_hardware_t() : + capabilities( Cap_None ), + input_monitor_mask( 0 ) + { + } + + virtual ~jack_hardware_t() {} + + virtual void release() = 0; + + virtual int set_input_monitor_mask( + unsigned long ) = 0; + + virtual int change_sample_clock( + SampleClockMode ) = 0; + + virtual double get_hardware_peak( + jack_port_t *port, + jack_nframes_t frames ) = 0; + + virtual double get_hardware_power( + jack_port_t *port, + jack_nframes_t frames ) = 0; + +}; + +#endif /* __jack_hardware_h__ */ diff --git a/qnx/wscript b/qnx/wscript index 00f23e50..3eabd0ad 100644 --- a/qnx/wscript +++ b/qnx/wscript @@ -41,3 +41,11 @@ def build(bld): deva = create_jack_driver_obj(bld, 'jack', 'ioaudio/ioaudio_driver.c', ['IOAUDIO', 'clientlib', 'PTHREAD']) deva.env['cxxshlib_PATTERN'] = 'deva-ctrl-%s.so' deva.install_path = '/lib/dll' + + ioaudio_driver_src = [ + '../common/memops.c', + #'ioaudio/generic_hw.c', + #'ioaudio/ioaudio_backend.c', + 'ioaudio/JackIoAudioDriver.cpp', + ] + create_jack_driver_obj(bld, 'ioaudio', ioaudio_driver_src, ['IOAUDIO', 'PTHREAD'])