diff --git a/Makefile b/Makefile new file mode 100755 index 00000000..3ff58399 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +.PHONY: all +all: + CC=${QNX_HOST}/usr/bin/aarch64-unknown-nto-qnx7.0.0-gcc CXX=${QNX_HOST}/usr/bin/aarch64-unknown-nto-qnx7.0.0-g++ AR=${QNX_HOST}/usr/bin/aarch64-unknown-nto-qnx7.0.0-ar LDFLAGS="-L${INSTALL_ROOT_nto}/aarch64le/lib -L${INSTALL_ROOT_nto}/aarch64le/usr/lib -L${QNX_TARGET}/aarch64le/lib -L${QNX_TARGET}/aarch64le/usr/lib" PKG_CONFIG_LIBDIR=${INSTALL_ROOT_nto}/aarch64le/usr/lib/pkgconfig ./waf configure --platform=qnx --prefix=/usr --libdir=/usr/lib64 + ./waf build + ./waf install --destdir=${INSTALL_ROOT_nto}/aarch64le + +.PHONY: install +install: all + +.PHONY:clean +clean: + # ignore error codes otherwise it would for example fail if clean is called before configure + -./waf clean + diff --git a/common/JackAudioAdapterInterface.cpp b/common/JackAudioAdapterInterface.cpp index ee90edd4..0aeffa05 100644 --- a/common/JackAudioAdapterInterface.cpp +++ b/common/JackAudioAdapterInterface.cpp @@ -22,7 +22,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #endif #include "JackAudioAdapter.h" -#ifndef MY_TARGET_OS_IPHONE +#if !defined(MY_TARGET_OS_IPHONE) && !defined(__QNXNTO__) #include "JackLibSampleRateResampler.h" #endif #include "JackTime.h" @@ -185,7 +185,7 @@ namespace Jack fRunning = false; } -#ifdef MY_TARGET_OS_IPHONE +#if defined(MY_TARGET_OS_IPHONE) || defined(__QNXNTO__) void JackAudioAdapterInterface::Create() {} #else diff --git a/common/JackClient.cpp b/common/JackClient.cpp index b03dbc25..b8303ab0 100644 --- a/common/JackClient.cpp +++ b/common/JackClient.cpp @@ -34,6 +34,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. using namespace std; +#ifdef __QNXNTO__ +void __attribute__((constructor)) init_output_for_percent_s_NULL_for_clients(); +void init_output_for_percent_s_NULL_for_clients() +{ + extern const char *output_for_percent_s_NULL; + output_for_percent_s_NULL = "(null)"; +} +#endif + namespace Jack { diff --git a/common/JackControlAPI.cpp b/common/JackControlAPI.cpp index bbabbac1..7e028948 100644 --- a/common/JackControlAPI.cpp +++ b/common/JackControlAPI.cpp @@ -663,7 +663,10 @@ jackctl_setup_signals( sigfillset(&allsignals); action.sa_handler = signal_handler; action.sa_mask = allsignals; - action.sa_flags = SA_RESTART|SA_RESETHAND; + action.sa_flags = SA_RESETHAND; +#ifndef __QNXNTO__ + action.sa_flags |= SA_RESTART; +#endif for (i = 1; i < NSIG; i++) { diff --git a/common/jack/systemdeps.h b/common/jack/systemdeps.h old mode 100644 new mode 100755 index 1a111927..2a2ffbeb --- a/common/jack/systemdeps.h +++ b/common/jack/systemdeps.h @@ -107,7 +107,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #endif /* _WIN32 && !__CYGWIN__ && !GNU_WIN32 */ -#if defined(__APPLE__) || defined(__linux__) || defined(__sun__) || defined(sun) || defined(__unix__) || defined(__CYGWIN__) || defined(GNU_WIN32) +#if defined(__APPLE__) || defined(__linux__) || defined(__sun__) || defined(sun) || defined(__unix__) || defined(__CYGWIN__) || defined(GNU_WIN32) || defined(__QNXNTO__) #if defined(__CYGWIN__) || defined(GNU_WIN32) #include diff --git a/common/netjack_packet.c b/common/netjack_packet.c index 4671368f..e59806c1 100644 --- a/common/netjack_packet.c +++ b/common/netjack_packet.c @@ -428,7 +428,9 @@ netjack_poll (int sockfd, int timeout) action.sa_handler = SIG_DFL; action.sa_mask = sigmask; +#ifndef __QNXNTO__ action.sa_flags = SA_RESTART; +#endif for (i = 1; i < NSIG; i++) if (sigismember (&sigmask, i)) diff --git a/common/shm.c b/common/shm.c index b1e7fc71..690de799 100644 --- a/common/shm.c +++ b/common/shm.c @@ -49,11 +49,16 @@ #include #include #include -#include #include #include #include "promiscuous.h" +#ifdef __QNXNTO__ +#include +#else +#include +#endif + #endif #include "shm.h" @@ -148,8 +153,6 @@ static jack_shm_registry_t *jack_shm_registry = NULL; #define JACK_SHM_REGISTRY_KEY JACK_SEMAPHORE_KEY #endif -static int semid = -1; - #ifdef WIN32 #include @@ -178,12 +181,61 @@ static BOOL check_process_running(DWORD process_id) } static int -semaphore_init () {return 0;} +jack_shm_lock_registry () {return 0;} + +static void +jack_shm_unlock_registry () { } + +#elif __QNXNTO__ +#include + +static sem_t* semid = SEM_FAILED; + +static int +semaphore_init () +{ + const char name[] = "/jack-shm-registry-lock"; + const int oflag = O_CREAT | O_RDWR; + const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + if ((semid = sem_open(name, oflag, mode, 1)) == SEM_FAILED) { + jack_error("Creating semaphore %s failed", name); + return -1; + } + + return 0; +} + +static int +jack_shm_lock_registry (void) +{ + if (semid == SEM_FAILED) { + if (semaphore_init () < 0) + return -1; + } + + // TODO automatically unblock in case the process terminates + const int ret = sem_wait(semid); + if (ret < 0) { + jack_error("sem_wait() failed with %s", strerror(ret)); + return -1; + } -static int -semaphore_add (int value) {return 0;} + return 0; +} + +static void +jack_shm_unlock_registry (void) +{ + const int ret = sem_post(semid); + + if (ret < 0) { + jack_error("sem_post() failed with %s", strerror(ret)); + } +} #else +static int semid = -1; + /* all semaphore errors are fatal -- issue message, but do not return */ static void semaphore_error (char *msg) @@ -247,8 +299,6 @@ semaphore_add (int value) return 0; } -#endif - static int jack_shm_lock_registry (void) { @@ -266,6 +316,8 @@ jack_shm_unlock_registry (void) semaphore_add (1); } +#endif + static void jack_shm_init_registry () { @@ -1308,4 +1360,3 @@ jack_attach_shm_read (jack_shm_info_t* si) } #endif /* !USE_POSIX_SHM */ - diff --git a/common/wscript b/common/wscript old mode 100644 new mode 100755 index cb794e28..c41089ea --- a/common/wscript +++ b/common/wscript @@ -28,6 +28,8 @@ def create_jack_process_obj(bld, target, sources, uselib = None, framework = Non env_includes = ['../macosx', '../posix', '../macosx/coreaudio'] if bld.env['IS_LINUX']: env_includes = ['../linux', '../posix', '../linux/alsa'] + if bld.env['IS_QNX']: + env_includes = ['../qnx', '../posix'] if bld.env['IS_SUN']: env_includes = ['../solaris', '../posix', '../solaris/oss'] if bld.env['IS_WINDOWS']: @@ -36,7 +38,7 @@ def create_jack_process_obj(bld, target, sources, uselib = None, framework = Non process.name = target process.target = target process.source = sources - if bld.env['IS_LINUX'] or bld.env['IS_MACOSX']: + if bld.env['IS_LINUX'] or bld.env['IS_MACOSX'] or bld.env['IS_QNX']: process.env.append_value('CPPFLAGS', '-fvisibility=hidden') process.install_path = '${ADDON_DIR}/' process.use = [uselib.name] @@ -92,6 +94,22 @@ def build(bld): uselib.append('RT') uselib.append('DL') + if bld.env['IS_QNX']: + common_libsources += [ + 'JackDebugClient.cpp', + 'timestamps.c', + 'promiscuous.c', + '../posix/JackPosixThread.cpp', + '../posix/JackPosixProcessSync.cpp', + '../posix/JackPosixMutex.cpp', + '../posix/JackSocket.cpp', + '../posix/JackFifo.cpp', + '../linux/JackLinuxTime.c', + ] + includes = ['../qnx', '../posix'] + includes + uselib.append('SOCKET') + # libdl and librt is included in libc in QNX + if bld.env['IS_SUN']: common_libsources += [ 'JackDebugClient.cpp', @@ -163,7 +181,7 @@ def build(bld): 'JackMetadata.cpp', ] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: clientlib.source += [ '../posix/JackSocketClientChannel.cpp', '../posix/JackPosixServerLaunch.cpp', @@ -192,7 +210,7 @@ def build(bld): if not bld.env['IS_WINDOWS']: clientlib.vnum = bld.env['JACK_API_VERSION'] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: clientlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_MACOSX']: @@ -270,7 +288,7 @@ def build(bld): 'JackMetadata.cpp', ] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: serverlib.source += [ '../posix/JackSocketServerChannel.cpp', '../posix/JackSocketNotifyChannel.cpp', @@ -307,7 +325,7 @@ def build(bld): if not bld.env['IS_WINDOWS']: serverlib.vnum = bld.env['JACK_API_VERSION'] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: serverlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') if bld.env['IS_MACOSX']: @@ -333,7 +351,7 @@ def build(bld): if skipshared: netlib.env['SHLIB_MARKER'] = '' netlib.use += ['WS2_32', 'WINMM'] - elif not bld.env['IS_MACOSX']: + elif not bld.env['IS_MACOSX'] and not bld.env['IS_QNX']: netlib.use += ['RT'] netlib.install_path = '${LIBDIR}' netlib.source = [ @@ -347,7 +365,7 @@ def build(bld): 'JackGlobals.cpp', 'ringbuffer.c'] - if bld.env['IS_LINUX']: + if bld.env['IS_LINUX'] or bld.env['IS_QNX']: netlib.source += ['../posix/JackNetUnixSocket.cpp','../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', '../linux/JackLinuxTime.c'] netlib.env.append_value('CPPFLAGS', '-fvisibility=hidden') diff --git a/example-clients/wscript b/example-clients/wscript index a8857aa7..065fcf15 100644 --- a/example-clients/wscript +++ b/example-clients/wscript @@ -49,6 +49,8 @@ def build(bld): os_incdir = ['../linux', '../posix'] if bld.env['IS_MACOSX']: os_incdir = ['../macosx', '../posix'] + if bld.env['IS_QNX']: + os_incdir = ['../qnx', '../posix'] if bld.env['IS_SUN']: os_incdir = ['../solaris', '../posix'] if bld.env['IS_WINDOWS']: @@ -76,7 +78,7 @@ def build(bld): prog.use = use if bld.env['IS_LINUX']: prog.use += ['RT', 'M'] - if bld.env['IS_SUN']: + if bld.env['IS_SUN'] or bld.env['IS_QNX']: prog.use += ['M'] #prog.cflags = ['-Wno-deprecated-declarations', '-Wno-misleading-indentation'] #prog.cxxflags = ['-Wno-deprecated-declarations', '-Wno-misleading-indentation'] @@ -90,7 +92,7 @@ def build(bld): prog.use = ['clientlib'] if bld.env['IS_LINUX']: prog.use += ['RT', 'READLINE'] - if bld.env['IS_MACOSX']: + if bld.env['IS_MACOSX'] or bld.env['IS_QNX']: prog.use += ['READLINE'] if bld.env['IS_WINDOWS']: prog.use += ['READLINE'] @@ -101,7 +103,7 @@ def build(bld): prog.includes = os_incdir + ['../common/jack', '../common'] prog.source = 'capture_client.c' prog.use = ['clientlib'] - if bld.env['IS_MACOSX']: + if bld.env['IS_MACOSX'] or bld.env['IS_QNX']: prog.use += ['SNDFILE'] if bld.env['IS_LINUX']: prog.use += ['RT', 'SNDFILE'] @@ -111,7 +113,7 @@ def build(bld): prog.uselib = ['SNDFILE'] prog.target = 'jack_rec' - if bld.env['IS_LINUX'] or bld.env['IS_MACOSX']: + if bld.env['IS_LINUX'] or bld.env['IS_MACOSX'] or bld.env['IS_QNX']: prog = bld(features = 'c cprogram') prog.includes = os_incdir + ['.', '..', '../common/jack', '../common'] prog.source = ['netsource.c', '../common/netjack_packet.c'] diff --git a/linux/alsa/JackAlsaDriver.cpp b/linux/alsa/JackAlsaDriver.cpp index 0bd904d5..6fcdf488 100644 --- a/linux/alsa/JackAlsaDriver.cpp +++ b/linux/alsa/JackAlsaDriver.cpp @@ -204,6 +204,7 @@ int JackAlsaDriver::Detach() return JackAudioDriver::Detach(); } +#ifndef __QNXNTO__ extern "C" char* get_control_device_name(const char * device_name) { char * ctl_name; @@ -233,7 +234,9 @@ extern "C" char* get_control_device_name(const char * device_name) return ctl_name; } +#endif +#ifndef __QNXNTO__ static int card_to_num(const char* device) { int err; @@ -273,6 +276,7 @@ free: fail: return i; } +#endif int JackAlsaDriver::Open(jack_nframes_t nframes, jack_nframes_t user_nperiods, @@ -301,6 +305,8 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, } alsa_midi_t *midi = 0; + +#ifndef __QNXNTO__ #ifndef __ANDROID__ if (strcmp(midi_driver_name, "seq") == 0) midi = alsa_seqmidi_new((jack_client_t*)this, 0); @@ -333,6 +339,7 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, } } } +#endif fDriver = alsa_driver_new ((char*)"alsa_pcm", (char*)playback_driver_name, (char*)capture_driver_name, NULL, @@ -356,12 +363,14 @@ int JackAlsaDriver::Open(jack_nframes_t nframes, // ALSA driver may have changed the in/out values fCaptureChannels = ((alsa_driver_t *)fDriver)->capture_nchannels; fPlaybackChannels = ((alsa_driver_t *)fDriver)->playback_nchannels; +#ifndef __QNXNTO__ if (JackServerGlobals::on_device_reservation_loop != NULL) { device_reservation_loop_running = true; if (JackPosixThread::StartImp(&fReservationLoopThread, 0, 0, on_device_reservation_loop, NULL) != 0) { device_reservation_loop_running = false; } } +#endif return 0; } else { Close(); @@ -378,6 +387,7 @@ int JackAlsaDriver::Close() alsa_driver_delete((alsa_driver_t*)fDriver); } +#ifndef __QNXNTO__ if (device_reservation_loop_running) { device_reservation_loop_running = false; JackPosixThread::StopImp(fReservationLoopThread); @@ -398,6 +408,7 @@ int JackAlsaDriver::Close() JackServerGlobals::on_device_release(audio_name); } } +#endif return res; } @@ -578,6 +589,7 @@ extern "C" { #endif +#ifndef __QNXNTO__ static jack_driver_param_constraint_desc_t * enum_alsa_devices() @@ -667,6 +679,7 @@ fail: jack_constraint_free(constraint_ptr); return NULL; } +#endif static int dither_opt (char c, DitherAlgorithm* dither) @@ -705,11 +718,14 @@ SERVER_EXPORT const jack_driver_desc_t* driver_get_descriptor () desc = jack_driver_descriptor_construct("alsa", JackDriverMaster, "Linux ALSA API based audio backend", &filler); strcpy(value.str, "hw:0"); +#ifndef __QNXNTO__ #ifdef __ANDROID__ jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, NULL, "ALSA device name", NULL); #else jack_driver_descriptor_add_parameter(desc, &filler, "device", 'd', JackDriverParamString, &value, enum_alsa_devices(), "ALSA device name", NULL); #endif +#endif + strcpy(value.str, "none"); jack_driver_descriptor_add_parameter(desc, &filler, "capture", 'C', JackDriverParamString, &value, NULL, "Provide capture ports. Optionally set device", NULL); @@ -951,8 +967,13 @@ void SetTime(jack_time_t time) int Restart() { int res; - if ((res = g_alsa_driver->Stop()) == 0) { - res = g_alsa_driver->Start(); + if ((res = g_alsa_driver->Stop()) != 0) { + jack_error("restart: stop driver failed"); + return res; + } + if ((res = g_alsa_driver->Start()) != 0) { + jack_error("restart: start driver failed"); + return res; } return res; } diff --git a/linux/alsa/alsa_driver.c b/linux/alsa/alsa_driver.c index 977d4c42..546b3ebd 100644 --- a/linux/alsa/alsa_driver.c +++ b/linux/alsa/alsa_driver.c @@ -95,6 +95,23 @@ jack_driver_nt_init (jack_driver_nt_t * driver) driver->nt_run_cycle = 0; } +static int +alsa_driver_prepare (snd_pcm_t *handle, int is_capture) +{ + int res = 0; + +#ifndef __QNXNTO__ + res = snd_pcm_prepare (handle); +#else + res = snd_pcm_plugin_prepare(handle, is_capture); +#endif + if (res < 0) { + jack_error("error preparing: %s", snd_strerror(res)); + } + + return res; +} + static void alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) { @@ -111,6 +128,20 @@ alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) driver->capture_addr = 0; } +#ifdef __QNXNTO__ + if (driver->playback_areas_ptr) { + free(driver->playback_areas_ptr); + driver->playback_areas = NULL; + driver->playback_areas_ptr = NULL; + } + + if (driver->capture_areas_ptr) { + free(driver->capture_areas_ptr); + driver->capture_areas = NULL; + driver->capture_areas_ptr = NULL; + } +#endif + if (driver->playback_interleave_skip) { free (driver->playback_interleave_skip); driver->playback_interleave_skip = NULL; @@ -132,6 +163,8 @@ alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) } } +#ifndef __QNXNTO__ + static int alsa_driver_check_capabilities (alsa_driver_t *driver) { @@ -266,6 +299,7 @@ alsa_driver_hw_specific (alsa_driver_t *driver, int hw_monitoring, return 0; } +#endif static void alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) @@ -352,6 +386,119 @@ alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) } } +#ifdef __QNXNTO__ + +static int +alsa_driver_allocate_buffer(alsa_driver_t *driver, int frames, int channels, bool is_capture) +{ + const long ALIGNMENT = 32; + + // TODO driver->playback_sample_bytes + char* const fBuffer = malloc(channels * ((sizeof(alsa_driver_default_format_t)) * frames) + ALIGNMENT); + if(fBuffer) { + /* Provide an 32 byte aligned buffer */ + char* const aligned_buffer = (char*)((uintptr_t)fBuffer & ~(ALIGNMENT-1)) + ALIGNMENT; + + if(is_capture) { + driver->capture_areas_ptr = fBuffer; + driver->capture_areas = aligned_buffer; + } else { + driver->playback_areas_ptr = fBuffer; + driver->playback_areas = aligned_buffer; + } + + return 0; + } + + jack_error ("ALSA: could not allocate audio buffer"); + return -1; +} + +static int +alsa_driver_get_setup (alsa_driver_t *driver, snd_pcm_channel_setup_t *setup, bool is_capture) +{ + int err = 0; + + memset(setup, 0, sizeof(*setup)); + setup->channel = is_capture; + + if(is_capture) { + err = snd_pcm_plugin_setup(driver->capture_handle, setup); + } else { + err = snd_pcm_plugin_setup(driver->playback_handle, setup); + } + if (err < 0) { + jack_error("couldn't get channel setup for %s, err = %s ", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + strerror(err)); + return -1; + } + + return 0; +} + +static int +alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, + const char *stream_name, + snd_pcm_t *handle, + unsigned int *nperiodsp, + channel_t *nchns, + unsigned long sample_width, + bool is_capture) +{ + int err = 0; + snd_pcm_channel_info_t ch_info; + snd_pcm_channel_params_t ch_params; + const unsigned long sample_size = is_capture ? driver->capture_sample_bytes + : driver->playback_sample_bytes; + + memset(&ch_info, 0, sizeof(ch_info)); + /*A pointer to a snd_pcm_channel_info_t structure that snd_pcm_plugin_info() fills in with information about the PCM channel. + * Before calling snd_pcm_plugin_info(), set the info structure's channel member to specify the direction. + * This function sets all the other members.*/ + ch_info.channel = is_capture; + if ((err = snd_pcm_plugin_info(handle, &ch_info)) < 0) { + jack_error("couldn't get channel info for %s, %s, err = (%s)", stream_name, device_name, snd_strerror(err)); + alsa_driver_delete(driver); + return -1; + } + + if (!*nchns) { + *nchns = ch_info.max_voices; + } + + ch_params.mode = SND_PCM_MODE_BLOCK; + ch_params.start_mode = SND_PCM_START_GO; + ch_params.stop_mode = SND_PCM_STOP_STOP; + ch_params.buf.block.frag_size = driver->frames_per_cycle * *nchns * sample_size; + + *nperiodsp = driver->user_nperiods; + ch_params.buf.block.frags_min = 1; + /* the maximal available periods (-1 due to one period is always processed + * by DMA and therefore not free) + */ + ch_params.buf.block.frags_max = *nperiodsp - 1; + ch_params.format.interleave = 1; + ch_params.format.rate = driver->frame_rate; + ch_params.format.voices = *nchns; + ch_params.channel = is_capture; + + ch_params.format.format = (sample_width == 4) ? SND_PCM_SFMT_S32_LE : SND_PCM_SFMT_S16_LE; + /*Set the configurable parameters for a PCM channel*/ + if ((err = snd_pcm_plugin_params(handle, &ch_params)) < 0) { + jack_error("snd_pcm_plugin_params failed for %s %s with err = (%s)", snd_strerror(err), stream_name, device_name); + alsa_driver_delete(driver); + return -1; + } + + /* + * The buffer has to be able to hold a full HW audio buffer + * (periods * period_size) because the silence prefill will fill the + * complete buffer + */ + return alsa_driver_allocate_buffer(driver, driver->frames_per_cycle * *nperiodsp, *nchns, is_capture); +} +#else static int alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, const char *stream_name, @@ -613,6 +760,58 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, return 0; } +#endif + +#ifdef __QNXNTO__ +static int +alsa_driver_check_format (unsigned int format) +{ +#else +static int +alsa_driver_check_format (snd_pcm_format_t format) +{ +#endif + + switch (format) { +#ifndef __QNXNTO__ + case SND_PCM_FORMAT_FLOAT_LE: + case SND_PCM_FORMAT_S24_3LE: + case SND_PCM_FORMAT_S24_3BE: + case SND_PCM_FORMAT_S24_LE: + case SND_PCM_FORMAT_S24_BE: + case SND_PCM_FORMAT_S32_BE: + case SND_PCM_FORMAT_S16_BE: +#endif + case SND_PCM_FORMAT_S16_LE: + case SND_PCM_FORMAT_S32_LE: + break; + default: + jack_error ("format not supported %d", format); + return -1; + } + + return 0; +} + +static void +alsa_driver_set_sample_bytes (alsa_driver_t *driver) +{ +#ifdef __QNXNTO__ + driver->playback_sample_bytes = + snd_pcm_format_width (driver->playback_sample_format) + / 8; + driver->capture_sample_bytes = + snd_pcm_format_width (driver->capture_sample_format) + / 8; +#else + driver->playback_sample_bytes = + snd_pcm_format_physical_width (driver->playback_sample_format) + / 8; + driver->capture_sample_bytes = + snd_pcm_format_physical_width (driver->capture_sample_format) + / 8; +#endif +} static int alsa_driver_set_parameters (alsa_driver_t *driver, @@ -620,7 +819,14 @@ alsa_driver_set_parameters (alsa_driver_t *driver, jack_nframes_t user_nperiods, jack_nframes_t rate) { +#ifdef __QNXNTO__ + snd_pcm_channel_setup_t c_setup; + snd_pcm_channel_setup_t p_setup; + jack_nframes_t p_periods = 0; + jack_nframes_t c_periods = 0; +#else int dir; +#endif snd_pcm_uframes_t p_period_size = 0; snd_pcm_uframes_t c_period_size = 0; channel_t chn; @@ -637,37 +843,90 @@ alsa_driver_set_parameters (alsa_driver_t *driver, rate, frames_per_cycle, (((float)frames_per_cycle / (float) rate) * 1000.0f), user_nperiods); if (driver->capture_handle) { - if (alsa_driver_configure_stream ( - driver, - driver->alsa_name_capture, - "capture", - driver->capture_handle, - driver->capture_hw_params, - driver->capture_sw_params, - &driver->capture_nperiods, - &driver->capture_nchannels, - driver->capture_sample_bytes)) { +#ifdef __QNXNTO__ + err = alsa_driver_configure_stream ( + driver, + driver->alsa_name_capture, + "capture", + driver->capture_handle, + &driver->capture_nperiods, + &driver->capture_nchannels, + driver->capture_sample_bytes, + SND_PCM_CHANNEL_CAPTURE); +#else + err = alsa_driver_configure_stream ( + driver, + driver->alsa_name_capture, + "capture", + driver->capture_handle, + driver->capture_hw_params, + driver->capture_sw_params, + &driver->capture_nperiods, + &driver->capture_nchannels, + driver->capture_sample_bytes); +#endif + if (err) { jack_error ("ALSA: cannot configure capture channel"); return -1; } } if (driver->playback_handle) { - if (alsa_driver_configure_stream ( - driver, - driver->alsa_name_playback, - "playback", - driver->playback_handle, - driver->playback_hw_params, - driver->playback_sw_params, - &driver->playback_nperiods, - &driver->playback_nchannels, - driver->playback_sample_bytes)) { +#ifdef __QNXNTO__ + err = alsa_driver_configure_stream ( + driver, + driver->alsa_name_playback, + "playback", + driver->playback_handle, + &driver->playback_nperiods, + &driver->playback_nchannels, + driver->playback_sample_bytes, + SND_PCM_CHANNEL_PLAYBACK); +#else + err = alsa_driver_configure_stream ( + driver, + driver->alsa_name_playback, + "playback", + driver->playback_handle, + driver->playback_hw_params, + driver->playback_sw_params, + &driver->playback_nperiods, + &driver->playback_nchannels, + driver->playback_sample_bytes); +#endif + if (err) { jack_error ("ALSA: cannot configure playback channel"); return -1; } } +#ifdef __QNXNTO__ + if (driver->capture_handle) { + err = alsa_driver_get_setup(driver, &c_setup, SND_PCM_CHANNEL_CAPTURE); + if(err < 0) { + return -1; + } + cr = c_setup.format.rate; + c_period_size = c_setup.buf.block.frag_size / driver->capture_nchannels + / driver->capture_sample_bytes; + c_periods = c_setup.buf.block.frags; + driver->capture_sample_format = c_setup.format.format; + driver->capture_interleaved = c_setup.format.interleave; + } + if (driver->playback_handle) { + err = alsa_driver_get_setup(driver, &p_setup, SND_PCM_CHANNEL_PLAYBACK); + if(err < 0) { + return -1; + } + pr = p_setup.format.rate; + p_period_size = p_setup.buf.block.frag_size / driver->playback_nchannels + / driver->playback_sample_bytes; + p_periods = p_setup.buf.block.frags; + driver->playback_sample_format = p_setup.format.format; + driver->playback_interleaved = p_setup.format.interleave; + } +#else + /* check the rate, since that's rather important */ if (driver->playback_handle) { @@ -679,6 +938,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, snd_pcm_hw_params_get_rate (driver->capture_hw_params, &cr, &dir); } +#endif if (driver->capture_handle && driver->playback_handle) { if (cr != pr) { @@ -716,6 +976,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, /* check the fragment size, since that's non-negotiable */ if (driver->playback_handle) { +#ifndef __QNXNTO__ snd_pcm_access_t access; err = snd_pcm_hw_params_get_period_size ( @@ -728,7 +989,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->playback_interleaved = (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) || (access == SND_PCM_ACCESS_MMAP_COMPLEX); - +#endif if (p_period_size != driver->frames_per_cycle) { jack_error ("alsa_pcm: requested an interrupt every %" PRIu32 @@ -736,9 +997,21 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->frames_per_cycle, p_period_size); return -1; } +#ifdef __QNXNTO__ + if (p_periods != driver->user_nperiods) { + jack_error ("alsa_pcm: requested %" + PRIu32 + " periods but got %" + PRIu32 + " periods for playback", + driver->user_nperiods, p_periods); + return -1; + } +#endif } if (driver->capture_handle) { +#ifndef __QNXNTO__ snd_pcm_access_t access; err = snd_pcm_hw_params_get_period_size ( @@ -751,7 +1024,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->capture_interleaved = (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) || (access == SND_PCM_ACCESS_MMAP_COMPLEX); - +#endif if (c_period_size != driver->frames_per_cycle) { jack_error ("alsa_pcm: requested an interrupt every %" PRIu32 @@ -759,56 +1032,44 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->frames_per_cycle, c_period_size); return -1; } +#ifdef __QNXNTO__ + /* capture buffers can be configured bigger but it should fail + * if they are smaller as expected + */ + if (c_periods < driver->user_nperiods) { + jack_error ("alsa_pcm: requested %" + PRIu32 + " periods but got %" + PRIu32 + " periods for capture", + driver->user_nperiods, c_periods); + return -1; + } +#endif } - driver->playback_sample_bytes = - snd_pcm_format_physical_width (driver->playback_sample_format) - / 8; - driver->capture_sample_bytes = - snd_pcm_format_physical_width (driver->capture_sample_format) - / 8; + alsa_driver_set_sample_bytes(driver); if (driver->playback_handle) { - switch (driver->playback_sample_format) { - case SND_PCM_FORMAT_FLOAT_LE: - case SND_PCM_FORMAT_S32_LE: - case SND_PCM_FORMAT_S24_3LE: - case SND_PCM_FORMAT_S24_3BE: - case SND_PCM_FORMAT_S24_LE: - case SND_PCM_FORMAT_S24_BE: - case SND_PCM_FORMAT_S16_LE: - case SND_PCM_FORMAT_S32_BE: - case SND_PCM_FORMAT_S16_BE: - break; - - default: + err = alsa_driver_check_format(driver->playback_sample_format); + if(err < 0) { jack_error ("programming error: unhandled format " "type for playback"); - exit (1); + return -1; } } if (driver->capture_handle) { - switch (driver->capture_sample_format) { - case SND_PCM_FORMAT_FLOAT_LE: - case SND_PCM_FORMAT_S32_LE: - case SND_PCM_FORMAT_S24_3LE: - case SND_PCM_FORMAT_S24_3BE: - case SND_PCM_FORMAT_S24_LE: - case SND_PCM_FORMAT_S24_BE: - case SND_PCM_FORMAT_S16_LE: - case SND_PCM_FORMAT_S32_BE: - case SND_PCM_FORMAT_S16_BE: - break; - - default: + err = alsa_driver_check_format(driver->capture_sample_format); + if(err < 0) { jack_error ("programming error: unhandled format " "type for capture"); - exit (1); + return -1; } } if (driver->playback_interleaved) { +#ifndef __QNXNTO__ const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames; if (snd_pcm_mmap_begin(driver->playback_handle, @@ -817,13 +1078,21 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->alsa_name_playback); return -1; } + + // TODO does not work for capture only driver->interleave_unit = snd_pcm_format_physical_width ( driver->playback_sample_format) / 8; +#else + driver->interleave_unit = snd_pcm_format_width( + driver->playback_sample_format) / 8; +#endif } else { + driver->interleave_unit = 0; /* NOT USED */ } +#ifndef __QNXNTO__ if (driver->capture_interleaved) { const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames; @@ -834,7 +1103,7 @@ alsa_driver_set_parameters (alsa_driver_t *driver, return -1; } } - +#endif if (driver->playback_nchannels > driver->capture_nchannels) { driver->max_nchannels = driver->playback_nchannels; driver->user_nchannels = driver->capture_nchannels; @@ -932,6 +1201,23 @@ alsa_driver_reset_parameters (alsa_driver_t *driver, user_nperiods, rate); } +#ifdef __QNXNTO__ +static int +snd_pcm_poll_descriptors_count(snd_pcm_t *pcm) +{ + return 1; +} + +static int +snd_pcm_poll_descriptors_revents(snd_pcm_t *pcm, struct pollfd *pfds, + unsigned int nfds, unsigned short *revents) +{ + *revents = pfds->revents; + + return 0; +} +#endif + static int alsa_driver_get_channel_addresses (alsa_driver_t *driver, snd_pcm_uframes_t *capture_avail, @@ -939,10 +1225,11 @@ alsa_driver_get_channel_addresses (alsa_driver_t *driver, snd_pcm_uframes_t *capture_offset, snd_pcm_uframes_t *playback_offset) { - int err; channel_t chn; if (capture_avail) { +#ifndef __QNXNTO__ + int err; if ((err = snd_pcm_mmap_begin ( driver->capture_handle, &driver->capture_areas, (snd_pcm_uframes_t *) capture_offset, @@ -959,9 +1246,25 @@ alsa_driver_get_channel_addresses (alsa_driver_t *driver, + ((a->first + a->step * *capture_offset) / 8); driver->capture_interleave_skip[chn] = (unsigned long ) (a->step / 8); } +#else + for (chn = 0; chn < driver->capture_nchannels; chn++) { + char* const a = driver->capture_areas; + if (driver->capture_interleaved) { + driver->capture_addr[chn] = &a[chn * driver->capture_sample_bytes]; + driver->capture_interleave_skip[chn] = driver->capture_nchannels * + driver->capture_sample_bytes; + } else { + driver->capture_addr[chn] = &a[chn * + driver->capture_sample_bytes * driver->frames_per_cycle]; + driver->capture_interleave_skip[chn] = driver->capture_sample_bytes; + } + } +#endif } if (playback_avail) { +#ifndef __QNXNTO__ + int err; if ((err = snd_pcm_mmap_begin ( driver->playback_handle, &driver->playback_areas, (snd_pcm_uframes_t *) playback_offset, @@ -978,11 +1281,39 @@ alsa_driver_get_channel_addresses (alsa_driver_t *driver, + ((a->first + a->step * *playback_offset) / 8); driver->playback_interleave_skip[chn] = (unsigned long ) (a->step / 8); } +#else + for (chn = 0; chn < driver->playback_nchannels; chn++) { + char* const a = driver->playback_areas; + if (driver->playback_interleaved) { + driver->playback_addr[chn] = &a[chn * driver->playback_sample_bytes]; + driver->playback_interleave_skip[chn] = driver->playback_nchannels * + driver->playback_sample_bytes; + } else { + driver->playback_addr[chn] = &a[chn * + driver->playback_sample_bytes * driver->frames_per_cycle]; + driver->playback_interleave_skip[chn] = driver->playback_sample_bytes; + } + } +#endif } return 0; } +#ifdef __QNXNTO__ +static int +alsa_driver_stream_start(snd_pcm_t *pcm, bool is_capture) +{ + return snd_pcm_channel_go(pcm, is_capture); +} +#else +static int +alsa_driver_stream_start(snd_pcm_t *pcm, bool is_capture) +{ + return snd_pcm_start(pcm); +} +#endif + int alsa_driver_start (alsa_driver_t *driver) { @@ -994,7 +1325,7 @@ alsa_driver_start (alsa_driver_t *driver) driver->poll_next = 0; if (driver->playback_handle) { - if ((err = snd_pcm_prepare (driver->playback_handle)) < 0) { + if ((err = alsa_driver_prepare (driver->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { jack_error ("ALSA: prepare error for playback on " "\"%s\" (%s)", driver->alsa_name_playback, snd_strerror(err)); @@ -1004,7 +1335,7 @@ alsa_driver_start (alsa_driver_t *driver) if ((driver->capture_handle && driver->capture_and_playback_not_synced) || !driver->playback_handle) { - if ((err = snd_pcm_prepare (driver->capture_handle)) < 0) { + if ((err = alsa_driver_prepare (driver->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { jack_error ("ALSA: prepare error for capture on \"%s\"" " (%s)", driver->alsa_name_capture, snd_strerror(err)); @@ -1052,20 +1383,24 @@ alsa_driver_start (alsa_driver_t *driver) (driver->midi->start)(driver->midi); if (driver->playback_handle) { + const jack_nframes_t silence_frames = driver->frames_per_cycle * + driver->playback_nperiods; /* fill playback buffer with zeroes, and mark all fragments as having data. */ +#ifndef __QNXNTO__ pavail = snd_pcm_avail_update (driver->playback_handle); - if (pavail != - driver->frames_per_cycle * driver->playback_nperiods) { + if (pavail != silence_frames) { jack_error ("ALSA: full buffer not available at start"); return -1; } +#endif if (alsa_driver_get_channel_addresses (driver, 0, &pavail, 0, &poffset)) { + jack_error("silence failed, get channel addresses"); return -1; } @@ -1080,16 +1415,23 @@ alsa_driver_start (alsa_driver_t *driver) for (chn = 0; chn < driver->playback_nchannels; chn++) { alsa_driver_silence_on_channel ( - driver, chn, - driver->user_nperiods - * driver->frames_per_cycle); + driver, chn, silence_frames); } - snd_pcm_mmap_commit (driver->playback_handle, poffset, - driver->user_nperiods - * driver->frames_per_cycle); +#ifdef __QNXNTO__ + const size_t bytes = silence_frames * driver->playback_nchannels * + driver->playback_sample_bytes; + if ((err = snd_pcm_plugin_write(driver->playback_handle, + driver->playback_areas, bytes)) < bytes) { + jack_error ("ALSA: could not complete write of %" + PRIu32 " frames: error = %d", silence_frames, err); + return -1; + } +#else + snd_pcm_mmap_commit (driver->playback_handle, poffset, silence_frames); +#endif - if ((err = snd_pcm_start (driver->playback_handle)) < 0) { + if ((err = alsa_driver_stream_start (driver->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { jack_error ("ALSA: could not start playback (%s)", snd_strerror (err)); return -1; @@ -1098,7 +1440,7 @@ alsa_driver_start (alsa_driver_t *driver) if ((driver->capture_handle && driver->capture_and_playback_not_synced) || !driver->playback_handle) { - if ((err = snd_pcm_start (driver->capture_handle)) < 0) { + if ((err = alsa_driver_stream_start (driver->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { jack_error ("ALSA: could not start capture (%s)", snd_strerror (err)); return -1; @@ -1138,7 +1480,13 @@ alsa_driver_stop (alsa_driver_t *driver) ClearOutput(); if (driver->playback_handle) { - if ((err = snd_pcm_drop (driver->playback_handle)) < 0) { +#ifdef __QNXNTO__ + /* In case of playback: Drain discards the frames */ + err = snd_pcm_plugin_playback_drain(driver->playback_handle); +#else + err = snd_pcm_drop (driver->playback_handle); +#endif + if (err < 0) { jack_error ("ALSA: channel flush for playback " "failed (%s)", snd_strerror (err)); return -1; @@ -1148,7 +1496,13 @@ alsa_driver_stop (alsa_driver_t *driver) if (!driver->playback_handle || driver->capture_and_playback_not_synced) { if (driver->capture_handle) { - if ((err = snd_pcm_drop (driver->capture_handle)) < 0) { +#ifdef __QNXNTO__ + /* In case of capture: Flush discards the frames */ + err = snd_pcm_plugin_flush(driver->capture_handle, SND_PCM_CHANNEL_CAPTURE); +#else + err = snd_pcm_drop (driver->capture_handle); +#endif + if (err < 0) { jack_error ("ALSA: channel flush for " "capture failed (%s)", snd_strerror (err)); @@ -1188,72 +1542,97 @@ alsa_driver_restart (alsa_driver_t *driver) } static int -alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs) +alsa_driver_get_status (alsa_driver_t *driver) { - snd_pcm_status_t *status; - int res; + snd_pcm_t *pcm_handle; - snd_pcm_status_alloca(&status); +#ifdef __QNXNTO__ + int res; + snd_pcm_channel_status_t status; +#endif if (driver->capture_handle) { - if ((res = snd_pcm_status(driver->capture_handle, status)) - < 0) { - jack_error("status error: %s", snd_strerror(res)); - } + pcm_handle = driver->capture_handle; } else { - if ((res = snd_pcm_status(driver->playback_handle, status)) - < 0) { - jack_error("status error: %s", snd_strerror(res)); - } + pcm_handle = driver->playback_handle; } - if (snd_pcm_status_get_state(status) == SND_PCM_STATE_SUSPENDED) - { - jack_log("**** alsa_pcm: pcm in suspended state, resuming it" ); +#ifdef __QNXNTO__ + memset (&status, 0, sizeof (status)); + status.channel = driver->capture_handle ? SND_PCM_CHANNEL_CAPTURE : + SND_PCM_CHANNEL_PLAYBACK; + res = snd_pcm_plugin_status(pcm_handle, &status); + if (res < 0) { + jack_error("status error: %s", snd_strerror(res)); + return -1; + } + return status.status; +#else + return snd_pcm_state(pcm_handle); +#endif +} + +static int +alsa_driver_xrun_recovery (alsa_driver_t *driver, float *delayed_usecs) +{ + int status; + int res; + + status = alsa_driver_get_status(driver); + if (status == SND_PCM_STATE_SUSPENDED) { if (driver->capture_handle) { - if ((res = snd_pcm_prepare(driver->capture_handle)) + if ((res = alsa_driver_prepare(driver->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { jack_error("error preparing after suspend: %s", snd_strerror(res)); } } if (driver->playback_handle) { - if ((res = snd_pcm_prepare(driver->playback_handle)) + if ((res = alsa_driver_prepare(driver->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { jack_error("error preparing after suspend: %s", snd_strerror(res)); } } } - if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN + // TODO overrun + if (status == SND_PCM_STATE_XRUN && driver->process_count > XRUN_REPORT_DELAY) { - struct timeval now, diff, tstamp; driver->xrun_count++; +#ifdef __QNXNTO__ + /* Timestamp api's are not available as per QNX Documentation */ + *delayed_usecs = 0; +#else + struct timeval now, diff, tstamp; snd_pcm_status_get_tstamp(status,&now); snd_pcm_status_get_trigger_tstamp(status, &tstamp); timersub(&now, &tstamp, &diff); *delayed_usecs = diff.tv_sec * 1000000.0 + diff.tv_usec; +#endif jack_log("**** alsa_pcm: xrun of at least %.3f msecs",*delayed_usecs / 1000.0); if (driver->capture_handle) { jack_log("Repreparing capture"); - if ((res = snd_pcm_prepare(driver->capture_handle)) < 0) { + if ((res = alsa_driver_prepare(driver->capture_handle, + SND_PCM_STREAM_CAPTURE)) < 0) { jack_error("error preparing after xrun: %s", snd_strerror(res)); } } if (driver->playback_handle) { jack_log("Repreparing playback"); - if ((res = snd_pcm_prepare(driver->playback_handle)) < 0) { + if ((res = alsa_driver_prepare(driver->playback_handle, + SND_PCM_STREAM_PLAYBACK)) < 0) { jack_error("error preparing after xrun: %s", snd_strerror(res)); } } } if (alsa_driver_restart (driver)) { + jack_error("xrun recovery failed to restart driver"); return -1; } return 0; } -void +static void alsa_driver_silence_untouched_channels (alsa_driver_t *driver, jack_nframes_t nframes) { @@ -1272,14 +1651,37 @@ alsa_driver_silence_untouched_channels (alsa_driver_t *driver, } } -void -alsa_driver_set_clock_sync_status (alsa_driver_t *driver, channel_t chn, - ClockSyncStatus status) +#ifdef __QNXNTO__ +static int +alsa_driver_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space, bool is_capture) { - driver->clock_sync_data[chn] = status; - alsa_driver_clock_sync_notify (driver, chn, status); + pfds->fd = snd_pcm_file_descriptor (pcm, is_capture); + pfds->events = POLLHUP|POLLNVAL; + pfds->events |= (is_capture == SND_PCM_STREAM_PLAYBACK) ? POLLOUT : POLLIN; + + return 0; } +static snd_pcm_sframes_t +alsa_driver_avail(alsa_driver_t *driver, snd_pcm_t *pcm, bool is_capture) +{ + /* QNX guarantees that after poll() event at least one perido is available */ + return driver->frames_per_cycle; +} +#else +static int +alsa_driver_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space, bool is_capture) +{ + return snd_pcm_poll_descriptors(pcm, pfds, space); +} + +static snd_pcm_sframes_t +alsa_driver_avail(alsa_driver_t *driver, snd_pcm_t *pcm, bool is_capture) +{ + return snd_pcm_avail_update(pcm); +} +#endif + static int under_gdb = FALSE; jack_nframes_t @@ -1320,16 +1722,16 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float nfds = 0; if (need_playback) { - snd_pcm_poll_descriptors (driver->playback_handle, + alsa_driver_poll_descriptors (driver->playback_handle, &driver->pfd[0], - driver->playback_nfds); + driver->playback_nfds, SND_PCM_STREAM_PLAYBACK); nfds += driver->playback_nfds; } if (need_capture) { - snd_pcm_poll_descriptors (driver->capture_handle, + alsa_driver_poll_descriptors (driver->capture_handle, &driver->pfd[nfds], - driver->capture_nfds); + driver->capture_nfds, SND_PCM_STREAM_CAPTURE); ci = nfds; nfds += driver->capture_nfds; } @@ -1506,8 +1908,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float } if (driver->capture_handle) { - if ((capture_avail = snd_pcm_avail_update ( - driver->capture_handle)) < 0) { + if ((capture_avail = alsa_driver_avail (driver, + driver->capture_handle, SND_PCM_STREAM_CAPTURE)) < 0) { if (capture_avail == -EPIPE) { xrun_detected = TRUE; } else { @@ -1521,8 +1923,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float } if (driver->playback_handle) { - if ((playback_avail = snd_pcm_avail_update ( - driver->playback_handle)) < 0) { + if ((playback_avail = alsa_driver_avail (driver, + driver->playback_handle, SND_PCM_STREAM_PLAYBACK)) < 0) { if (playback_avail == -EPIPE) { xrun_detected = TRUE; } else { @@ -1608,6 +2010,17 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) &offset, 0) < 0) { return -1; } + +#ifdef __QNXNTO__ + const size_t bytes = contiguous * driver->capture_nchannels * driver->capture_sample_bytes; + if ((err = snd_pcm_plugin_read(driver->capture_handle, + driver->capture_areas, bytes)) < bytes) { + jack_error ("ALSA: could not complete read of %" + PRIu32 " frames: error = %d", contiguous, err); + return -1; + } +#endif + // JACK2 /* for (chn = 0, node = driver->capture_ports; node; @@ -1626,12 +2039,14 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) */ ReadInput(orig_nframes, contiguous, nread); +#ifndef __QNXNTO__ if ((err = snd_pcm_mmap_commit (driver->capture_handle, offset, contiguous)) < 0) { jack_error ("ALSA: could not complete read of %" PRIu32 " frames: error = %d", contiguous, err); return -1; } +#endif nframes -= contiguous; nread += contiguous; @@ -1749,6 +2164,15 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) contiguous); } +#ifdef __QNXNTO__ + const size_t bytes = contiguous * driver->playback_nchannels * driver->playback_sample_bytes; + if ((err = snd_pcm_plugin_write(driver->playback_handle, + driver->playback_areas, bytes)) < bytes) { + jack_error ("ALSA: could not complete write of %" + PRIu32 " frames: error = %d", contiguous, err); + return -1; + } +#else if ((err = snd_pcm_mmap_commit (driver->playback_handle, offset, contiguous)) < 0) { jack_error ("ALSA: could not complete playback of %" @@ -1756,6 +2180,7 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) if (err != -EPIPE && err != -ESTRPIPE) return -1; } +#endif nframes -= contiguous; nwritten += contiguous; @@ -1841,6 +2266,7 @@ alsa_driver_delete (alsa_driver_t *driver) driver->capture_handle = 0; } +#ifndef __QNXNTO__ if (driver->capture_hw_params) { snd_pcm_hw_params_free (driver->capture_hw_params); driver->capture_hw_params = 0; @@ -1860,6 +2286,7 @@ alsa_driver_delete (alsa_driver_t *driver) snd_pcm_sw_params_free (driver->playback_sw_params); driver->playback_sw_params = 0; } +#endif if (driver->pfd) { free (driver->pfd); @@ -1980,6 +2407,121 @@ discover_alsa_using_apps () } } +static int +alsa_driver_open (alsa_driver_t *driver, bool is_capture) +{ + int err = 0; + char* current_apps; + + if(is_capture) { +#ifdef __QNXNTO__ + err = snd_pcm_open_name (&driver->capture_handle, + driver->alsa_name_capture, + SND_PCM_OPEN_CAPTURE | SND_PCM_OPEN_NONBLOCK); +#else + err = snd_pcm_open (&driver->capture_handle, + driver->alsa_name_capture, + SND_PCM_STREAM_CAPTURE, + SND_PCM_NONBLOCK); +#endif + } else { +#ifdef __QNXNTO__ + err = snd_pcm_open_name (&driver->playback_handle, + driver->alsa_name_playback, + SND_PCM_OPEN_PLAYBACK | SND_PCM_OPEN_NONBLOCK); +#else + err = snd_pcm_open (&driver->playback_handle, + driver->alsa_name_playback, + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK); +#endif + } + if (err < 0) { + switch (errno) { + case EBUSY: +#ifdef __ANDROID__ + jack_error ("\n\nATTENTION: The device \"%s\" is " + "already in use. Please stop the" + " application using it and " + "run JACK again", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback); +#else + current_apps = discover_alsa_using_apps (); + if (current_apps) { + jack_error ("\n\nATTENTION: The device \"%s\" is " + "already in use. The following applications " + " are using your soundcard(s) so you should " + " check them and stop them as necessary before " + " trying to start JACK again:\n\n%s", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + current_apps); + free (current_apps); + } else { + jack_error ("\n\nATTENTION: The device \"%s\" is " + "already in use. Please stop the" + " application using it and " + "run JACK again", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback); + } +#endif + break; + + case EPERM: + jack_error ("you do not have permission to open " + "the audio device \"%s\" for playback", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback); + break; + + case EINVAL: + jack_error ("the state of handle or the mode is invalid " + "or invalid state change occured \"%s\" for %s", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + is_capture ? "capture" : "playback"); + break; + + case ENOENT: + jack_error ("device \"%s\" does not exist for %s", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + is_capture ? "capture" : "playback"); + break; + + case ENOMEM: + jack_error ("Not enough memory available for allocation for \"%s\" for %s", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + is_capture ? "capture" : "playback"); + break; + + case SND_ERROR_INCOMPATIBLE_VERSION: + jack_error ("Version mismatch \"%s\" for %s", + is_capture ? driver->alsa_name_capture : driver->alsa_name_playback, + is_capture ? "capture" : "playback"); + break; + } + alsa_driver_delete (driver); + if(is_capture) { + driver->capture_handle = NULL; + } else { + driver->playback_handle = NULL; + } + } + + if (is_capture && driver->capture_handle) { +#ifdef __QNXNTO__ + snd_pcm_nonblock_mode (driver->capture_handle, 0); +#else + snd_pcm_nonblock (driver->capture_handle, 0); +#endif + } else if(!is_capture && driver->playback_handle) { +#ifdef __QNXNTO__ + snd_pcm_nonblock_mode (driver->playback_handle, 0); +#else + snd_pcm_nonblock (driver->playback_handle, 0); +#endif + } + + return err; +} + jack_driver_t * alsa_driver_new (char *name, char *playback_alsa_device, char *capture_alsa_device, @@ -2003,7 +2545,6 @@ alsa_driver_new (char *name, char *playback_alsa_device, ) { int err; - char* current_apps; alsa_driver_t *driver; jack_info ("creating alsa driver ... %s|%s|%" PRIu32 "|%" PRIu32 @@ -2052,6 +2593,12 @@ alsa_driver_new (char *name, char *playback_alsa_device, driver->capture_addr = 0; driver->playback_interleave_skip = NULL; driver->capture_interleave_skip = NULL; +#ifdef __QNXNTO__ + driver->playback_areas = NULL; + driver->playback_areas_ptr = NULL; + driver->capture_areas = NULL; + driver->capture_areas_ptr = NULL; +#endif driver->silent = 0; @@ -2087,113 +2634,29 @@ alsa_driver_new (char *name, char *playback_alsa_device, driver->midi = midi_driver; driver->xrun_recovery = 0; +#ifndef __QNXNTO__ if (alsa_driver_check_card_type (driver)) { alsa_driver_delete (driver); return NULL; } alsa_driver_hw_specific (driver, hw_monitoring, hw_metering); - - if (playing) { - if (snd_pcm_open (&driver->playback_handle, - playback_alsa_device, - SND_PCM_STREAM_PLAYBACK, - SND_PCM_NONBLOCK) < 0) { - switch (errno) { - case EBUSY: -#ifdef __ANDROID__ - jack_error ("\n\nATTENTION: The playback device \"%s\" is " - "already in use. Please stop the" - " application using it and " - "run JACK again", - playback_alsa_device); -#else - current_apps = discover_alsa_using_apps (); - if (current_apps) { - jack_error ("\n\nATTENTION: The playback device \"%s\" is " - "already in use. The following applications " - " are using your soundcard(s) so you should " - " check them and stop them as necessary before " - " trying to start JACK again:\n\n%s", - playback_alsa_device, - current_apps); - free (current_apps); - } else { - jack_error ("\n\nATTENTION: The playback device \"%s\" is " - "already in use. Please stop the" - " application using it and " - "run JACK again", - playback_alsa_device); - } #endif - alsa_driver_delete (driver); - return NULL; - case EPERM: - jack_error ("you do not have permission to open " - "the audio device \"%s\" for playback", - playback_alsa_device); - alsa_driver_delete (driver); - return NULL; - break; - } - - driver->playback_handle = NULL; - } - - if (driver->playback_handle) { - snd_pcm_nonblock (driver->playback_handle, 0); + if (playing) { + err = alsa_driver_open(driver, SND_PCM_STREAM_PLAYBACK); + if(err < 0) { + jack_error ("\n\nATTENTION: Opening of the playback device \"%s\" failed.", + playback_alsa_device); + return NULL; } } - - if (capturing) { - if (snd_pcm_open (&driver->capture_handle, - capture_alsa_device, - SND_PCM_STREAM_CAPTURE, - SND_PCM_NONBLOCK) < 0) { - switch (errno) { - case EBUSY: -#ifdef __ANDROID__ - jack_error ("\n\nATTENTION: The capture (recording) device \"%s\" is " - "already in use", - capture_alsa_device); -#else - current_apps = discover_alsa_using_apps (); - if (current_apps) { - jack_error ("\n\nATTENTION: The capture device \"%s\" is " - "already in use. The following applications " - " are using your soundcard(s) so you should " - " check them and stop them as necessary before " - " trying to start JACK again:\n\n%s", - capture_alsa_device, - current_apps); - free (current_apps); - } else { - jack_error ("\n\nATTENTION: The capture (recording) device \"%s\" is " - "already in use. Please stop the" - " application using it and " - "run JACK again", - capture_alsa_device); - } - alsa_driver_delete (driver); - return NULL; -#endif - break; - - case EPERM: - jack_error ("you do not have permission to open " - "the audio device \"%s\" for capture", - capture_alsa_device); - alsa_driver_delete (driver); - return NULL; - break; - } - - driver->capture_handle = NULL; - } - - if (driver->capture_handle) { - snd_pcm_nonblock (driver->capture_handle, 0); + if(capturing) { + err = alsa_driver_open(driver, SND_PCM_STREAM_CAPTURE); + if(err < 0) { + jack_error ("\n\nATTENTION: Opening of the capture device \"%s\" failed.", + capture_alsa_device); + return NULL; } } @@ -2235,6 +2698,7 @@ alsa_driver_new (char *name, char *playback_alsa_device, } } +#ifndef __QNXNTO__ driver->playback_hw_params = 0; driver->capture_hw_params = 0; driver->playback_sw_params = 0; @@ -2275,7 +2739,7 @@ alsa_driver_new (char *name, char *playback_alsa_device, return NULL; } } - +#endif if (alsa_driver_set_parameters (driver, frames_per_cycle, user_nperiods, rate)) { alsa_driver_delete (driver); @@ -2296,25 +2760,6 @@ alsa_driver_new (char *name, char *playback_alsa_device, return (jack_driver_t *) driver; } -int -alsa_driver_listen_for_clock_sync_status (alsa_driver_t *driver, - ClockSyncListenerFunction func, - void *arg) -{ - ClockSyncListener *csl; - - csl = (ClockSyncListener *) malloc (sizeof (ClockSyncListener)); - csl->function = func; - csl->arg = arg; - csl->id = driver->next_clock_sync_listener_id++; - - pthread_mutex_lock (&driver->clock_sync_lock); - driver->clock_sync_listeners = - jack_slist_prepend (driver->clock_sync_listeners, csl); - pthread_mutex_unlock (&driver->clock_sync_lock); - return csl->id; -} - int alsa_driver_stop_listening_to_clock_sync_status (alsa_driver_t *driver, unsigned int which) @@ -2339,22 +2784,6 @@ alsa_driver_stop_listening_to_clock_sync_status (alsa_driver_t *driver, return ret; } -void -alsa_driver_clock_sync_notify (alsa_driver_t *driver, channel_t chn, - ClockSyncStatus status) -{ - JSList *node; - - pthread_mutex_lock (&driver->clock_sync_lock); - for (node = driver->clock_sync_listeners; node; - node = jack_slist_next (node)) { - ClockSyncListener *csl = (ClockSyncListener *) node->data; - csl->function (chn, status, csl->arg); - } - pthread_mutex_unlock (&driver->clock_sync_lock); - -} - /* DRIVER "PLUGIN" INTERFACE */ const char driver_client_name[] = "alsa_pcm"; diff --git a/linux/alsa/alsa_driver.h b/linux/alsa/alsa_driver.h index f01c08ea..2acc07dc 100644 --- a/linux/alsa/alsa_driver.h +++ b/linux/alsa/alsa_driver.h @@ -21,7 +21,12 @@ #ifndef __jack_alsa_driver_h__ #define __jack_alsa_driver_h__ +#ifdef __QNXNTO__ +#include +#else #include +#endif + #include "bitset.h" #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -41,6 +46,24 @@ #include "memops.h" #include "alsa_midi.h" +#ifdef __QNXNTO__ +#define SND_PCM_FORMAT_S16_LE SND_PCM_SFMT_S16_LE +#define SND_PCM_FORMAT_S16_BE SND_PCM_SFMT_S16_BE +#define SND_PCM_FORMAT_S24_LE SND_PCM_SFMT_S24_LE +#define SND_PCM_FORMAT_S24_BE SND_PCM_SFMT_S24_BE +#define SND_PCM_FORMAT_S32_LE SND_PCM_SFMT_S32_LE +#define SND_PCM_FORMAT_S32_BE SND_PCM_SFMT_S32_BE +#define SND_PCM_FORMAT_FLOAT_LE SND_PCM_SFMT_FLOAT_LE +#define SND_PCM_STATE_SUSPENDED SND_PCM_STATUS_SUSPENDED +#define SND_PCM_STATE_XRUN SND_PCM_STATUS_UNDERRUN +#define SND_PCM_STREAM_PLAYBACK SND_PCM_CHANNEL_PLAYBACK +#define SND_PCM_STREAM_CAPTURE SND_PCM_CHANNEL_CAPTURE + +typedef unsigned long snd_pcm_uframes_t; +typedef signed long snd_pcm_sframes_t; +typedef int32_t alsa_driver_default_format_t; +#endif + #ifdef __cplusplus extern "C" { @@ -58,13 +81,28 @@ typedef struct _alsa_driver { JACK_DRIVER_NT_DECL +#ifdef __QNXNTO__ + unsigned int playback_sample_format; + unsigned int capture_sample_format; + void *capture_areas; + void *playback_areas; + void *capture_areas_ptr; + void *playback_areas_ptr; +#else + snd_pcm_format_t playback_sample_format; + snd_pcm_format_t capture_sample_format; + const snd_pcm_channel_area_t *capture_areas; + const snd_pcm_channel_area_t *playback_areas; + snd_pcm_hw_params_t *playback_hw_params; + snd_pcm_sw_params_t *playback_sw_params; + snd_pcm_hw_params_t *capture_hw_params; + snd_pcm_sw_params_t *capture_sw_params; +#endif int poll_timeout_ms; jack_time_t poll_last; jack_time_t poll_next; char **playback_addr; char **capture_addr; - const snd_pcm_channel_area_t *capture_areas; - const snd_pcm_channel_area_t *playback_areas; struct pollfd *pfd; unsigned int playback_nfds; unsigned int capture_nfds; @@ -89,8 +127,6 @@ typedef struct _alsa_driver { char *alsa_driver; bitset_t channels_not_done; bitset_t channels_done; - snd_pcm_format_t playback_sample_format; - snd_pcm_format_t capture_sample_format; float max_sample_val; unsigned long user_nperiods; unsigned int playback_nperiods; @@ -99,10 +135,6 @@ typedef struct _alsa_driver { snd_ctl_t *ctl_handle; snd_pcm_t *playback_handle; snd_pcm_t *capture_handle; - snd_pcm_hw_params_t *playback_hw_params; - snd_pcm_sw_params_t *playback_sw_params; - snd_pcm_hw_params_t *capture_hw_params; - snd_pcm_sw_params_t *capture_sw_params; jack_hardware_t *hw; ClockSyncStatus *clock_sync_data; jack_client_t *client; @@ -211,18 +243,6 @@ alsa_driver_write_to_channel (alsa_driver_t *driver, alsa_driver_mark_channel_done (driver, channel); } -void alsa_driver_silence_untouched_channels (alsa_driver_t *driver, - jack_nframes_t nframes); -void alsa_driver_set_clock_sync_status (alsa_driver_t *driver, channel_t chn, - ClockSyncStatus status); -int alsa_driver_listen_for_clock_sync_status (alsa_driver_t *, - ClockSyncListenerFunction, - void *arg); -int alsa_driver_stop_listen_for_clock_sync_status (alsa_driver_t *, - unsigned int); -void alsa_driver_clock_sync_notify (alsa_driver_t *, channel_t chn, - ClockSyncStatus); - int alsa_driver_reset_parameters (alsa_driver_t *driver, jack_nframes_t frames_per_cycle, @@ -269,8 +289,6 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes); int alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes); -jack_time_t jack_get_microseconds(void); - // Code implemented in JackAlsaDriver.cpp void ReadInput(jack_nframes_t orig_nframes, snd_pcm_sframes_t contiguous, snd_pcm_sframes_t nread); diff --git a/linux/alsa/usx2y.h b/linux/alsa/usx2y.h index 522d19a8..51a82a0f 100644 --- a/linux/alsa/usx2y.h +++ b/linux/alsa/usx2y.h @@ -22,6 +22,8 @@ #ifndef __jack_usx2y_h__ #define __jack_usx2y_h__ + #include + #define USX2Y_MAXPACK 50 #define USX2Y_MAXBUFFERMS 100 #define USX2Y_MAXSTRIDE 3 @@ -51,7 +53,9 @@ typedef struct snd_usX2Y_hwdep_pcm_shm snd_usX2Y_hwdep_pcm_shm_t; typedef struct { alsa_driver_t *driver; +#ifndef __QNXNTO__ snd_hwdep_t *hwdep_handle; +#endif struct pollfd pfds; struct snd_usX2Y_hwdep_pcm_shm *hwdep_pcm_shm; int playback_iso_start; diff --git a/posix/JackFifo.h b/posix/JackFifo.h index 8ac72591..8b0a8d78 100644 --- a/posix/JackFifo.h +++ b/posix/JackFifo.h @@ -44,7 +44,7 @@ class SERVER_EXPORT JackFifo : public detail::JackSynchro protected: - void BuildName(const char* name, const char* server_name, char* res); + void BuildName(const char* name, const char* server_name, char* res, int size); public: diff --git a/qnx/JackAtomic_os.h b/qnx/JackAtomic_os.h new file mode 100755 index 00000000..e201d0d6 --- /dev/null +++ b/qnx/JackAtomic_os.h @@ -0,0 +1,86 @@ +/* +Copyright (C) 2004-2008 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackAtomic_linux__ +#define __JackAtomic_linux__ + +#include "JackTypes.h" + +#ifdef __PPC__ + +static inline int CAS(register UInt32 value, register UInt32 newvalue, register volatile void* addr) +{ + register int result; + register UInt32 tmp; + asm volatile ( + "# CAS \n" + " lwarx %4, 0, %1 \n" // creates a reservation on addr + " cmpw %4, %2 \n" // test value at addr + " bne- 1f \n" + " sync \n" // synchronize instructions + " stwcx. %3, 0, %1 \n" // if the reservation is not altered + // stores the new value at addr + " bne- 1f \n" + " li %0, 1 \n" + " b 2f \n" + "1: \n" + " li %0, 0 \n" + "2: \n" + : "=r" (result) + : "r" (addr), "r" (value), "r" (newvalue), "r" (tmp) + ); + return result; +} + +#endif + +#if defined(__i386__) || defined(__x86_64__) + +#define LOCK "lock ; " + +static inline char CAS(volatile UInt32 value, UInt32 newvalue, volatile void* addr) +{ + register char ret; + __asm__ __volatile__ ( + "# CAS \n\t" + LOCK "cmpxchg %2, (%1) \n\t" + "sete %0 \n\t" + : "=a" (ret) + : "c" (addr), "d" (newvalue), "a" (value) + ); + return ret; +} + +#endif + + + + +#if !defined(__i386__) && !defined(__x86_64__) && !defined(__PPC__) + + +static inline char CAS(volatile UInt32 value, UInt32 newvalue, volatile void* addr) +{ + return __sync_bool_compare_and_swap ((UInt32*)addr, value, newvalue); +} +#endif + + +#endif + diff --git a/qnx/JackLinuxTime.c b/qnx/JackLinuxTime.c new file mode 100755 index 00000000..a5ece765 --- /dev/null +++ b/qnx/JackLinuxTime.c @@ -0,0 +1,215 @@ +/* +Copyright (C) 2001-2003 Paul Davis +Copyright (C) 2005 Jussi Laako +Copyright (C) 2004-2008 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "JackConstants.h" +#include "JackTime.h" +#include "JackTypes.h" +#include "JackError.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +jack_time_t (*_jack_get_microseconds)(void) = 0; + +#if defined(__gnu_linux__) && (defined(__i386__) || defined(__x86_64__)) +#define HPET_SUPPORT +#define HPET_MMAP_SIZE 1024 +#define HPET_CAPS 0x000 +#define HPET_PERIOD 0x004 +#define HPET_COUNTER 0x0f0 +#define HPET_CAPS_COUNTER_64BIT (1 << 13) +#if defined(__x86_64__) +typedef uint64_t hpet_counter_t; +#else +typedef uint32_t hpet_counter_t; +#endif +static int hpet_fd; +static unsigned char *hpet_ptr; +static uint32_t hpet_period; /* period length in femto secs */ +static uint64_t hpet_offset = 0; +static uint64_t hpet_wrap; +static hpet_counter_t hpet_previous = 0; +#endif /* defined(__gnu_linux__) && (__i386__ || __x86_64__) */ + +#ifdef HPET_SUPPORT + +static int jack_hpet_init () +{ + uint32_t hpet_caps; + + hpet_fd = open("/dev/hpet", O_RDONLY); + if (hpet_fd < 0) { + jack_error ("This system has no accessible HPET device (%s)", strerror (errno)); + return -1; + } + + hpet_ptr = (unsigned char *) mmap(NULL, HPET_MMAP_SIZE, + PROT_READ, MAP_SHARED, hpet_fd, 0); + if (hpet_ptr == MAP_FAILED) { + jack_error ("This system has no mappable HPET device (%s)", strerror (errno)); + close (hpet_fd); + return -1; + } + + /* this assumes period to be constant. if needed, + it can be moved to the clock access function + */ + hpet_period = *((uint32_t *) (hpet_ptr + HPET_PERIOD)); + hpet_caps = *((uint32_t *) (hpet_ptr + HPET_CAPS)); + hpet_wrap = ((hpet_caps & HPET_CAPS_COUNTER_64BIT) && + (sizeof(hpet_counter_t) == sizeof(uint64_t))) ? + 0 : ((uint64_t) 1 << 32); + + return 0; +} + +static jack_time_t jack_get_microseconds_from_hpet (void) +{ + hpet_counter_t hpet_counter; + long double hpet_time; + + hpet_counter = *((hpet_counter_t *) (hpet_ptr + HPET_COUNTER)); + if (hpet_counter < hpet_previous) + hpet_offset += hpet_wrap; + hpet_previous = hpet_counter; + hpet_time = (long double) (hpet_offset + hpet_counter) * + (long double) hpet_period * (long double) 1e-9; + return ((jack_time_t) (hpet_time + 0.5)); +} + +#else + +static int jack_hpet_init () +{ + jack_error ("This version of JACK or this computer does not have HPET support.\n" + "Please choose a different clock source."); + return -1; +} + +static jack_time_t jack_get_microseconds_from_hpet (void) +{ + /* never called */ + return 0; +} + +#endif /* HPET_SUPPORT */ + +#define HAVE_CLOCK_GETTIME 1 + +#ifndef HAVE_CLOCK_GETTIME + +static jack_time_t jack_get_microseconds_from_system (void) +{ + jack_time_t jackTime; + struct timeval tv; + + gettimeofday (&tv, NULL); + jackTime = (jack_time_t) tv.tv_sec * 1000000 + (jack_time_t) tv.tv_usec; + return jackTime; +} + +#else + +static jack_time_t jack_get_microseconds_from_system (void) +{ + jack_time_t jackTime; + struct timespec time; + + clock_gettime(CLOCK_MONOTONIC, &time); + jackTime = (jack_time_t) time.tv_sec * 1e6 + + (jack_time_t) time.tv_nsec / 1e3; + return jackTime; +} + +#endif /* HAVE_CLOCK_GETTIME */ + + +SERVER_EXPORT void JackSleep(long usec) +{ + usleep(usec); +} + +SERVER_EXPORT void InitTime() +{ + /* nothing to do on a generic system - we use the system clock */ +} + +SERVER_EXPORT void EndTime() +{} + +void SetClockSource(jack_timer_type_t source) +{ + jack_log("Clock source : %s", ClockSourceName(source)); + + switch (source) + { + case JACK_TIMER_HPET: + if (jack_hpet_init () == 0) { + _jack_get_microseconds = jack_get_microseconds_from_hpet; + } else { + _jack_get_microseconds = jack_get_microseconds_from_system; + } + break; + + case JACK_TIMER_SYSTEM_CLOCK: + default: + _jack_get_microseconds = jack_get_microseconds_from_system; + break; + } +} + +const char* ClockSourceName(jack_timer_type_t source) +{ + switch (source) { + case JACK_TIMER_HPET: + return "hpet"; + case JACK_TIMER_SYSTEM_CLOCK: + #ifdef HAVE_CLOCK_GETTIME + return "system clock via clock_gettime"; + #else + return "system clock via gettimeofday"; + #endif + } + + /* what is wrong with gcc ? */ + return "unknown"; +} + +SERVER_EXPORT jack_time_t GetMicroSeconds() +{ + return _jack_get_microseconds(); +} + +SERVER_EXPORT jack_time_t jack_get_microseconds() +{ + return _jack_get_microseconds(); +} + diff --git a/qnx/JackPlatformPlug_os.h b/qnx/JackPlatformPlug_os.h new file mode 100755 index 00000000..c845dca8 --- /dev/null +++ b/qnx/JackPlatformPlug_os.h @@ -0,0 +1,85 @@ +/* +Copyright (C) 2004-2008 Grame + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#ifndef __JackPlatformPlug_linux__ +#define __JackPlatformPlug_linux__ + +#define jack_server_dir "/dev/shm" +#define jack_client_dir "/dev/shm" +#define JACK_DEFAULT_DRIVER "alsa" + +namespace Jack +{ + struct JackRequest; + struct JackResult; + + class JackPosixMutex; + class JackPosixThread; + class JackFifo; + class JackSocketServerChannel; + class JackSocketClientChannel; + class JackSocketServerNotifyChannel; + class JackSocketNotifyChannel; + class JackClientSocket; + class JackNetUnixSocket; +} + +/* __JackPlatformMutex__ */ +#include "JackPosixMutex.h" +namespace Jack {typedef JackPosixMutex JackMutex; } + +/* __JackPlatformThread__ */ +#include "JackPosixThread.h" +namespace Jack { typedef JackPosixThread JackThread; } + +#include "JackFifo.h" +namespace Jack { typedef JackFifo JackSynchro; } + + +/* __JackPlatformChannelTransaction__ */ +/* +#include "JackSocket.h" +namespace Jack { typedef JackClientSocket JackChannelTransaction; } +*/ + +/* __JackPlatformProcessSync__ */ +#include "JackPosixProcessSync.h" +namespace Jack { typedef JackPosixProcessSync JackProcessSync; } + +/* __JackPlatformServerChannel__ */ +#include "JackSocketServerChannel.h" +namespace Jack { typedef JackSocketServerChannel JackServerChannel; } + +/* __JackPlatformClientChannel__ */ +#include "JackSocketClientChannel.h" +namespace Jack { typedef JackSocketClientChannel JackClientChannel; } + +/* __JackPlatformServerNotifyChannel__ */ +#include "JackSocketServerNotifyChannel.h" +namespace Jack { typedef JackSocketServerNotifyChannel JackServerNotifyChannel; } + +/* __JackPlatformNotifyChannel__ */ +#include "JackSocketNotifyChannel.h" +namespace Jack { typedef JackSocketNotifyChannel JackNotifyChannel; } + +/* __JackPlatformNetSocket__ */ +#include "JackNetUnixSocket.h" +namespace Jack { typedef JackNetUnixSocket JackNetSocket; } + +#endif diff --git a/qnx/driver.h b/qnx/driver.h new file mode 100755 index 00000000..6e1466f4 --- /dev/null +++ b/qnx/driver.h @@ -0,0 +1,300 @@ +/* + Copyright (C) 2001 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: driver.h,v 1.2 2005/11/23 11:24:29 letz Exp $ +*/ + +#ifndef __jack_driver_h__ +#define __jack_driver_h__ + +#include +#include "types.h" +#include "jslist.h" +#include "driver_interface.h" + +typedef float gain_t; +typedef long channel_t; + +typedef enum { + Lock = 0x1, + NoLock = 0x2, + Sync = 0x4, + NoSync = 0x8 +} ClockSyncStatus; + +typedef void (*ClockSyncListenerFunction)(channel_t, ClockSyncStatus, void*); + +typedef struct +{ + unsigned long id; + ClockSyncListenerFunction function; + void *arg; +} +ClockSyncListener; + +struct _jack_engine; +struct _jack_driver; + +typedef int (*JackDriverAttachFunction)(struct _jack_driver *, + struct _jack_engine *); +typedef int (*JackDriverDetachFunction)(struct _jack_driver *, + struct _jack_engine *); +typedef int (*JackDriverReadFunction)(struct _jack_driver *, + jack_nframes_t nframes); +typedef int (*JackDriverWriteFunction)(struct _jack_driver *, + jack_nframes_t nframes); +typedef int (*JackDriverNullCycleFunction)(struct _jack_driver *, + jack_nframes_t nframes); +typedef int (*JackDriverStopFunction)(struct _jack_driver *); +typedef int (*JackDriverStartFunction)(struct _jack_driver *); +typedef int (*JackDriverBufSizeFunction)(struct _jack_driver *, + jack_nframes_t nframes); +/* + Call sequence summary: + + 1) engine loads driver via runtime dynamic linking + - calls jack_driver_load + - we call dlsym for "driver_initialize" and execute it + 2) engine attaches to driver + 3) engine starts driver + 4) driver runs its own thread, calling + while () { + driver->wait (); + driver->engine->run_cycle () + } + 5) engine stops driver + 6) engine detaches from driver + 7) engine calls driver `finish' routine + + Note that stop/start may be called multiple times in the event of an + error return from the `wait' function. +*/ + +typedef struct _jack_driver +{ + + /* The _jack_driver structure fields are included at the beginning of + each driver-specific structure using the JACK_DRIVER_DECL macro, + which is defined below. The comments that follow describe each + common field. + + The driver should set this to be the interval it expects to elapse + between returning from the `wait' function. if set to zero, it + implies that the driver does not expect regular periodic wakeups. + + jack_time_t period_usecs; + + + The driver should set this within its "wait" function to indicate + the UST of the most recent determination that the engine cycle + should run. it should not be set if the "extra_fd" argument of + the wait function is set to a non-zero value. + + jack_time_t last_wait_ust; + + + These are not used by the driver. They should not be written to or + modified in any way + + void *handle; + struct _jack_internal_client *internal_client; + + This should perform any cleanup associated with the driver. it will + be called when jack server process decides to get rid of the + driver. in some systems, it may not be called at all, so the driver + should never rely on a call to this. it can set it to NULL if + it has nothing do do. + + void (*finish)(struct _jack_driver *); + + + The JACK engine will call this when it wishes to attach itself to + the driver. the engine will pass a pointer to itself, which the driver + may use in anyway it wishes to. the driver may assume that this + is the same engine object that will make `wait' calls until a + `detach' call is made. + + JackDriverAttachFunction attach; + + + The JACK engine will call this when it is finished using a driver. + + JackDriverDetachFunction detach; + + + The JACK engine will call this when it wants to wait until the + driver decides that its time to process some data. the driver returns + a count of the number of audioframes that can be processed. + + it should set the variable pointed to by `status' as follows: + + zero: the wait completed normally, processing may begin + negative: the wait failed, and recovery is not possible + positive: the wait failed, and the driver stopped itself. + a call to `start' will return the driver to + a correct and known state. + + the driver should also fill out the `delayed_usecs' variable to + indicate any delay in its expected periodic execution. for example, + if it discovers that its return from poll(2) is later than it + expects it to be, it would place an estimate of the delay + in this variable. the engine will use this to decide if it + plans to continue execution. + + JackDriverWaitFunction wait; + + + The JACK engine will call this to ask the driver to move + data from its inputs to its output port buffers. it should + return 0 to indicate successful completion, negative otherwise. + + This function will always be called after the wait function (above). + + JackDriverReadFunction read; + + + The JACK engine will call this to ask the driver to move + data from its input port buffers to its outputs. it should + return 0 to indicate successful completion, negative otherwise. + + this function will always be called after the read function (above). + + JackDriverWriteFunction write; + + + The JACK engine will call this after the wait function (above) has + been called, but for some reason the engine is unable to execute + a full "cycle". the driver should do whatever is necessary to + keep itself running correctly, but cannot reference ports + or other JACK data structures in any way. + + JackDriverNullCycleFunction null_cycle; + + + The engine will call this when it plans to stop calling the `wait' + function for some period of time. the driver should take + appropriate steps to handle this (possibly no steps at all). + NOTE: the driver must silence its capture buffers (if any) + from within this function or the function that actually + implements the change in state. + + JackDriverStopFunction stop; + + + The engine will call this to let the driver know that it plans + to start calling the `wait' function on a regular basis. the driver + should take any appropriate steps to handle this (possibly no steps + at all). NOTE: The driver may wish to silence its playback buffers + (if any) from within this function or the function that actually + implements the change in state. + + JackDriverStartFunction start; + + The engine will call this to let the driver know that some client + has requested a new buffer size. The stop function will be called + prior to this, and the start function after this one has returned. + + JackDriverBufSizeFunction bufsize; + */ + + /* define the fields here... */ +#define JACK_DRIVER_DECL \ + jack_time_t period_usecs; \ + jack_time_t last_wait_ust; \ + void *handle; \ + struct _jack_client_internal * internal_client; \ + void (*finish)(struct _jack_driver *);\ + JackDriverAttachFunction attach; \ + JackDriverDetachFunction detach; \ + JackDriverReadFunction read; \ + JackDriverWriteFunction write; \ + JackDriverNullCycleFunction null_cycle; \ + JackDriverStopFunction stop; \ + JackDriverStartFunction start; \ + JackDriverBufSizeFunction bufsize; + + JACK_DRIVER_DECL /* expand the macro */ +} +jack_driver_t; + +void jack_driver_init (jack_driver_t *); +void jack_driver_release (jack_driver_t *); + +jack_driver_t *jack_driver_load (int argc, char **argv); +void jack_driver_unload (jack_driver_t *); + +/**************************** + *** Non-Threaded Drivers *** + ****************************/ + +/* + Call sequence summary: + + 1) engine loads driver via runtime dynamic linking + - calls jack_driver_load + - we call dlsym for "driver_initialize" and execute it + - driver_initialize calls jack_driver_nt_init + 2) nt layer attaches to driver + 3) nt layer starts driver + 4) nt layer runs a thread, calling + while () { + driver->nt_run_ctcle(); + } + 5) nt layer stops driver + 6) nt layer detaches driver + 7) engine calls driver `finish' routine which calls jack_driver_nt_finish + + Note that stop/start may be called multiple times in the event of an + error return from the `wait' function. +*/ + +struct _jack_driver_nt; + +typedef int (*JackDriverNTAttachFunction)(struct _jack_driver_nt *); +typedef int (*JackDriverNTDetachFunction)(struct _jack_driver_nt *); +typedef int (*JackDriverNTStopFunction)(struct _jack_driver_nt *); +typedef int (*JackDriverNTStartFunction)(struct _jack_driver_nt *); +typedef int (*JackDriverNTBufSizeFunction)(struct _jack_driver_nt *, + jack_nframes_t nframes); +typedef int (*JackDriverNTRunCycleFunction)(struct _jack_driver_nt *); + +typedef struct _jack_driver_nt +{ +#define JACK_DRIVER_NT_DECL \ + JACK_DRIVER_DECL \ + struct _jack_engine * engine; \ + volatile int nt_run; \ + pthread_t nt_thread; \ + pthread_mutex_t nt_run_lock; \ + JackDriverNTAttachFunction nt_attach; \ + JackDriverNTDetachFunction nt_detach; \ + JackDriverNTStopFunction nt_stop; \ + JackDriverNTStartFunction nt_start; \ + JackDriverNTBufSizeFunction nt_bufsize; \ + JackDriverNTRunCycleFunction nt_run_cycle; +#define nt_read read +#define nt_write write +#define nt_null_cycle null_cycle + + JACK_DRIVER_NT_DECL +} +jack_driver_nt_t; + +void jack_driver_nt_init (jack_driver_nt_t * driver); +void jack_driver_nt_finish (jack_driver_nt_t * driver); + +#endif /* __jack_driver_h__ */ diff --git a/tests/wscript b/tests/wscript index 96a63bc9..0dcae8c3 100644 --- a/tests/wscript +++ b/tests/wscript @@ -20,6 +20,8 @@ def build(bld): prog.includes = ['..','../macosx', '../posix', '../common/jack', '../common'] if bld.env['IS_LINUX']: prog.includes = ['..','../linux', '../posix', '../common/jack', '../common'] + if bld.env['IS_QNX']: + prog.includes = ['..','../qnx', '../posix', '../common/jack', '../common'] if bld.env['IS_SUN']: prog.includes = ['..','../solaris', '../posix', '../common/jack', '../common'] prog.source = test_program_sources diff --git a/wscript b/wscript old mode 100644 new mode 100755 index 14e561a3..7d46528f --- a/wscript +++ b/wscript @@ -95,6 +95,15 @@ def options(opt): alsa.check_cfg( package='alsa >= 1.0.18', args='--cflags --libs') + # Check for QNX sound headers + qsa = opt.add_auto_option( + 'qsa', + help='Enable QNX sound driver', + conf_dest='BUILD_DRIVER_ALSA') + qsa.check(lib='asound', uselib_store='ALSA') + qsa.check( + header_name=['sys/asoundlib.h'], + msg='Checking for header sys/asoundlib.h') firewire = opt.add_auto_option( 'firewire', help='Enable FireWire driver (FFADO)', @@ -185,6 +194,7 @@ def detect_platform(conf): platforms = [ # ('KEY, 'Human readable name', ['strings', 'to', 'check', 'for']) ('IS_LINUX', 'Linux', ['gnu0', 'gnukfreebsd', 'linux', 'posix']), + ('IS_QNX', 'QNX', ['qnx']), ('IS_MACOSX', 'MacOS X', ['darwin']), ('IS_SUN', 'SunOS', ['sunos']), ('IS_WINDOWS', 'Windows', ['cygwin', 'msys', 'win32']) @@ -304,7 +314,11 @@ def configure(conf): conf.check_cc(fragment=fragment, define_name='HAVE_NGREG', mandatory=False, msg='Checking for NGREG') - conf.env['LIB_PTHREAD'] = ['pthread'] + if conf.env['IS_QNX']: + conf.env['LIB_PTHREAD'] = ['c'] + conf.env['LIB_SOCKET'] = ['socket'] + else: + conf.env['LIB_PTHREAD'] = ['pthread'] conf.env['LIB_DL'] = ['dl'] conf.env['LIB_RT'] = ['rt'] conf.env['LIB_M'] = ['m'] @@ -512,6 +526,9 @@ def obj_add_includes(bld, obj): if bld.env['IS_MACOSX']: obj.includes += ['macosx', 'posix'] + if bld.env['IS_QNX']: + obj.includes += ['qnx', 'posix'] + if bld.env['IS_SUN']: obj.includes += ['posix', 'solaris'] @@ -540,6 +557,10 @@ def build_jackd(bld): jackd.use += ['DL', 'PTHREAD'] jackd.framework = ['CoreFoundation'] + if bld.env['IS_QNX']: + jackd.use += ['M', 'PTHREAD'] + + if bld.env['IS_SUN']: jackd.use += ['DL', 'PTHREAD'] @@ -598,16 +619,20 @@ def build_drivers(bld): alsa_src = [ 'common/memops.c', 'linux/alsa/JackAlsaDriver.cpp', - 'linux/alsa/alsa_rawmidi.c', - 'linux/alsa/alsa_seqmidi.c', - 'linux/alsa/alsa_midi_jackmp.cpp', - 'linux/alsa/generic_hw.c', - 'linux/alsa/hdsp.c', - 'linux/alsa/alsa_driver.c', - 'linux/alsa/hammerfall.c', - 'linux/alsa/ice1712.c' + 'linux/alsa/alsa_driver.c' ] + if not bld.env['IS_QNX']: + alsa_src += [ + 'linux/alsa/alsa_rawmidi.c', + 'linux/alsa/alsa_seqmidi.c', + 'linux/alsa/alsa_midi_jackmp.cpp', + 'linux/alsa/generic_hw.c', + 'linux/alsa/hdsp.c', + 'linux/alsa/hammerfall.c', + 'linux/alsa/ice1712.c' + ] + alsarawmidi_src = [ 'linux/alsarawmidi/JackALSARawMidiDriver.cpp', 'linux/alsarawmidi/JackALSARawMidiInputPort.cpp', @@ -704,11 +729,12 @@ def build_drivers(bld): target = 'alsa', source = alsa_src, use = ['ALSA']) - create_driver_obj( - bld, - target = 'alsarawmidi', - source = alsarawmidi_src, - use = ['ALSA']) + if not bld.env['IS_QNX']: + create_driver_obj( + bld, + target = 'alsarawmidi', + source = alsarawmidi_src, + use = ['ALSA']) if bld.env['BUILD_DRIVER_FFADO']: create_driver_obj(