diff --git a/.cvsignore b/.cvsignore index e5103c9..4a87f23 100644 --- a/.cvsignore +++ b/.cvsignore @@ -17,3 +17,8 @@ configure *.desc *diff *diffs +compile +depcomp +install-sh +missing +mkinstalldirs diff --git a/AUTHORS b/AUTHORS index 1d83338..e16f279 100644 --- a/AUTHORS +++ b/AUTHORS @@ -10,12 +10,15 @@ contributions to those discussions came from (in alphabetical order): Many other members of LAD contributed ideas to JACK, particularly Richard Guenther. -Paul Davis was the principal author of the JACK API and of the sample -implementation contained here. Andy Wingo and Kai Vehmanen provided -many small patches and documentation. Fernando Pablo Lopez-Lezcano -contributed the capabilities-based code. Jeremy Hall, Steve Harris, -and Martin Boer contributed sample clients and utilities. -Jack O'Quin contributed new transport interfaces and documentation. -Taybin Rutkin helps with patch management and releases. +Paul Davis was the principal author of the JACK API and of the +implementation contained here. Andy Wingo and Kai Vehmanen provided +many small patches and documentation. Fernando Pablo Lopez-Lezcano +contributed the capabilities-based code. Jeremy Hall, Steve Harris, +and Martin Boer contributed sample clients and utilities. Jack O'Quin +contributed new transport interfaces, implemented the buffer size +callback, and added much documentation. Taybin Rutkin helps with +patch management and releases. Melanie Thielker contributed +significantly to JACK's interaction with aspects of both POSIX and +System V APIs. Many others have contributed patches and/or test results. diff --git a/TODO b/TODO index a2487c5..1fb72d8 100644 --- a/TODO +++ b/TODO @@ -15,21 +15,15 @@ TO BE DECIDED (owner) - support for on-the-fly sampling rate change - whether to default to triangular dithering for 16bit output. (swh) -TODO for 1.0 (owner) - -- API to change buffer size (joq) - TODO post-1.0 - jack session handling (taybin) -- get portaudio driver working under Linux (joq) - handle mixed-mode 64bit and 32bit clients (joq) - TBD TODO general (owner) - better scheme for handling machine and system dependencies (joq) -- don't build static libraries of drivers and ip-clients (kaiv) - add explanation of protocol versioning to README.developers (kaiv) - proper handling of client return values in libjack (kaiv) - pool based malloc for rt client-local mem allocation (paul, moved back here) @@ -40,11 +34,12 @@ TO THINK ABOUT - whether we want to support varispeed (resampling and/or changing the actual rate) - per-block timestamping against system clock (UST stamps at driver level) -- multiple port buffer shm segments (i.e. dynamically - increase the total number of ports in the system) +- dynamically increase the total number of ports in the system CLOSED (date,who,comment) +- don't build static libraries of drivers and ip-clients (2003/10/07,paul) +- API to change buffer size (joq) (2003/10/07) - added code to enforce the 'bufsize==2^x' rule (taybin) (2003/8/28) - make sure that process callbacks always have nframes equal to buffersize (2003/08/27,done,kaiv) - add Paul's graph/subgraph explanation to the design doc (2003/08/27,done,kaiv) @@ -66,5 +61,6 @@ CLOSED (date,who,comment) - whether to hide the transport.h structs from clients (2003/08/04, joq) - define transport info struct contents (2003/08/13, joq) - resolve helper thread design question? (2003/08/31, joq) +- get portaudio driver working under Linux (2003/10/29, joq) ----------------------------------------------------------------------- diff --git a/autogen.sh b/autogen.sh index 7a0ba52..1afe37e 100755 --- a/autogen.sh +++ b/autogen.sh @@ -25,7 +25,7 @@ autoconf || { exit 1 } -echo "Running ./configure --enable-maintainer-mode $@..." - -./configure --enable-maintainer-mode $@ - +if test x$1 != x--no-conf; then + echo "Running ./configure --enable-maintainer-mode $@..." + ./configure --enable-maintainer-mode $@ +fi diff --git a/configure.in b/configure.in index 817e61b..68f609e 100644 --- a/configure.in +++ b/configure.in @@ -2,6 +2,7 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(jackd/jackd.c) AC_CONFIG_AUX_DIR(.) +AC_CANONICAL_TARGET dnl --- dnl HOWTO: updating the JACK version number @@ -13,8 +14,8 @@ dnl micro version = incremented when implementation-only dnl changes are made dnl --- JACK_MAJOR_VERSION=0 -JACK_MINOR_VERSION=83 -JACK_MICRO_VERSION=5 +JACK_MINOR_VERSION=89 +JACK_MICRO_VERSION=0 dnl --- dnl HOWTO: updating the jack protocal version @@ -24,7 +25,7 @@ dnl made to the way libjack communicates with jackd dnl that would break applications linked with an older dnl version of libjack. dnl --- -JACK_PROTOCOL_VERSION=10 +JACK_PROTOCOL_VERSION=11 dnl --- dnl HOWTO: updating the libjack interface version @@ -41,7 +42,7 @@ dnl slacker than this, and closer to those for the JACK version dnl number. dnl --- JACK_API_CURRENT=0 -JACK_API_REVISION=22 +JACK_API_REVISION=23 JACK_API_AGE=0 AC_SUBST(JACK_MAJOR_VERSION) @@ -68,6 +69,9 @@ AM_MAINTAINER_MODE AM_CONFIG_HEADER(config.h) +AC_ENABLE_STATIC(no) +AC_ENABLE_SHARED(yes) + AC_PROG_CC AC_PROG_CXX AC_PROG_LD @@ -88,7 +92,7 @@ dnl XXX this could probably be improved dnl AC_ARG_ENABLE(posix-shm, [ --enable-posix-shm use POSIX shm API ], - TRY_POSIX_SHM=yes , TRY_POSIX_SHM=no ) + TRY_POSIX_SHM=$enableval , TRY_POSIX_SHM=no ) if test "x$TRY_POSIX_SHM" = "xyes" @@ -113,7 +117,7 @@ fi JACK_CORE_CFLAGS="-I\$(top_srcdir) -D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS -Wall" JACK_CFLAGS="$JACK_CORE_CFLAGS -g" -JACK_OPT_CFLAGS="$JACK_CORE_CFLAGS -O3 -fomit-frame-pointer -ffast-math -fstrength-reduce -funroll-loops -fmove-all-movables" +JACK_OPT_CFLAGS="$JACK_CORE_CFLAGS -O3 -march=$target_cpu -fomit-frame-pointer -ffast-math -fstrength-reduce -funroll-loops -fmove-all-movables" AC_ARG_ENABLE(optimize, [ --enable-optimize ask the compiler for its best optimizations], @@ -241,8 +245,8 @@ AC_CHECK_LIB(asound,snd_pcm_drop, ) AC_SUBST(ALSA_LIBS) -AC_ARG_ENABLE(portaudio, [ --enable-portaudio Include PortAudio driver ], - TRY_PORTAUDIO=yes , TRY_PORTAUDIO=no ) +AC_ARG_ENABLE(portaudio, [ --disable-portaudio Ignore PortAudio driver ], + TRY_PORTAUDIO=$enableval , TRY_PORTAUDIO=yes ) HAVE_PA="false" diff --git a/doc/Makefile.am b/doc/Makefile.am index 45ad123..39a80c9 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -16,7 +16,8 @@ DOC_DIR=$(HTML_DIR) all-local: doxygen-build.stamp doxygen-build.stamp: $(DOX) mainpage.dox transport.dox fsm.png \ -../jack/jack.h ../jack/types.h ../jack/transport.h +../jack/jack.h ../jack/types.h ../jack/transport.h ../jack/ringbuffer.h ../jack/port.h \ +../example-clients/simple_client.c @echo '*** Running doxygen ***' doxygen $(DOX) touch doxygen-build.stamp diff --git a/doc/mainpage.dox b/doc/mainpage.dox index da6953b..41eb3dd 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -8,20 +8,75 @@ @section intro Introduction -JACK is a low-latency audio server, written primarily for the -GNU/Linux operating system. It can connect several client -applications to an audio device, and allow them to share audio with -each other. Clients can run as separate processes like normal -applications, or within the JACK server as "plugins". +JACK is a low-latency audio server, written for POSIX conformant +operating systems such as GNU/Linux and Apple's OS X. It can connect +several client applications to an audio device, and allow them to +share audio with each other. Clients can run as separate processes +like normal applications, or within the JACK server as "plugins". -JACK differs from other audio servers in being designed from the -ground up for professional audio work, focusing on two key areas: -synchronous execution of all clients, and low latency operation. +JACK was designed from the ground up for professional audio work, and +its design focuses on two key areas: synchronous execution of all +clients, and low latency operation. @see -@section contents Contents +@section jack_overview JACK Overview + +Traditionally it has been hard if not impossible to write audio +applications that can share data with each other. In addition, +configuring and managing audio interface hardware has often been one +of the most complex aspect of writing audio software. + +JACK changes all this by providing an API that does several things: + + 1. provides a high level abstraction for programmers that + removes the audio interface hardware from the picture and + allows them to concentrate on the core functionality of + their software. + + 2. allows applications to send and receive audio data to/from + each other as well as the audio interface. There is + difference in how an application sends or receives + data regardless of whether it comes from another application + or an audio interface. + +For programmers with experience of several other audio APIs such as +PortAudio, Apple's CoreAudio, Steinberg's VST and ASIO as well as many +others, JACK presents a familiar model: your program provides a +"callback" function that will be executed at the right time. Your +callback can send and receive data as well as do other signal +processing tasks. You are not responsible for managing audio +interfaces or threading, and there is no "format negotiation": all +audio data within JACK is represented as 32 bit floating point values. + +For those with experiences rooted in the Unix world, JACK presents a +somewhat unfamiliar API. Most Unix APIs are based on the read/write +model spawned by the "everything is a file" abstraction that Unix is +rightly famous for. The problem with this design is that it fails to +take the realtime nature of audio interfaces into account, or more +precisely, it fails to force application developers to pay sufficient +attention to this aspect of their task. In addition, it becomes rather +difficult to facilitate inter-application audio routing when different +programs are not all running synchronously. + +Using JACK within your program is very simple, and typically consists +of just: + + - calling @ref jack_client_new to connect to the JACK server. + - registering "ports" to enable data to be moved to and from + your application. + - registering a "process callback" which will be called at the + right time by the JACK server. + - telling JACK that your application is ready to start processing + data. + +There is a lot more that you can do with JACK's interfaces, but for +many applications, this is all that is needed. This +demonstrates a complete (simple!) JACK application that just copies +the signal arriving at its input port to its output port. + +@section reference Reference The JACK programming interfaces are described in several header files: @@ -30,7 +85,23 @@ The JACK programming interfaces are described in several header files: for starting, stopping and repositioning clients. This is described in the @ref transport-design document. - defines most of the data types for JACK. + - defines a simple API for using lock-free + ringbuffers, a very valuable and common data structure in real + time streaming media software. It is critical for use in + applications that do disk I/O such as audio file players and + recording software. + +In addition, the example_clients directory provides numerous examples +of simple JACK clients that nevertheless use the API to do something +useful. It includes + - a metronome. + - a recording client that can capture any number of channels + from any JACK sources and store them as an audio file. + - command line clients to control the transport mechanism, + change the buffer size and more. + - simple examples of wrapping a GUI around a JACK application. + - tools for checking the status of a running JACK system. @section license License @@ -38,10 +109,10 @@ Copyright (C) 2001-2003 by Paul Davis and others. JACK is free software; you can redistribute it and/or modify it under the terms of the GNU GPL and LGPL licenses as published by the Free -Software Foundation, . Most of JACK uses the GPL, -as noted in the source file headers. But, the library interfaces are -licensed under the LGPL, allowing proprietary programs to link with -JACK and call its services. You should have received a copy of these +Software Foundation, . The JACK server uses the +GPL, as noted in the source file headers. However, the JACK library +is licensed under the LGPL, allowing proprietary programs to link with +it and use JACK services. You should have received a copy of these Licenses along with the program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. diff --git a/doc/reference.doxygen.in b/doc/reference.doxygen.in index 2b60b9f..59be832 100644 --- a/doc/reference.doxygen.in +++ b/doc/reference.doxygen.in @@ -305,7 +305,9 @@ INPUT = @top_srcdir@/doc/mainpage.dox \ @top_srcdir@/doc/transport.dox \ @top_srcdir@/jack/jack.h \ @top_srcdir@/jack/types.h \ - @top_srcdir@/jack/transport.h + @top_srcdir@/jack/transport.h \ + @top_srcdir@/jack/ringbuffer.h \ + @top_srcdir@/example-clients/simple_client.c # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp diff --git a/drivers/alsa/alsa_driver.c b/drivers/alsa/alsa_driver.c index a60c80a..58bff35 100644 --- a/drivers/alsa/alsa_driver.c +++ b/drivers/alsa/alsa_driver.c @@ -26,7 +26,10 @@ #include #include #include - +#include +#include +#include +#include #include "alsa_driver.h" #include #include @@ -47,9 +50,9 @@ extern void show_work_times (); /* Delay (in process calls) before jackd will report an xrun */ #define XRUN_REPORT_DELAY 64 + static void alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) - { if (driver->playback_addr) { free (driver->playback_addr); @@ -74,40 +77,60 @@ alsa_driver_release_channel_dependent_memory (alsa_driver_t *driver) static int alsa_driver_check_capabilities (alsa_driver_t *driver) - { return 0; } static int alsa_driver_check_card_type (alsa_driver_t *driver) - { int err; snd_ctl_card_info_t *card_info; + char * ctl_name; + regex_t expression; snd_ctl_card_info_alloca (&card_info); - // XXX: I don't know the "right" way to do this. Which to use driver->alsa_name_playback or driver->alsa_name_capture. - if ((err = snd_ctl_open (&driver->ctl_handle, driver->alsa_name_playback, 0)) < 0) { - jack_error ("control open \"%s\" (%s)", driver->alsa_name_playback, snd_strerror(err)); + regcomp(&expression,"(plug)?hw:[0-9](,[0-9])?",REG_ICASE|REG_EXTENDED); + + if (!regexec(&expression,driver->alsa_name_playback,0,NULL,0)) { + /* the user wants a hw or plughw device, the ctl name + * should be hw:x where x is the card number */ + + char tmp[5]; + strncpy(tmp,strstr(driver->alsa_name_playback,"hw"),4); + tmp[4]='\0'; + printf("control device %s\n",tmp); + ctl_name = strdup(tmp); + } else { + ctl_name = strdup(driver->alsa_name_playback); + } + + // XXX: I don't know the "right" way to do this. Which to use + // driver->alsa_name_playback or driver->alsa_name_capture. + if ((err = snd_ctl_open (&driver->ctl_handle, ctl_name, 0)) < 0) { + jack_error ("control open \"%s\" (%s)", ctl_name, + snd_strerror(err)); return -1; } - + if ((err = snd_ctl_card_info(driver->ctl_handle, card_info)) < 0) { - jack_error ("control hardware info \"%s\" (%s)", driver->alsa_name_playback, snd_strerror (err)); + jack_error ("control hardware info \"%s\" (%s)", + driver->alsa_name_playback, snd_strerror (err)); snd_ctl_close (driver->ctl_handle); return -1; } driver->alsa_driver = strdup(snd_ctl_card_info_get_driver (card_info)); + regfree(&expression); + free(ctl_name); + return alsa_driver_check_capabilities (driver); } static int alsa_driver_hammerfall_hardware (alsa_driver_t *driver) - { driver->hw = jack_alsa_hammerfall_hw_new (driver); return 0; @@ -115,7 +138,6 @@ alsa_driver_hammerfall_hardware (alsa_driver_t *driver) static int alsa_driver_hdsp_hardware (alsa_driver_t *driver) - { driver->hw = jack_alsa_hdsp_hw_new (driver); return 0; @@ -123,7 +145,6 @@ alsa_driver_hdsp_hardware (alsa_driver_t *driver) static int alsa_driver_ice1712_hardware (alsa_driver_t *driver) - { driver->hw = jack_alsa_ice1712_hw_new (driver); return 0; @@ -131,15 +152,14 @@ alsa_driver_ice1712_hardware (alsa_driver_t *driver) static int alsa_driver_generic_hardware (alsa_driver_t *driver) - { driver->hw = jack_alsa_generic_hw_new (driver); return 0; } static int -alsa_driver_hw_specific (alsa_driver_t *driver, int hw_monitoring, int hw_metering) - +alsa_driver_hw_specific (alsa_driver_t *driver, int hw_monitoring, + int hw_metering) { int err; @@ -163,7 +183,8 @@ alsa_driver_hw_specific (alsa_driver_t *driver, int hw_monitoring, int hw_meteri if (driver->hw->capabilities & Cap_HardwareMonitoring) { driver->has_hw_monitoring = TRUE; - /* XXX need to ensure that this is really FALSE or TRUE or whatever*/ + /* XXX need to ensure that this is really FALSE or + * TRUE or whatever*/ driver->hw_monitoring = hw_monitoring; } else { driver->has_hw_monitoring = FALSE; @@ -189,7 +210,6 @@ alsa_driver_hw_specific (alsa_driver_t *driver, int hw_monitoring, int hw_meteri static void alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) - { switch (driver->playback_sample_bytes) { case 2: @@ -212,7 +232,8 @@ alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) case Shaped: printf("Noise-shaped dithering at 16 bits\n"); - driver->write_via_copy = sample_move_dither_shaped_d16_sS; + driver->write_via_copy = + sample_move_dither_shaped_d16_sS; break; default: @@ -231,17 +252,20 @@ alsa_driver_setup_io_function_pointers (alsa_driver_t *driver) switch (driver->dither) { case Rectangular: printf("Rectangular dithering at 16 bits\n"); - driver->write_via_copy = sample_move_dither_rect_d32u24_sS; + driver->write_via_copy = + sample_move_dither_rect_d32u24_sS; break; case Triangular: printf("Triangular dithering at 16 bits\n"); - driver->write_via_copy = sample_move_dither_tri_d32u24_sS; + driver->write_via_copy = + sample_move_dither_tri_d32u24_sS; break; case Shaped: printf("Noise-shaped dithering at 16 bits\n"); - driver->write_via_copy = sample_move_dither_shaped_d32u24_sS; + driver->write_via_copy = + sample_move_dither_shaped_d32u24_sS; break; default: @@ -267,7 +291,7 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, snd_pcm_t *handle, snd_pcm_hw_params_t *hw_params, snd_pcm_sw_params_t *sw_params, - unsigned long *nchns) + unsigned long *nchns,unsigned long sample_width) { int err; @@ -277,25 +301,62 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, return -1; } - if ((err = snd_pcm_hw_params_set_periods_integer (handle, hw_params)) < 0) { - jack_error ("ALSA: cannot restrict period size to integral value."); + if ((err = snd_pcm_hw_params_set_periods_integer (handle, hw_params)) + < 0) { + jack_error ("ALSA: cannot restrict period size to integral" + " value."); return -1; } - if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) < 0) { - if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) { - jack_error ("ALSA: mmap-based access is not possible for the %s " - "stream of this audio interface", stream_name); + if ((err = snd_pcm_hw_params_set_access ( + handle, hw_params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) + < 0) { + if ((err = snd_pcm_hw_params_set_access ( + handle, hw_params, + SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) { + jack_error ("ALSA: mmap-based access is not possible" + " for the %s " + "stream of this audio interface", + stream_name); return -1; } } - if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_S32)) < 0) { - if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_S16)) < 0) { - jack_error ("Sorry. The audio interface \"%s\"" - "doesn't support either of the two hardware sample formats that jack can use.", - device_name); - return -1; + + if (sample_width == 4) { + + if ((err = snd_pcm_hw_params_set_format ( + handle, hw_params, SND_PCM_FORMAT_S32)) < 0) { + jack_error("Couldn't open %s for 32bit samples " + "trying 16bit instead",device_name); + if ((err = snd_pcm_hw_params_set_format ( + handle, hw_params, SND_PCM_FORMAT_S16)) + < 0) { + jack_error ("Sorry. The audio interface \"%s\"" + "doesn't support either of the two" + " hardware sample formats that " + "JACK can use.", + device_name); + return -1; + } + } + + } else { + + if ((err = snd_pcm_hw_params_set_format ( + handle, hw_params, SND_PCM_FORMAT_S16)) < 0) { + jack_error("Couldn't open %s for 16bit samples trying" + " 32bit instead",device_name); + if ((err = snd_pcm_hw_params_set_format ( + handle, hw_params, SND_PCM_FORMAT_S32)) + < 0) { + jack_error ("Sorry. The audio interface \"%s\"" + "doesn't support either of the two" + " hardware sample formats that " + "JACK can use.", + device_name); + return -1; + } } } @@ -307,27 +368,36 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, stream_name); return -1; } - - *nchns = snd_pcm_hw_params_get_channels_max (hw_params); - - if (*nchns > 1024) { - - /* the hapless user is an unwitting victim of the "default" - ALSA PCM device, which can support up to 16 million - channels. since they can't be bothered to set up - a proper default device, limit the number of channels - for them to a sane default. - */ - - jack_error ("You appear to be using the ALSA software \"plug\" layer, probably\n" - "a result of using the \"default\" ALSA device. This is less\n" - "efficient than it could be. Consider using a ~/.asoundrc file\n" - "to define a hardware audio device rather than using the plug layer\n"); - *nchns = 2; + if (!*nchns) { + /*if not user-specified, try to find the maximum + * number of channels */ + *nchns = snd_pcm_hw_params_get_channels_max (hw_params); + + if (*nchns > 1024) { + + /* the hapless user is an unwitting victim of + the "default" ALSA PCM device, which can + support up to 16 million channels. since + they can't be bothered to set up a proper + default device, limit the number of + channels for them to a sane default. + */ + + jack_error ( +"You appear to be using the ALSA software \"plug\" layer, probably\n" +"a result of using the \"default\" ALSA device. This is less\n" +"efficient than it could be. Consider using a hardware device\n" +"instead rather than using the plug layer. Usually the name of the\n" +"hardware device that corresponds to the first sound card is hw:0\n" + ); + *nchns = 2; + } } - if ((err = snd_pcm_hw_params_set_channels (handle, hw_params, *nchns)) < 0) { - jack_error ("ALSA: cannot set channel count to %u for %s", *nchns, stream_name); + if ((err = snd_pcm_hw_params_set_channels (handle, hw_params, + *nchns)) < 0) { + jack_error ("ALSA: cannot set channel count to %u for %s", + *nchns, stream_name); return -1; } @@ -341,17 +411,24 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, return -1; } - if ((err = snd_pcm_hw_params_set_periods (handle, hw_params, driver->user_nperiods, 0)) < 0) { - jack_error ("ALSA: cannot set number of periods to %u for %s", driver->user_nperiods, stream_name); + if ((err = snd_pcm_hw_params_set_periods (handle, hw_params, + driver->user_nperiods, 0)) + < 0) { + jack_error ("ALSA: cannot set number of periods to %u for %s", + driver->user_nperiods, stream_name); return -1; } if (!jack_power_of_two(driver->frames_per_cycle)) { - jack_error("JACK: frames must be a power of two (64, 512, 1024, ...)\n"); + jack_error("JACK: frames must be a power of two " + "(64, 512, 1024, ...)\n"); return -1; } - if ((err = snd_pcm_hw_params_set_buffer_size (handle, hw_params, driver->user_nperiods * driver->frames_per_cycle)) < 0) { + if ((err = snd_pcm_hw_params_set_buffer_size (handle, hw_params, + driver->user_nperiods * + driver->frames_per_cycle)) + < 0) { jack_error ("ALSA: cannot set buffer length to %" PRIu32 " for %s", driver->user_nperiods * driver->frames_per_cycle, @@ -360,46 +437,58 @@ alsa_driver_configure_stream (alsa_driver_t *driver, char *device_name, } if ((err = snd_pcm_hw_params (handle, hw_params)) < 0) { - jack_error ("ALSA: cannot set hardware parameters for %s", stream_name); + jack_error ("ALSA: cannot set hardware parameters for %s", + stream_name); return -1; } snd_pcm_sw_params_current (handle, sw_params); - if ((err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, 0U)) < 0) { + if ((err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, + 0U)) < 0) { jack_error ("ALSA: cannot set start mode for %s", stream_name); return -1; } { - snd_pcm_uframes_t stop_th = driver->user_nperiods * driver->frames_per_cycle; + snd_pcm_uframes_t stop_th = + driver->user_nperiods * driver->frames_per_cycle; if (driver->soft_mode) { stop_th = (snd_pcm_uframes_t)-1; } - if ((err = snd_pcm_sw_params_set_stop_threshold (handle, sw_params, stop_th)) < 0) { - jack_error ("ALSA: cannot set stop mode for %s", stream_name); + if ((err = snd_pcm_sw_params_set_stop_threshold ( + handle, sw_params, stop_th)) < 0) { + jack_error ("ALSA: cannot set stop mode for %s", + stream_name); return -1; } } - if ((err = snd_pcm_sw_params_set_silence_threshold (handle, sw_params, 0)) < 0) { - jack_error ("ALSA: cannot set silence threshold for %s", stream_name); + if ((err = snd_pcm_sw_params_set_silence_threshold ( + handle, sw_params, 0)) < 0) { + jack_error ("ALSA: cannot set silence threshold for %s", + stream_name); return -1; } - if ((err = snd_pcm_sw_params_set_silence_size (handle, sw_params, driver->frames_per_cycle * driver->nfragments)) < 0) { - jack_error ("ALSA: cannot set silence size for %s", stream_name); + if ((err = snd_pcm_sw_params_set_silence_size ( + handle, sw_params, + driver->frames_per_cycle * driver->nfragments)) < 0) { + jack_error ("ALSA: cannot set silence size for %s", + stream_name); return -1; } - if ((err = snd_pcm_sw_params_set_avail_min (handle, sw_params, driver->frames_per_cycle)) < 0) { + if ((err = snd_pcm_sw_params_set_avail_min ( + handle, sw_params, driver->frames_per_cycle)) < 0) { jack_error ("ALSA: cannot set avail min for %s", stream_name); return -1; } if ((err = snd_pcm_sw_params (handle, sw_params)) < 0) { - jack_error ("ALSA: cannot set software parameters for %s", stream_name); + jack_error ("ALSA: cannot set software parameters for %s", + stream_name); return -1; } @@ -424,26 +513,30 @@ alsa_driver_set_parameters (alsa_driver_t *driver, driver->user_nperiods = 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_nchannels)) { + if (alsa_driver_configure_stream ( + driver, + driver->alsa_name_capture, + "capture", + driver->capture_handle, + driver->capture_hw_params, + driver->capture_sw_params, + &driver->capture_nchannels, + driver->capture_sample_bytes)) { 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_nchannels)) { + if (alsa_driver_configure_stream ( + driver, + driver->alsa_name_playback, + "playback", + driver->playback_handle, + driver->playback_hw_params, + driver->playback_sw_params, + &driver->playback_nchannels, + driver->playback_sample_bytes)) { jack_error ("ALSA: cannot configure playback channel"); return -1; } @@ -685,39 +778,46 @@ alsa_driver_get_channel_addresses (alsa_driver_t *driver, channel_t chn; if (capture_avail) { - if ((err = snd_pcm_mmap_begin (driver->capture_handle, &driver->capture_areas, - (snd_pcm_uframes_t *) capture_offset, - (snd_pcm_uframes_t *) capture_avail)) < 0) { - jack_error ("ALSA: %s: mmap areas info error", driver->alsa_name_capture); + if ((err = snd_pcm_mmap_begin ( + driver->capture_handle, &driver->capture_areas, + (snd_pcm_uframes_t *) capture_offset, + (snd_pcm_uframes_t *) capture_avail)) < 0) { + jack_error ("ALSA: %s: mmap areas info error", + driver->alsa_name_capture); return -1; } for (chn = 0; chn < driver->capture_nchannels; chn++) { - const snd_pcm_channel_area_t *a = &driver->capture_areas[chn]; - driver->capture_addr[chn] = (char *) a->addr + ((a->first + a->step * *capture_offset) / 8); + const snd_pcm_channel_area_t *a = + &driver->capture_areas[chn]; + driver->capture_addr[chn] = (char *) a->addr + + ((a->first + a->step * *capture_offset) / 8); } } if (playback_avail) { - if ((err = snd_pcm_mmap_begin (driver->playback_handle, &driver->playback_areas, - (snd_pcm_uframes_t *) playback_offset, - (snd_pcm_uframes_t *) playback_avail)) < 0) { - jack_error ("ALSA: %s: mmap areas info error ", driver->alsa_name_playback); + if ((err = snd_pcm_mmap_begin ( + driver->playback_handle, &driver->playback_areas, + (snd_pcm_uframes_t *) playback_offset, + (snd_pcm_uframes_t *) playback_avail)) < 0) { + jack_error ("ALSA: %s: mmap areas info error ", + driver->alsa_name_playback); return -1; } for (chn = 0; chn < driver->playback_nchannels; chn++) { - const snd_pcm_channel_area_t *a = &driver->playback_areas[chn]; - driver->playback_addr[chn] = (char *) a->addr + ((a->first + a->step * *playback_offset) / 8); + const snd_pcm_channel_area_t *a = + &driver->playback_areas[chn]; + driver->playback_addr[chn] = (char *) a->addr + + ((a->first + a->step * *playback_offset) / 8); } } return 0; } - -static int -alsa_driver_audio_start (alsa_driver_t *driver) +static int +alsa_driver_start (alsa_driver_t *driver) { int err; snd_pcm_uframes_t poffset, pavail; @@ -728,21 +828,26 @@ alsa_driver_audio_start (alsa_driver_t *driver) if (driver->playback_handle) { if ((err = snd_pcm_prepare (driver->playback_handle)) < 0) { - jack_error ("ALSA: prepare error for playback on \"%s\" (%s)", driver->alsa_name_playback, snd_strerror(err)); + jack_error ("ALSA: prepare error for playback on " + "\"%s\" (%s)", driver->alsa_name_playback, + snd_strerror(err)); return -1; } } - if ((driver->capture_handle && driver->capture_and_playback_not_synced) || - !driver->playback_handle) { + if ((driver->capture_handle && driver->capture_and_playback_not_synced) + || !driver->playback_handle) { if ((err = snd_pcm_prepare (driver->capture_handle)) < 0) { - jack_error ("ALSA: prepare error for capture on \"%s\" (%s)", driver->alsa_name_capture, snd_strerror(err)); + jack_error ("ALSA: prepare error for capture on \"%s\"" + " (%s)", driver->alsa_name_capture, + snd_strerror(err)); return -1; } } if (driver->hw_monitoring) { - driver->hw->set_input_monitor_mask (driver->hw, driver->input_monitor_mask); + driver->hw->set_input_monitor_mask (driver->hw, + driver->input_monitor_mask); } if (driver->playback_handle) { @@ -757,82 +862,118 @@ alsa_driver_audio_start (alsa_driver_t *driver) return -1; } - if (alsa_driver_get_channel_addresses (driver, 0, &pavail, 0, &poffset)) { + if (alsa_driver_get_channel_addresses (driver, 0, &pavail, + 0, &poffset)) { return -1; } - /* XXX this is cheating. ALSA offers no guarantee that we can access - the entire buffer at any one time. It works on most hardware - tested so far, however, buts its a liability in the long run. I think - that alsa-lib may have a better function for doing this here, where - the goal is to silence the entire buffer. + /* XXX this is cheating. ALSA offers no guarantee that + we can access the entire buffer at any one time. It + works on most hardware tested so far, however, buts + its a liability in the long run. I think that + alsa-lib may have a better function for doing this + here, where the goal is to silence the entire + buffer. */ for (chn = 0; chn < driver->playback_nchannels; chn++) { - alsa_driver_silence_on_channel (driver, chn, driver->buffer_frames); + alsa_driver_silence_on_channel (driver, chn, + driver->buffer_frames); } - snd_pcm_mmap_commit (driver->playback_handle, poffset, driver->buffer_frames); + snd_pcm_mmap_commit (driver->playback_handle, poffset, + driver->buffer_frames); if ((err = snd_pcm_start (driver->playback_handle)) < 0) { - jack_error ("could not start playback (%s)", snd_strerror (err)); + jack_error ("could not start playback (%s)", + snd_strerror (err)); return -1; } } - if ((driver->capture_handle && driver->capture_and_playback_not_synced) || - !driver->playback_handle) { + if ((driver->capture_handle && driver->capture_and_playback_not_synced) + || !driver->playback_handle) { if ((err = snd_pcm_start (driver->capture_handle)) < 0) { - jack_error ("could not start capture (%s)", snd_strerror (err)); + jack_error ("could not start capture (%s)", + snd_strerror (err)); return -1; } } - if (driver->hw_monitoring && (driver->input_monitor_mask || driver->all_monitor_in)) { + if (driver->hw_monitoring && + (driver->input_monitor_mask || driver->all_monitor_in)) { if (driver->all_monitor_in) { driver->hw->set_input_monitor_mask (driver->hw, ~0U); } else { - driver->hw->set_input_monitor_mask (driver->hw, driver->input_monitor_mask); + driver->hw->set_input_monitor_mask ( + driver->hw, driver->input_monitor_mask); } } if (driver->playback_handle) { - driver->playback_nfds = snd_pcm_poll_descriptors_count (driver->playback_handle); + driver->playback_nfds = + snd_pcm_poll_descriptors_count ( + driver->playback_handle); } else { driver->playback_nfds = 0; } if (driver->capture_handle) { - driver->capture_nfds = snd_pcm_poll_descriptors_count (driver->capture_handle); + driver->capture_nfds = + snd_pcm_poll_descriptors_count (driver->capture_handle); } else { driver->capture_nfds = 0; } - if (driver->pfd) + if (driver->pfd) { free (driver->pfd); - driver->pfd = (struct pollfd *) malloc (sizeof (struct pollfd) * - (driver->playback_nfds + driver->capture_nfds + 2)); + } + + driver->pfd = (struct pollfd *) + malloc (sizeof (struct pollfd) * + (driver->playback_nfds + driver->capture_nfds + 2)); return 0; } static int -alsa_driver_audio_stop (alsa_driver_t *driver) - +alsa_driver_stop (alsa_driver_t *driver) { int err; + JSList* node; + int chn; + + /* silence all capture port buffers, because we might + be entering offline mode. + */ + + for (chn = 0, node = driver->capture_ports; node; + node = jack_slist_next (node), chn++) { + + jack_port_t* port; + char* buf; + jack_nframes_t nframes = driver->engine->control->buffer_size; + port = (jack_port_t *) node->data; + buf = jack_port_get_buffer (port, nframes); + memset (buf, 0, sizeof (jack_default_audio_sample_t) * nframes); + } + if (driver->playback_handle) { if ((err = snd_pcm_drop (driver->playback_handle)) < 0) { - jack_error ("alsa_pcm: channel flush for playback failed (%s)", snd_strerror (err)); + jack_error ("alsa_pcm: channel flush for playback " + "failed (%s)", snd_strerror (err)); return -1; } } - if (!driver->playback_handle || driver->capture_and_playback_not_synced) { + if (!driver->playback_handle + || driver->capture_and_playback_not_synced) { if (driver->capture_handle) { if ((err = snd_pcm_drop (driver->capture_handle)) < 0) { - jack_error ("alsa_pcm: channel flush for capture failed (%s)", snd_strerror (err)); + jack_error ("alsa_pcm: channel flush for " + "capture failed (%s)", + snd_strerror (err)); return -1; } } @@ -847,7 +988,6 @@ alsa_driver_audio_stop (alsa_driver_t *driver) static int alsa_driver_xrun_recovery (alsa_driver_t *driver) - { snd_pcm_status_t *status; int res; @@ -855,25 +995,32 @@ alsa_driver_xrun_recovery (alsa_driver_t *driver) snd_pcm_status_alloca(&status); if (driver->capture_handle) { - if ((res = snd_pcm_status(driver->capture_handle, status)) < 0) { + if ((res = snd_pcm_status(driver->capture_handle, status)) + < 0) { jack_error("status error: %s", snd_strerror(res)); } } else { - if ((res = snd_pcm_status(driver->playback_handle, status)) < 0) { + if ((res = snd_pcm_status(driver->playback_handle, status)) + < 0) { jack_error("status error: %s", snd_strerror(res)); } } - if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN && driver->xrun_count == 0 && driver->process_count > XRUN_REPORT_DELAY) { + if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN + && driver->xrun_count == 0 + && driver->process_count > XRUN_REPORT_DELAY) { struct timeval now, diff, tstamp; driver->xrun_count++; gettimeofday(&now, 0); snd_pcm_status_get_trigger_tstamp(status, &tstamp); timersub(&now, &tstamp, &diff); - fprintf(stderr, "\n\n**** alsa_pcm: xrun of at least %.3f msecs\n\n", diff.tv_sec * 1000 + diff.tv_usec / 1000.0); + fprintf(stderr, "\n\n**** alsa_pcm: xrun of at least %.3f " + "msecs\n\n", + diff.tv_sec * 1000 + diff.tv_usec / 1000.0); } - if (alsa_driver_audio_stop (driver) || alsa_driver_audio_start (driver)) { + if (alsa_driver_stop (driver) || + alsa_driver_start (driver)) { return -1; } @@ -881,15 +1028,16 @@ alsa_driver_xrun_recovery (alsa_driver_t *driver) } static void -alsa_driver_silence_untouched_channels (alsa_driver_t *driver, jack_nframes_t nframes) - +alsa_driver_silence_untouched_channels (alsa_driver_t *driver, + jack_nframes_t nframes) { channel_t chn; for (chn = 0; chn < driver->playback_nchannels; chn++) { if ((driver->channels_not_done & (1<silent[chn] < driver->buffer_frames) { - alsa_driver_silence_on_channel_no_mark (driver, chn, nframes); + alsa_driver_silence_on_channel_no_mark ( + driver, chn, nframes); driver->silent[chn] += nframes; } } @@ -897,8 +1045,8 @@ alsa_driver_silence_untouched_channels (alsa_driver_t *driver, jack_nframes_t nf } void -alsa_driver_set_clock_sync_status (alsa_driver_t *driver, channel_t chn, ClockSyncStatus status) - +alsa_driver_set_clock_sync_status (alsa_driver_t *driver, channel_t chn, + ClockSyncStatus status) { driver->clock_sync_data[chn] = status; alsa_driver_clock_sync_notify (driver, chn, status); @@ -907,7 +1055,8 @@ alsa_driver_set_clock_sync_status (alsa_driver_t *driver, channel_t chn, ClockSy static int under_gdb = FALSE; static jack_nframes_t -alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delayed_usecs) +alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float + *delayed_usecs) { snd_pcm_sframes_t avail = 0; snd_pcm_sframes_t capture_avail = 0; @@ -941,12 +1090,16 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delay nfds = 0; if (need_playback) { - snd_pcm_poll_descriptors (driver->playback_handle, &driver->pfd[0], driver->playback_nfds); + snd_pcm_poll_descriptors (driver->playback_handle, + &driver->pfd[0], + driver->playback_nfds); nfds += driver->playback_nfds; } if (need_capture) { - snd_pcm_poll_descriptors (driver->capture_handle, &driver->pfd[nfds], driver->capture_nfds); + snd_pcm_poll_descriptors (driver->capture_handle, + &driver->pfd[nfds], + driver->capture_nfds); ci = nfds; nfds += driver->capture_nfds; } @@ -959,7 +1112,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delay if (extra_fd >= 0) { driver->pfd[nfds].fd = extra_fd; - driver->pfd[nfds].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; + driver->pfd[nfds].events = + POLLIN|POLLERR|POLLHUP|POLLNVAL; nfds++; } @@ -978,7 +1132,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delay return 0; } - jack_error ("ALSA: poll call failed (%s)", strerror (errno)); + jack_error ("ALSA: poll call failed (%s)", + strerror (errno)); *status = -3; return 0; @@ -1002,8 +1157,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delay poll_ret - poll_enter); #endif - /* check to see if it was the extra FD that caused us to return from poll - */ + /* check to see if it was the extra FD that caused us + * to return from poll */ if (extra_fd >= 0) { @@ -1088,27 +1243,33 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float *delay } if (driver->capture_handle) { - if ((capture_avail = snd_pcm_avail_update (driver->capture_handle)) < 0) { + if ((capture_avail = snd_pcm_avail_update ( + driver->capture_handle)) < 0) { if (capture_avail == -EPIPE) { xrun_detected = TRUE; } else { - jack_error ("unknown ALSA avail_update return value (%u)", capture_avail); + jack_error ("unknown ALSA avail_update return" + " value (%u)", capture_avail); } } } else { - capture_avail = INT_MAX; /* odd, but see min() computation below */ + /* odd, but see min() computation below */ + capture_avail = INT_MAX; } if (driver->playback_handle) { - if ((playback_avail = snd_pcm_avail_update (driver->playback_handle)) < 0) { + if ((playback_avail = snd_pcm_avail_update ( + driver->playback_handle)) < 0) { if (playback_avail == -EPIPE) { xrun_detected = TRUE; } else { - jack_error ("unknown ALSA avail_update return value (%u)", playback_avail); + jack_error ("unknown ALSA avail_update return" + " value (%u)", playback_avail); } } } else { - playback_avail = INT_MAX; /* odd, but see min() computation below */ + /* odd, but see min() computation below */ + playback_avail = INT_MAX; } if (xrun_detected) { @@ -1151,15 +1312,19 @@ alsa_driver_null_cycle (alsa_driver_t* driver, jack_nframes_t nframes) offset = 0; while (nf) { - contiguous = (nf > driver->frames_per_cycle) ? driver->frames_per_cycle : nf; + contiguous = (nf > driver->frames_per_cycle) ? + driver->frames_per_cycle : nf; - if (snd_pcm_mmap_begin (driver->capture_handle, &driver->capture_areas, - (snd_pcm_uframes_t *) &offset, - (snd_pcm_uframes_t *) &contiguous)) { + if (snd_pcm_mmap_begin ( + driver->capture_handle, + &driver->capture_areas, + (snd_pcm_uframes_t *) &offset, + (snd_pcm_uframes_t *) &contiguous)) { return -1; } - if (snd_pcm_mmap_commit (driver->capture_handle, offset, contiguous) < 0) { + if (snd_pcm_mmap_commit (driver->capture_handle, + offset, contiguous) < 0) { return -1; } @@ -1171,19 +1336,24 @@ alsa_driver_null_cycle (alsa_driver_t* driver, jack_nframes_t nframes) nf = nframes; offset = 0; while (nf) { - contiguous = (nf > driver->frames_per_cycle) ? driver->frames_per_cycle : nf; + contiguous = (nf > driver->frames_per_cycle) ? + driver->frames_per_cycle : nf; - if (snd_pcm_mmap_begin (driver->playback_handle, &driver->playback_areas, - (snd_pcm_uframes_t *) &offset, - (snd_pcm_uframes_t *) &contiguous)) { + if (snd_pcm_mmap_begin ( + driver->playback_handle, + &driver->playback_areas, + (snd_pcm_uframes_t *) &offset, + (snd_pcm_uframes_t *) &contiguous)) { return -1; } for (chn = 0; chn < driver->playback_nchannels; chn++) { - alsa_driver_silence_on_channel (driver, chn, contiguous); + alsa_driver_silence_on_channel (driver, chn, + contiguous); } - if (snd_pcm_mmap_commit (driver->playback_handle, offset, contiguous) < 0) { + if (snd_pcm_mmap_commit (driver->playback_handle, + offset, contiguous) < 0) { return -1; } @@ -1197,23 +1367,9 @@ alsa_driver_null_cycle (alsa_driver_t* driver, jack_nframes_t nframes) static int alsa_driver_bufsize (alsa_driver_t* driver, jack_nframes_t nframes) { - int rc; - - if (alsa_driver_audio_stop (driver)) { - jack_error ("ALSA: cannot stop to set buffer size"); - return EIO; - } - - rc = alsa_driver_reset_parameters (driver, nframes, - driver->user_nperiods, - driver->frame_rate); - - if (alsa_driver_audio_start (driver)) { - jack_error ("ALSA: cannot restart after setting buffer size"); - rc = rc? rc: EIO; - } - - return rc; + return alsa_driver_reset_parameters (driver, nframes, + driver->user_nperiods, + driver->frame_rate); } static int @@ -1227,7 +1383,7 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) JSList *node; jack_port_t* port; - if (!driver->capture_handle) { + if (!driver->capture_handle || driver->engine->freewheeling) { return 0; } @@ -1236,16 +1392,19 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) while (nframes) { - contiguous = (nframes > driver->frames_per_cycle) ? driver->frames_per_cycle : nframes; + contiguous = (nframes > driver->frames_per_cycle) ? + driver->frames_per_cycle : nframes; - if (alsa_driver_get_channel_addresses (driver, - (snd_pcm_uframes_t *) &contiguous, - (snd_pcm_uframes_t *) 0, - &offset, 0) < 0) { + if (alsa_driver_get_channel_addresses ( + driver, + (snd_pcm_uframes_t *) &contiguous, + (snd_pcm_uframes_t *) 0, + &offset, 0) < 0) { return -1; } - for (chn = 0, node = driver->capture_ports; node; node = jack_slist_next (node), chn++) { + for (chn = 0, node = driver->capture_ports; node; + node = jack_slist_next (node), chn++) { port = (jack_port_t *) node->data; @@ -1255,10 +1414,13 @@ alsa_driver_read (alsa_driver_t *driver, jack_nframes_t nframes) } buf = jack_port_get_buffer (port, nframes); - alsa_driver_read_from_channel (driver, chn, buf + nread, contiguous); + alsa_driver_read_from_channel (driver, chn, + buf + nread, + contiguous); } - if (snd_pcm_mmap_commit (driver->capture_handle, offset, contiguous) < 0) { + if (snd_pcm_mmap_commit (driver->capture_handle, offset, + contiguous) < 0) { jack_error ("alsa_pcm: could not complete read of %" PRIu32 " frames\n", contiguous); return -1; @@ -1284,7 +1446,7 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) driver->process_count++; - if (!driver->playback_handle) { + if (!driver->playback_handle || driver->engine->freewheeling) { return 0; } @@ -1295,30 +1457,37 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) driver->input_monitor_mask = 0; - for (chn = 0, node = driver->capture_ports; node; node = jack_slist_next (node), chn++) { + for (chn = 0, node = driver->capture_ports; node; + node = jack_slist_next (node), chn++) { if (((jack_port_t *) node->data)->shared->monitor_requests) { driver->input_monitor_mask |= (1<hw_monitoring) { - if ((driver->hw->input_monitor_mask != driver->input_monitor_mask) && !driver->all_monitor_in) { - driver->hw->set_input_monitor_mask (driver->hw, driver->input_monitor_mask); + if ((driver->hw->input_monitor_mask + != driver->input_monitor_mask) + && !driver->all_monitor_in) { + driver->hw->set_input_monitor_mask ( + driver->hw, driver->input_monitor_mask); } } while (nframes) { - contiguous = (nframes > driver->frames_per_cycle) ? driver->frames_per_cycle : nframes; + contiguous = (nframes > driver->frames_per_cycle) ? + driver->frames_per_cycle : nframes; - if (alsa_driver_get_channel_addresses (driver, - (snd_pcm_uframes_t *) 0, - (snd_pcm_uframes_t *) &contiguous, - 0, &offset) < 0) { + if (alsa_driver_get_channel_addresses ( + driver, + (snd_pcm_uframes_t *) 0, + (snd_pcm_uframes_t *) &contiguous, + 0, &offset) < 0) { return -1; } - for (chn = 0, node = driver->playback_ports; node; node = jack_slist_next (node), chn++) { + for (chn = 0, node = driver->playback_ports; node; + node = jack_slist_next (node), chn++) { port = (jack_port_t *) node->data; @@ -1328,11 +1497,14 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) buf = jack_port_get_buffer (port, contiguous); - alsa_driver_write_to_channel (driver, chn, buf + nwritten, contiguous); + alsa_driver_write_to_channel (driver, chn, + buf + nwritten, + contiguous); } if (driver->channels_not_done) { - alsa_driver_silence_untouched_channels (driver, contiguous); + alsa_driver_silence_untouched_channels (driver, + contiguous); } if ((err = snd_pcm_mmap_commit (driver->playback_handle, @@ -1351,19 +1523,45 @@ alsa_driver_write (alsa_driver_t* driver, jack_nframes_t nframes) return 0; } +static inline int +alsa_driver_run_cycle (alsa_driver_t *driver) +{ + jack_engine_t *engine = driver->engine; + int wait_status; + float delayed_usecs; + + jack_nframes_t nframes = alsa_driver_wait (driver, -1, &wait_status, + &delayed_usecs); + + if (nframes == 0) { + + /* we detected an xrun and restarted: notify + * clients about the delay. + */ + + engine->delay (engine); + return 0; + } + + if (wait_status == 0) + return engine->run_cycle (engine, nframes, delayed_usecs); + + if (wait_status < 0) + return -1; /* driver failed */ + else + return 0; +} static int -alsa_driver_attach (alsa_driver_t *driver, jack_engine_t *engine) +alsa_driver_attach (alsa_driver_t *driver) { char buf[32]; channel_t chn; jack_port_t *port; int port_flags; - driver->engine = engine; - - driver->engine->set_buffer_size (engine, driver->frames_per_cycle); - driver->engine->set_sample_rate (engine, driver->frame_rate); + driver->engine->set_buffer_size (driver->engine, driver->frames_per_cycle); + driver->engine->set_sample_rate (driver->engine, driver->frame_rate); port_flags = JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal; @@ -1375,16 +1573,20 @@ alsa_driver_attach (alsa_driver_t *driver, jack_engine_t *engine) snprintf (buf, sizeof(buf) - 1, "capture_%lu", chn+1); - if ((port = jack_port_register (driver->client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0)) == NULL) { + if ((port = jack_port_register (driver->client, buf, + JACK_DEFAULT_AUDIO_TYPE, + port_flags, 0)) == NULL) { jack_error ("ALSA: cannot register port for %s", buf); break; } - /* XXX fix this so that it can handle: systemic (external) latency + /* XXX fix this so that it can handle: systemic + * (external) latency */ jack_port_set_latency (port, driver->frames_per_cycle); - driver->capture_ports = jack_slist_append (driver->capture_ports, port); + driver->capture_ports = + jack_slist_append (driver->capture_ports, port); } port_flags = JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal; @@ -1394,57 +1596,69 @@ alsa_driver_attach (alsa_driver_t *driver, jack_engine_t *engine) snprintf (buf, sizeof(buf) - 1, "playback_%lu", chn+1); - if ((port = jack_port_register (driver->client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0)) == NULL) { + if ((port = jack_port_register (driver->client, buf, + JACK_DEFAULT_AUDIO_TYPE, + port_flags, 0)) == NULL) { jack_error ("ALSA: cannot register port for %s", buf); break; } - /* XXX fix this so that it can handle: systemic (external) latency + /* XXX fix this so that it can handle: systemic + * (external) latency */ - jack_port_set_latency (port, driver->frames_per_cycle * driver->nfragments); + jack_port_set_latency (port, driver->frames_per_cycle + * driver->nfragments); - driver->playback_ports = jack_slist_append (driver->playback_ports, port); + driver->playback_ports = + jack_slist_append (driver->playback_ports, port); if (driver->with_monitor_ports) { snprintf (buf, sizeof(buf) - 1, "monitor_%lu", chn+1); - if ((monitor_port = jack_port_register (driver->client, buf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)) == NULL) { - jack_error ("ALSA: cannot register monitor port for %s", buf); + if ((monitor_port = jack_port_register ( + driver->client, buf, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0)) == NULL) { + jack_error ("ALSA: cannot register monitor " + "port for %s", buf); } else { jack_port_tie (port, monitor_port); } } } - jack_activate (driver->client); - return 0; -} -static void -alsa_driver_detach (alsa_driver_t *driver, jack_engine_t *engine) + return jack_activate (driver->client); +} +static int +alsa_driver_detach (alsa_driver_t *driver) { JSList *node; - if (driver->engine == 0) { - return; + if (driver->engine == NULL) { + return 0; } - for (node = driver->capture_ports; node; node = jack_slist_next (node)) { - jack_port_unregister (driver->client, ((jack_port_t *) node->data)); + for (node = driver->capture_ports; node; + node = jack_slist_next (node)) { + jack_port_unregister (driver->client, + ((jack_port_t *) node->data)); } jack_slist_free (driver->capture_ports); driver->capture_ports = 0; - for (node = driver->playback_ports; node; node = jack_slist_next (node)) { - jack_port_unregister (driver->client, ((jack_port_t *) node->data)); + for (node = driver->playback_ports; node; + node = jack_slist_next (node)) { + jack_port_unregister (driver->client, + ((jack_port_t *) node->data)); } jack_slist_free (driver->playback_ports); driver->playback_ports = 0; - - driver->engine = 0; + + return 0; } #if 0 @@ -1463,7 +1677,8 @@ alsa_driver_request_all_monitor_input (alsa_driver_t *driver, int yn) if (yn) { driver->hw->set_input_monitor_mask (driver->hw, ~0U); } else { - driver->hw->set_input_monitor_mask (driver->hw, driver->input_monitor_mask); + driver->hw->set_input_monitor_mask ( + driver->hw, driver->input_monitor_mask); } } @@ -1472,7 +1687,6 @@ alsa_driver_request_all_monitor_input (alsa_driver_t *driver, int yn) static void /* UNUSED */ alsa_driver_set_hw_monitoring (alsa_driver_t *driver, int yn) - { if (yn) { driver->hw_monitoring = TRUE; @@ -1480,7 +1694,8 @@ alsa_driver_set_hw_monitoring (alsa_driver_t *driver, int yn) if (driver->all_monitor_in) { driver->hw->set_input_monitor_mask (driver->hw, ~0U); } else { - driver->hw->set_input_monitor_mask (driver->hw, driver->input_monitor_mask); + driver->hw->set_input_monitor_mask ( + driver->hw, driver->input_monitor_mask); } } else { driver->hw_monitoring = FALSE; @@ -1490,7 +1705,6 @@ alsa_driver_set_hw_monitoring (alsa_driver_t *driver, int yn) static ClockSyncStatus /* UNUSED */ alsa_driver_clock_sync_status (channel_t chn) - { return Lock; } @@ -1498,11 +1712,11 @@ alsa_driver_clock_sync_status (channel_t chn) static void alsa_driver_delete (alsa_driver_t *driver) - { JSList *node; - for (node = driver->clock_sync_listeners; node; node = jack_slist_next (node)) { + for (node = driver->clock_sync_listeners; node; + node = jack_slist_next (node)) { free (node->data); } jack_slist_free (driver->clock_sync_listeners); @@ -1550,6 +1764,7 @@ alsa_driver_delete (alsa_driver_t *driver) free(driver->alsa_driver); alsa_driver_release_channel_dependent_memory (driver); + jack_driver_nt_finish ((jack_driver_nt_t *) driver); free (driver); } @@ -1566,33 +1781,40 @@ alsa_driver_new (char *name, char *playback_alsa_device, int playing, DitherAlgorithm dither, int soft_mode, - int monitor) + int monitor, + int user_capture_nchnls, + int user_playback_nchnls, + int shorts_first + ) { int err; alsa_driver_t *driver; printf ("creating alsa driver ... %s|%s|%" PRIu32 "|%" PRIu32 - "|%" PRIu32 "|%s|%s|%s\n", playback_alsa_device, - capture_alsa_device, frames_per_cycle, user_nperiods, rate, + "|%" PRIu32"|%" PRIu32"|%" PRIu32 "|%s|%s|%s|%s\n", + playback_alsa_device,capture_alsa_device, + frames_per_cycle, user_nperiods, rate, + user_capture_nchnls,user_playback_nchnls, hw_monitoring ? "hwmon": "nomon", hw_metering ? "hwmeter":"swmeter", - soft_mode ? "soft-mode":"rt"); + soft_mode ? "soft-mode":"rt", + shorts_first ? "16bit":"32bit"); driver = (alsa_driver_t *) calloc (1, sizeof (alsa_driver_t)); - jack_driver_init ((jack_driver_t *) driver); + jack_driver_nt_init ((jack_driver_nt_t *) driver); - driver->attach = (JackDriverAttachFunction) alsa_driver_attach; - driver->detach = (JackDriverDetachFunction) alsa_driver_detach; - driver->wait = (JackDriverWaitFunction) alsa_driver_wait; + driver->nt_attach = (JackDriverNTAttachFunction) alsa_driver_attach; + driver->nt_detach = (JackDriverNTDetachFunction) alsa_driver_detach; driver->read = (JackDriverReadFunction) alsa_driver_read; driver->write = (JackDriverReadFunction) alsa_driver_write; driver->null_cycle = (JackDriverNullCycleFunction) alsa_driver_null_cycle; - driver->bufsize = (JackDriverBufSizeFunction) alsa_driver_bufsize; - driver->start = (JackDriverStartFunction) alsa_driver_audio_start; - driver->stop = (JackDriverStopFunction) alsa_driver_audio_stop; + driver->nt_bufsize = (JackDriverNTBufSizeFunction) alsa_driver_bufsize; + driver->nt_start = (JackDriverNTStartFunction) alsa_driver_start; + driver->nt_stop = (JackDriverNTStopFunction) alsa_driver_stop; + driver->nt_run_cycle = (JackDriverNTRunCycleFunction) alsa_driver_run_cycle; driver->playback_handle = NULL; driver->capture_handle = NULL; @@ -1602,10 +1824,15 @@ alsa_driver_new (char *name, char *playback_alsa_device, driver->nfragments = 0; driver->max_nchannels = 0; driver->user_nchannels = 0; - driver->playback_nchannels = 0; - driver->capture_nchannels = 0; + driver->playback_nchannels = user_playback_nchnls; + driver->capture_nchannels = user_capture_nchnls; + driver->playback_sample_bytes = (shorts_first ? 2:4); + driver->capture_sample_bytes = (shorts_first ? 2:4); + driver->playback_addr = 0; driver->capture_addr = 0; + + driver->silent = 0; driver->all_monitor_in = FALSE; driver->with_monitor_ports = monitor; @@ -1630,10 +1857,16 @@ alsa_driver_new (char *name, char *playback_alsa_device, driver->process_count = 0; if (playing) { - if (snd_pcm_open (&driver->playback_handle, playback_alsa_device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) { + if (snd_pcm_open (&driver->playback_handle, + playback_alsa_device, + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK) < 0) { if (errno == -EBUSY) { - jack_error ("the playback device \"%s\" is already in use. Please stop the application using it and " - "run JACK again", playback_alsa_device); + jack_error ("the playback device \"%s\" is " + "already in use. Please stop the" + " application using it and " + "run JACK again", + playback_alsa_device); return NULL; } driver->playback_handle = NULL; @@ -1642,10 +1875,16 @@ alsa_driver_new (char *name, char *playback_alsa_device, } if (capturing) { - if (snd_pcm_open (&driver->capture_handle, capture_alsa_device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) < 0) { + if (snd_pcm_open (&driver->capture_handle, + capture_alsa_device, + SND_PCM_STREAM_CAPTURE, + SND_PCM_NONBLOCK) < 0) { if (errno == -EBUSY) { - jack_error ("the capture device \"%s\" is already in use. Please stop the application using it and " - "run JACK again", capture_alsa_device); + jack_error ("the capture device \"%s\" is " + "already in use. Please stop the" + " application using it and " + "run JACK again", + capture_alsa_device); return NULL; } driver->capture_handle = NULL; @@ -1653,15 +1892,14 @@ alsa_driver_new (char *name, char *playback_alsa_device, snd_pcm_nonblock (driver->capture_handle, 0); } - fprintf (stderr, "open\n"); - if (driver->playback_handle == NULL) { if (playing) { /* they asked for playback, but we can't do it */ - jack_error ("ALSA: Cannot open PCM device %s for playback. Falling back to capture-only mode", - name); + jack_error ("ALSA: Cannot open PCM device %s for " + "playback. Falling back to capture-only" + " mode", name); if (driver->capture_handle == NULL) { /* can't do anything */ @@ -1678,8 +1916,9 @@ alsa_driver_new (char *name, char *playback_alsa_device, /* they asked for capture, but we can't do it */ - jack_error ("ALSA: Cannot open PCM device %s for capture. Falling back to playback-only mode", - name); + jack_error ("ALSA: Cannot open PCM device %s for " + "capture. Falling back to playback-only" + " mode", name); if (driver->playback_handle == NULL) { /* can't do anything */ @@ -1711,28 +1950,36 @@ alsa_driver_new (char *name, char *playback_alsa_device, driver->capture_sw_params = 0; if (driver->playback_handle) { - if ((err = snd_pcm_hw_params_malloc (&driver->playback_hw_params)) < 0) { - jack_error ("ALSA: could no allocate playback hw params structure"); + if ((err = snd_pcm_hw_params_malloc ( + &driver->playback_hw_params)) < 0) { + jack_error ("ALSA: could not allocate playback hw" + " params structure"); alsa_driver_delete (driver); return 0; } - if ((err = snd_pcm_sw_params_malloc (&driver->playback_sw_params)) < 0) { - jack_error ("ALSA: could no allocate playback sw params structure"); + if ((err = snd_pcm_sw_params_malloc ( + &driver->playback_sw_params)) < 0) { + jack_error ("ALSA: could not allocate playback sw" + " params structure"); alsa_driver_delete (driver); return 0; } } if (driver->capture_handle) { - if ((err = snd_pcm_hw_params_malloc (&driver->capture_hw_params)) < 0) { - jack_error ("ALSA: could no allocate capture hw params structure"); + if ((err = snd_pcm_hw_params_malloc ( + &driver->capture_hw_params)) < 0) { + jack_error ("ALSA: could not allocate capture hw" + " params structure"); alsa_driver_delete (driver); return 0; } - if ((err = snd_pcm_sw_params_malloc (&driver->capture_sw_params)) < 0) { - jack_error ("ALSA: could no allocate capture sw params structure"); + if ((err = snd_pcm_sw_params_malloc ( + &driver->capture_sw_params)) < 0) { + jack_error ("ALSA: could not allocate capture sw" + " params structure"); alsa_driver_delete (driver); return 0; } @@ -1773,21 +2020,26 @@ alsa_driver_listen_for_clock_sync_status (alsa_driver_t *driver, 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); + 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) +alsa_driver_stop_listening_to_clock_sync_status (alsa_driver_t *driver, + unsigned int which) { JSList *node; int ret = -1; pthread_mutex_lock (&driver->clock_sync_lock); - for (node = driver->clock_sync_listeners; node; node = jack_slist_next (node)) { + for (node = driver->clock_sync_listeners; node; + node = jack_slist_next (node)) { if (((ClockSyncListener *) node->data)->id == which) { - driver->clock_sync_listeners = jack_slist_remove_link (driver->clock_sync_listeners, node); + driver->clock_sync_listeners = + jack_slist_remove_link ( + driver->clock_sync_listeners, node); free (node->data); jack_slist_free_1 (node); ret = 0; @@ -1799,12 +2051,14 @@ alsa_driver_stop_listening_to_clock_sync_status (alsa_driver_t *driver, unsigned } void -alsa_driver_clock_sync_notify (alsa_driver_t *driver, channel_t chn, ClockSyncStatus status) +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)) { + for (node = driver->clock_sync_listeners; node; + node = jack_slist_next (node)) { ClockSyncListener *csl = (ClockSyncListener *) node->data; csl->function (chn, status, csl->arg); } @@ -1812,42 +2066,10 @@ alsa_driver_clock_sync_notify (alsa_driver_t *driver, channel_t chn, ClockSyncSt } -static void -alsa_usage () -{ - fprintf (stderr, "\n" -"ALSA driver arguments:\n" -" -h,--help \tprint this message\n" -" -d,--device \tALSA device name (default: \"default\")\n" -" -r,--rate \tsample rate (default: 48000)\n" -" -p,--period \tframes per period (default: 1024)\n" -" -n,--nperiods \tnumber of periods in hardware buffer (default: 2)\n" -" -H,--hwmon \tuse hardware monitoring, if available (default: no)\n" -" -M,--hwmeter \tuse hardware metering, if available (default: no)\n" -" -D,--duplex \tduplex I/O (default: yes)\n" -" -C,--capture [name] \tcapture input and optionally set the capture device (default: duplex)\n" -" -P,--playback [name] \tplayback output and optionally set the playback device (default: duplex)\n" -" -s,--softmode\tsoft-mode, no xrun handling (default: off)\n" -" -m,--monitor \tprovide monitor ports for the output (default: off)\n" -" -z,--dither \tdithering mode:\n" -" -zn,--dither=none (off, the default)\n" -" -zr,--dither=rectangular\n" -" -zs,--dither=shaped\n" -" -zt,--dither=triangular\n" -"\n"); -} - -static void -alsa_error (char *type, char *value) -{ - fprintf (stderr, "ALSA driver: unknown %s: `%s'\n", type, value); - alsa_usage(); -} - static int dither_opt (char c, DitherAlgorithm* dither) { - switch (*optarg) { + switch (c) { case '-': case 'n': *dither = None; @@ -1866,7 +2088,7 @@ dither_opt (char c, DitherAlgorithm* dither) break; default: - alsa_error ("illegal dithering mode", ""); + fprintf (stderr, "ALSA driver: illegal dithering mode %c\n", c); return -1; } return 0; @@ -1877,14 +2099,176 @@ dither_opt (char c, DitherAlgorithm* dither) const char driver_client_name[] = "alsa_pcm"; +const jack_driver_desc_t * +driver_get_descriptor () +{ + jack_driver_desc_t * desc; + jack_driver_param_desc_t * params; + unsigned int i; + + desc = calloc (1, sizeof (jack_driver_desc_t)); + + strcpy (desc->name,"alsa"); + desc->nparams = 15; + + params = calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); + + i = 0; + strcpy (params[i].name, "capture"); + params[i].character = 'C'; + params[i].has_arg = optional_argument; + params[i].type = JackDriverParamString; + strcpy (params[i].short_desc, + "Provide only capture ports. Optionally set device"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "playback"); + params[i].character = 'P'; + params[i].has_arg = optional_argument; + params[i].type = JackDriverParamString; + strcpy (params[i].short_desc, + "Provide only playback ports. Optionally set device"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "device"); + params[i].character = 'd'; + params[i].has_arg = required_argument; + params[i].type = JackDriverParamString; + strcpy (params[i].value.str, "hw:0"); + strcpy (params[i].short_desc, "ALSA device name"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "rate"); + params[i].character = 'r'; + params[i].has_arg = required_argument; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 48000U; + strcpy (params[i].short_desc, "Sample rate"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "period"); + params[i].character = 'p'; + params[i].has_arg = required_argument; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 1024U; + strcpy (params[i].short_desc, "Frames per period"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "nperiods"); + params[i].character = 'n'; + params[i].has_arg = required_argument; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 2U; + strcpy (params[i].short_desc, "Number of periods in hardware buffer"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "hwmon"); + params[i].character = 'H'; + params[i].has_arg = no_argument; + params[i].type = JackDriverParamBool; + params[i].value.i = 0; + strcpy (params[i].short_desc,"Hardware monitoring, if available"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "hwmeter"); + params[i].character = 'M'; + params[i].has_arg = no_argument; + params[i].type = JackDriverParamBool; + params[i].value.i = 0; + strcpy (params[i].short_desc, "Hardware metering, if available"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "duplex"); + params[i].character = 'D'; + params[i].has_arg = no_argument; + params[i].type = JackDriverParamBool; + params[i].value.i = 1; + strcpy (params[i].short_desc, + "Provide both capture and playback ports"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "softmode"); + params[i].character = 's'; + params[i].has_arg = no_argument; + params[i].type = JackDriverParamBool; + params[i].value.i = 0; + strcpy (params[i].short_desc, "Soft-mode, no xrun handling"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "monitor"); + params[i].character = 'm'; + params[i].has_arg = no_argument; + params[i].type = JackDriverParamBool; + params[i].value.i = 0; + strcpy (params[i].short_desc, "Provide monitor ports for the output"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "dither"); + params[i].character = 'z'; + params[i].has_arg = optional_argument; + params[i].type = JackDriverParamChar; + params[i].value.c = 'n'; + strcpy (params[i].short_desc, "Dithering mode"); + strcpy (params[i].long_desc, + "Dithering mode:\n" + " n - none\n" + " r - rectangular\n" + " s - shaped\n" + " t - triangular"); + + i++; + strcpy (params[i].name, "inchannels"); + params[i].character = 'i'; + params[i].has_arg = required_argument; + params[i].type = JackDriverParamUInt; + params[i].value.i = 0; + strcpy (params[i].short_desc, + "Number of capture channels (defaults to hardware max)"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "outchannels"); + params[i].character = 'o'; + params[i].has_arg = required_argument; + params[i].type = JackDriverParamUInt; + params[i].value.i = 0; + strcpy (params[i].short_desc, + "Number of playback channels (defaults to hardware max)"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "shorts"); + params[i].character = 'S'; + params[i].has_arg = no_argument; + params[i].type = JackDriverParamBool; + params[i].value.i = 0; + strcpy (params[i].short_desc, "Try 16-bit samples before 32-bit"); + strcpy (params[i].long_desc, params[i].short_desc); + + desc->params = params; + + return desc; +} + jack_driver_t * -driver_initialize (jack_client_t *client, int argc, char **argv) +driver_initialize (jack_client_t *client, const JSList * params) { - jack_nframes_t srate = 48000; + jack_nframes_t srate = 48000; jack_nframes_t frames_per_interrupt = 1024; unsigned long user_nperiods = 2; - char *playback_pcm_name = "default"; - char *capture_pcm_name = "default"; + char *playback_pcm_name = "hw:0"; + char *capture_pcm_name = "hw:0"; int hw_monitoring = FALSE; int hw_metering = FALSE; int capture = FALSE; @@ -1892,26 +2276,12 @@ driver_initialize (jack_client_t *client, int argc, char **argv) int soft_mode = FALSE; int monitor = FALSE; DitherAlgorithm dither = None; - int opt; + int user_capture_nchnls = 0; + int user_playback_nchnls = 0; + int shorts_first = FALSE; char *envvar; - char optstring[2]; /* string made from opt char */ - struct option long_options[] = - { - { "capture", 2, NULL, 'C' }, - { "duplex", 0, NULL, 'D' }, - { "device", 1, NULL, 'd' }, - { "hwmon", 0, NULL, 'H' }, - { "hwmeter", 0, NULL, 'M' }, - { "help", 0, NULL, 'h' }, - { "playback", 2, NULL, 'P' }, - { "period", 1, NULL, 'p' }, - { "rate", 1, NULL, 'r' }, - { "nperiods", 1, NULL, 'n' }, - { "softmode", 0, NULL, 's' }, - { "dither", 2, NULL, 'z' }, - { "monitor", 0, NULL, 'm' }, - { 0, 0, 0, 0 } - }; + const JSList * node; + const jack_driver_param_t * param; /* before we do anything else, see if there are environment variables for each parameter. @@ -1956,27 +2326,40 @@ driver_initialize (jack_client_t *client, int argc, char **argv) playback = atoi (envvar); } + if ((envvar = getenv ("JACK_ALSA_CAPTURE_NCHANNELS")) != NULL) { + monitor = atoi (envvar); + } + + if ((envvar = getenv ("JACK_ALSA_PLAYBACK_NCHANNELS")) != NULL) { + monitor = atoi (envvar); + } + if ((envvar = getenv ("JACK_ALSA_MONITOR")) != NULL) { monitor = atoi (envvar); } - - /* - * Setting optind back to zero is a hack to reinitialize a new - * getopts() loop. See declaration in . - */ - optind = 0; - opterr = 0; + if ((envvar = getenv ("JACK_ALSA_SHORTS_FIRST")) != NULL) { + shorts_first = TRUE; + } + - while ((opt = getopt_long(argc, argv, "-C::Dd:HMP::p:r:n:msz::", - long_options, NULL)) - != EOF) { - switch (opt) { + + for (node = params; node; node = jack_slist_next (node)) { + param = (const jack_driver_param_t *) node->data; + + switch (param->character) { case 'C': capture = TRUE; - if (optarg != NULL) { - capture_pcm_name = optarg; + if (strcmp (param->value.str, "") != 0) { + capture_pcm_name = strdup (param->value.str); + } + break; + + case 'P': + playback = TRUE; + if (strcmp (param->value.str, "") != 0) { + playback_pcm_name = strdup (param->value.str); } break; @@ -1986,70 +2369,57 @@ driver_initialize (jack_client_t *client, int argc, char **argv) break; case 'd': - playback_pcm_name = optarg; - capture_pcm_name = optarg; + playback_pcm_name = strdup (param->value.str); + capture_pcm_name = strdup (param->value.str); break; case 'H': hw_monitoring = TRUE; break; - case 'h': - alsa_usage(); - return NULL; - case 'm': - monitor = TRUE; + monitor = param->value.i; break; case 'M': - hw_metering = TRUE; - break; - - case 'P': - playback = TRUE; - if (optarg != NULL) { - playback_pcm_name = optarg; - } + hw_metering = param->value.i; break; - case 'p': - frames_per_interrupt = atoi(optarg); - break; - case 'r': - srate = atoi(optarg); + srate = param->value.ui; + break; + + case 'p': + frames_per_interrupt = param->value.ui; break; case 'n': - user_nperiods = atoi(optarg); + user_nperiods = param->value.ui; break; case 's': - soft_mode = TRUE; + soft_mode = param->value.i; break; case 'z': - if (optarg == NULL) { - dither = None; - } else { - if (dither_opt (*optarg, &dither)) { - return NULL; - } + if (dither_opt (param->value.c, &dither)) { + return NULL; } break; - /* the rest is error handling: */ - case 1: /* not an option */ - alsa_error("parameter", optarg); - return NULL; - - default: /* unrecognized option */ - optstring[0] = (char) optopt; - optstring[1] = '\0'; - alsa_error("option", optstring); - return NULL; + case 'i': + user_capture_nchnls = param->value.ui; + break; + case 'o': + user_playback_nchnls = param->value.ui; + break; + + case 'S': + shorts_first = param->value.i; + break; + } + } /* duplex is the default */ @@ -2058,9 +2428,14 @@ driver_initialize (jack_client_t *client, int argc, char **argv) playback = TRUE; } - return alsa_driver_new ("alsa_pcm", playback_pcm_name, capture_pcm_name, client, frames_per_interrupt, - user_nperiods, srate, hw_monitoring, hw_metering, capture, - playback, dither, soft_mode, monitor); + return alsa_driver_new ("alsa_pcm", playback_pcm_name, + capture_pcm_name, client, + frames_per_interrupt, + user_nperiods, srate, hw_monitoring, + hw_metering, capture, playback, dither, + soft_mode, monitor, + user_capture_nchnls, user_playback_nchnls, + shorts_first); } void @@ -2068,4 +2443,3 @@ driver_finish (jack_driver_t *driver) { alsa_driver_delete ((alsa_driver_t *) driver); } - diff --git a/drivers/alsa/alsa_driver.h b/drivers/alsa/alsa_driver.h index 9f44951..8746f1a 100644 --- a/drivers/alsa/alsa_driver.h +++ b/drivers/alsa/alsa_driver.h @@ -43,7 +43,7 @@ typedef void (*CopyCopyFunction) (char *dst, char *src, typedef struct { - JACK_DRIVER_DECL + JACK_DRIVER_NT_DECL int poll_timeout; jack_time_t poll_last; @@ -92,7 +92,6 @@ typedef struct { snd_pcm_sw_params_t *capture_sw_params; jack_hardware_t *hw; ClockSyncStatus *clock_sync_data; - struct _jack_engine *engine; jack_client_t *client; JSList *capture_ports; JSList *playback_ports; @@ -123,8 +122,12 @@ typedef struct { char has_hw_monitoring : 1; char has_hw_metering : 1; + int running; + int run; + int xrun_count; int process_count; + } alsa_driver_t; static __inline__ void alsa_driver_mark_channel_done (alsa_driver_t *driver, channel_t chn) { diff --git a/drivers/dummy/dummy_driver.c b/drivers/dummy/dummy_driver.c index d3a8def..d3468ee 100644 --- a/drivers/dummy/dummy_driver.c +++ b/drivers/dummy/dummy_driver.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "linux"; -*- */ /* Copyright (C) 2003 Robert Ham Copyright (C) 2001 Paul Davis @@ -27,6 +28,7 @@ #include #include #include +#include #include #include @@ -38,162 +40,176 @@ #undef DEBUG_WAKEUP -static int -dummy_driver_audio_start (dummy_driver_t *driver) -{ - return 0; -} - -static int -dummy_driver_audio_stop (dummy_driver_t *driver) -{ - return 0; -} - static jack_nframes_t dummy_driver_wait (dummy_driver_t *driver, int extra_fd, int *status, float *delayed_usecs) { - jack_time_t starting_time = jack_get_microseconds(); - jack_time_t processing_time = (driver->last_wait_ust? - starting_time - driver->last_wait_ust: 0); - - /* wait until time for next cycle */ - if (driver->wait_time > processing_time) - usleep (driver->wait_time - processing_time); - - driver->last_wait_ust = jack_get_microseconds (); - driver->engine->transport_cycle_start (driver->engine, driver->last_wait_ust); - - /* this driver doesn't work so well if we report a delay */ - *delayed_usecs = 0; /* lie about it */ - *status = 0; - - return driver->period_size; + jack_time_t starting_time = jack_get_microseconds(); + jack_time_t processing_time = (driver->last_wait_ust? + starting_time - driver->last_wait_ust: + 0); + + /* wait until time for next cycle */ + if (driver->wait_time > processing_time) + usleep (driver->wait_time - processing_time); + + driver->last_wait_ust = jack_get_microseconds (); + driver->engine->transport_cycle_start (driver->engine, + driver->last_wait_ust); + + /* this driver doesn't work so well if we report a delay */ + *delayed_usecs = 0; /* lie about it */ + *status = 0; + return driver->period_size; } -static int -dummy_driver_null_cycle (dummy_driver_t* driver, jack_nframes_t nframes) +static inline int +dummy_driver_run_cycle (dummy_driver_t *driver) { - return 0; + jack_engine_t *engine = driver->engine; + int wait_status; + float delayed_usecs; + + jack_nframes_t nframes = dummy_driver_wait (driver, -1, &wait_status, + &delayed_usecs); + if (nframes == 0) { + /* we detected an xrun and restarted: notify + * clients about the delay. */ + engine->delay (engine); + return 0; + } + + if (wait_status == 0) + return engine->run_cycle (engine, nframes, delayed_usecs); + + if (wait_status < 0) + return -1; + else + return 0; } static int -dummy_driver_bufsize (dummy_driver_t* driver, jack_nframes_t nframes) +dummy_driver_null_cycle (dummy_driver_t* driver, jack_nframes_t nframes) { - int rc; - - /* This is a somewhat arbitrary size restriction. The dummy driver - * doesn't work well with smaller buffer sizes, apparantly due to - * usleep() inaccuracy under Linux 2.4. If you can get it working - * with smaller buffers, lower the limit. (JOQ) */ - if (nframes < 128) - return EINVAL; - - /* no need to stop and start the dummy driver */ - driver->period_size = nframes; - driver->period_usecs = driver->wait_time = - (jack_time_t) floor ((((float) nframes) / driver->sample_rate) - * 1000000.0f); - - /* ask the engine to change its buffer size */ - driver->engine->set_buffer_size (driver->engine, nframes); - - return rc; + return 0; } static int -dummy_driver_read (dummy_driver_t *driver, jack_nframes_t nframes) +dummy_driver_bufsize (dummy_driver_t* driver, jack_nframes_t nframes) { - return 0; + /* This is a somewhat arbitrary size restriction. The dummy + * driver doesn't work well with smaller buffer sizes, + * apparantly due to usleep() inaccuracy under Linux 2.4. If + * you can get it working with smaller buffers, lower the + * limit. (JOQ) */ + if (nframes < 128) + return EINVAL; + + driver->period_size = nframes; + driver->period_usecs = driver->wait_time = + (jack_time_t) floor ((((float) nframes) / driver->sample_rate) + * 1000000.0f); + + /* tell the engine to change its buffer size */ + driver->engine->set_buffer_size (driver->engine, nframes); + + return 0; } static int dummy_driver_write (dummy_driver_t* driver, jack_nframes_t nframes) { - return 0; + return 0; } static int -dummy_driver_attach (dummy_driver_t *driver, jack_engine_t *engine) +dummy_driver_attach (dummy_driver_t *driver) { - jack_port_t * port; - char buf[32]; - unsigned int chn; - int port_flags; - - driver->engine = engine; + jack_port_t * port; + char buf[32]; + unsigned int chn; + int port_flags; - driver->engine->set_buffer_size (engine, driver->period_size); - driver->engine->set_sample_rate (engine, driver->sample_rate); + driver->engine->set_buffer_size (driver->engine, driver->period_size); + driver->engine->set_sample_rate (driver->engine, driver->sample_rate); - port_flags = JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal; + port_flags = JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal; - for (chn = 0; chn < driver->capture_channels; chn++) - { - snprintf (buf, sizeof(buf) - 1, "capture_%u", chn+1); - - port = jack_port_register (driver->client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0); - if (!port) + for (chn = 0; chn < driver->capture_channels; chn++) { - jack_error ("DUMMY: cannot register port for %s", buf); - break; - } + snprintf (buf, sizeof(buf) - 1, "capture_%u", chn+1); + + port = jack_port_register (driver->client, buf, + JACK_DEFAULT_AUDIO_TYPE, + port_flags, 0); + if (!port) + { + jack_error ("DUMMY: cannot register port for %s", buf); + break; + } - driver->capture_ports = jack_slist_append (driver->capture_ports, port); - } + driver->capture_ports = + jack_slist_append (driver->capture_ports, port); + } - port_flags = JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal; + port_flags = JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal; - for (chn = 0; chn < driver->playback_channels; chn++) - { - snprintf (buf, sizeof(buf) - 1, "playback_%u", chn+1); + for (chn = 0; chn < driver->playback_channels; chn++) + { + snprintf (buf, sizeof(buf) - 1, "playback_%u", chn+1); - port = jack_port_register (driver->client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0); + port = jack_port_register (driver->client, buf, + JACK_DEFAULT_AUDIO_TYPE, + port_flags, 0); - if (!port) - { - jack_error ("DUMMY: cannot register port for %s", buf); - break; - } + if (!port) + { + jack_error ("DUMMY: cannot register port for %s", buf); + break; + } - driver->playback_ports = jack_slist_append (driver->playback_ports, port); - } + driver->playback_ports = + jack_slist_append (driver->playback_ports, port); + } - jack_activate (driver->client); + jack_activate (driver->client); - return 0; + return 0; } -static void -dummy_driver_detach (dummy_driver_t *driver, jack_engine_t *engine) +static int +dummy_driver_detach (dummy_driver_t *driver) { - JSList * node; + JSList * node; - if (driver->engine == 0) - return; + if (driver->engine == 0) + return 0; - for (node = driver->capture_ports; node; node = jack_slist_next (node)) - jack_port_unregister (driver->client, ((jack_port_t *) node->data)); + for (node = driver->capture_ports; node; node = jack_slist_next (node)) + jack_port_unregister (driver->client, + ((jack_port_t *) node->data)); - jack_slist_free (driver->capture_ports); - driver->capture_ports = NULL; + jack_slist_free (driver->capture_ports); + driver->capture_ports = NULL; - for (node = driver->playback_ports; node; node = jack_slist_next (node)) - jack_port_unregister (driver->client, ((jack_port_t *) node->data)); + for (node = driver->playback_ports; node; node = jack_slist_next (node)) + jack_port_unregister (driver->client, + ((jack_port_t *) node->data)); - jack_slist_free (driver->playback_ports); - driver->playback_ports = NULL; - - driver->engine = NULL; + jack_slist_free (driver->playback_ports); + driver->playback_ports = NULL; + + return 0; } + static void dummy_driver_delete (dummy_driver_t *driver) { - free (driver); + jack_driver_nt_finish ((jack_driver_nt_t *) driver); + free (driver); } static jack_driver_t * @@ -205,73 +221,113 @@ dummy_driver_new (jack_client_t * client, jack_nframes_t period_size, unsigned long wait_time) { - dummy_driver_t * driver; - - printf ("creating dummy driver ... %s|%" PRIu32 "|%" PRIu32 - "|%lu|%u|%u\n", name, sample_rate, period_size, wait_time, - capture_ports, playback_ports); - - driver = (dummy_driver_t *) calloc (1, sizeof (dummy_driver_t)); - - jack_driver_init ((jack_driver_t *) driver); - - driver->attach = (JackDriverAttachFunction) dummy_driver_attach; - driver->detach = (JackDriverDetachFunction) dummy_driver_detach; - driver->wait = (JackDriverWaitFunction) dummy_driver_wait; - driver->read = (JackDriverReadFunction) dummy_driver_read; - driver->write = (JackDriverReadFunction) dummy_driver_write; - driver->null_cycle = (JackDriverNullCycleFunction) dummy_driver_null_cycle; - driver->bufsize = (JackDriverBufSizeFunction) dummy_driver_bufsize; - driver->start = (JackDriverStartFunction) dummy_driver_audio_start; - driver->stop = (JackDriverStopFunction) dummy_driver_audio_stop; - - driver->period_usecs = - (jack_time_t) floor ((((float) period_size) / sample_rate) * 1000000.0f); - driver->sample_rate = sample_rate; - driver->period_size = period_size; - driver->wait_time = wait_time; - driver->last_wait_ust = 0; - - driver->capture_channels = capture_ports; - driver->capture_ports = NULL; - driver->playback_channels = playback_ports; - driver->playback_ports = NULL; - - driver->client = client; - driver->engine = NULL; - - return (jack_driver_t *) driver; -} + dummy_driver_t * driver; -static void -dummy_usage () -{ - fprintf (stderr, "\n" -"dummy driver arguments:\n" -" -h,--help \tprint this message\n" -" -r,--rate \tsample rate (default: 48000)\n" -" -p,--period \tframes per period (default: 1024)\n" -" -C,--capture \tnumber of capture ports (default: 2)\n" -" -P,--playback \tnumber of playback ports (default: 2)\n" -" -w,--wait \tnumber of usecs to wait between engine processes (default: 21333)\n" -"\n"); -} + printf ("creating dummy driver ... %s|%" PRIu32 "|%" PRIu32 + "|%lu|%u|%u\n", name, sample_rate, period_size, wait_time, + capture_ports, playback_ports); -static void -dummy_error (char *type, char *value) -{ - fprintf (stderr, "dummy driver: unknown %s: `%s'\n", type, value); - dummy_usage(); -} + driver = (dummy_driver_t *) calloc (1, sizeof (dummy_driver_t)); + + jack_driver_nt_init ((jack_driver_nt_t *) driver); + + driver->write = (JackDriverReadFunction) dummy_driver_write; + driver->null_cycle = (JackDriverNullCycleFunction) dummy_driver_null_cycle; + driver->nt_attach = (JackDriverNTAttachFunction) dummy_driver_attach; + driver->nt_detach = (JackDriverNTDetachFunction) dummy_driver_detach; + driver->nt_bufsize = (JackDriverNTBufSizeFunction) dummy_driver_bufsize; + driver->nt_run_cycle = (JackDriverNTRunCycleFunction) dummy_driver_run_cycle; + + driver->period_usecs = + (jack_time_t) floor ((((float) period_size) / sample_rate) + * 1000000.0f); + driver->sample_rate = sample_rate; + driver->period_size = period_size; + driver->wait_time = wait_time; + driver->last_wait_ust = 0; + + driver->capture_channels = capture_ports; + driver->capture_ports = NULL; + driver->playback_channels = playback_ports; + driver->playback_ports = NULL; + + driver->client = client; + driver->engine = NULL; + return (jack_driver_t *) driver; +} /* DRIVER "PLUGIN" INTERFACE */ +jack_driver_desc_t * +driver_get_descriptor () +{ + jack_driver_desc_t * desc; + jack_driver_param_desc_t * params; + unsigned int i; + + desc = calloc (1, sizeof (jack_driver_desc_t)); + strcpy (desc->name, "dummy"); + desc->nparams = 5; + + params = calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); + + i = 0; + strcpy (params[i].name, "capture"); + params[i].character = 'C'; + params[i].has_arg = required_argument; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 2U; + strcpy (params[i].short_desc, "Number of capture ports"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "playback"); + params[i].character = 'P'; + params[i].has_arg = required_argument; + params[i].type = JackDriverParamUInt; + params[1].value.ui = 2U; + strcpy (params[i].short_desc, "Number of playback ports"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "rate"); + params[i].character = 'r'; + params[i].has_arg = required_argument; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 48000U; + strcpy (params[i].short_desc, "Sample rate"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "period"); + params[i].character = 'p'; + params[i].has_arg = required_argument; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 1024U; + strcpy (params[i].short_desc, "Frames per period"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "wait"); + params[i].character = 'w'; + params[i].has_arg = required_argument; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 21333U; + strcpy (params[i].short_desc, + "Number of usecs to wait between engine processes"); + strcpy (params[i].long_desc, params[i].short_desc); + + desc->params = params; + + return desc; +} + const char driver_client_name[] = "dummy_pcm"; jack_driver_t * -driver_initialize (jack_client_t *client, int argc, char **argv) +driver_initialize (jack_client_t *client, const JSList * params) { jack_nframes_t sample_rate = 48000; jack_nframes_t period_size = 1024; @@ -279,69 +335,35 @@ driver_initialize (jack_client_t *client, int argc, char **argv) unsigned int playback_ports = 2; int wait_time_set = 0; unsigned long wait_time; + const JSList * node; + const jack_driver_param_t * param; - int opt; - char optstring[2]; /* string made from opt char */ - struct option long_options[] = - { - { "help", no_argument, NULL, 'h' }, - { "rate", required_argument, NULL, 'r' }, - { "period", required_argument, NULL, 'p' }, - { "capture", required_argument, NULL, 'C' }, - { "playback", required_argument, NULL, 'P' }, - { "wait", required_argument, NULL, 'w' }, - { 0, 0, 0, 0 } - }; + for (node = params; node; node = jack_slist_next (node)) { + param = (const jack_driver_param_t *) node->data; - - /* - * Setting optind back to zero is a hack to reinitialize a new - * getopts() loop. See declaration in . - */ - - optind = 0; - opterr = 0; - - while ((opt = getopt_long(argc, argv, "C::P::p:r:w:h", - long_options, NULL)) - != EOF) { - switch (opt) { + switch (param->character) { case 'C': - capture_ports = atoi (optarg); + capture_ports = param->value.ui; break; case 'P': - playback_ports = atoi (optarg); + playback_ports = param->value.ui; break; - case 'p': - period_size = atoi(optarg); - break; - case 'r': - sample_rate = atoi(optarg); + sample_rate = param->value.ui; break; - + + case 'p': + period_size = param->value.ui; + break; + case 'w': - wait_time = strtoul(optarg, NULL, 10); + wait_time = param->value.ui; wait_time_set = 1; break; - case 'h': - dummy_usage(); - return NULL; - - /* the rest is error handling: */ - case 1: /* not an option */ - dummy_error("parameter", optarg); - return NULL; - - default: /* unrecognized option */ - optstring[0] = (char) optopt; - optstring[1] = '\0'; - dummy_error("option", optstring); - return NULL; } } diff --git a/drivers/dummy/dummy_driver.h b/drivers/dummy/dummy_driver.h index 827a233..57f6e48 100644 --- a/drivers/dummy/dummy_driver.h +++ b/drivers/dummy/dummy_driver.h @@ -31,22 +31,19 @@ typedef struct _dummy_driver dummy_driver_t; struct _dummy_driver { + JACK_DRIVER_NT_DECL - JACK_DRIVER_DECL + jack_nframes_t sample_rate; + jack_nframes_t period_size; + unsigned long wait_time; - jack_nframes_t sample_rate; - jack_nframes_t period_size; - unsigned long wait_time; + unsigned int capture_channels; + unsigned int playback_channels; - unsigned int capture_channels; - unsigned int playback_channels; + JSList *capture_ports; + JSList *playback_ports; - JSList * capture_ports; - JSList * playback_ports; - - struct _jack_engine * engine; - - jack_client_t * client; + jack_client_t *client; }; #endif /* __JACK_DUMMY_DRIVER_H__ */ diff --git a/drivers/portaudio/portaudio_driver.c b/drivers/portaudio/portaudio_driver.c index c2f6aab..8c303ee 100644 --- a/drivers/portaudio/portaudio_driver.c +++ b/drivers/portaudio/portaudio_driver.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "bsd"; -*- */ /* Copyright © Grame 2003 @@ -23,6 +24,7 @@ #include #include #include +#include #include #include "portaudio_driver.h" @@ -143,12 +145,6 @@ portaudio_driver_null_cycle (portaudio_driver_t* driver, jack_nframes_t nframes) return 0; } -static int -portaudio_driver_bufsize (portaudio_driver_t* driver, jack_nframes_t nframes) -{ - return ENOSYS; /* function not implemented */ -} - static int portaudio_driver_read (portaudio_driver_t *driver, jack_nframes_t nframes) { @@ -219,6 +215,99 @@ portaudio_driver_audio_stop (portaudio_driver_t *driver) return (err != paNoError) ? -1 : 0; } +static int +portaudio_driver_set_parameters (portaudio_driver_t* driver, + jack_nframes_t nframes, + jack_nframes_t rate) +{ + int capturing = driver->capturing; + int playing = driver->playing; + + int err = Pa_OpenStream( + &driver->stream, + ((capturing) ? Pa_GetDefaultInputDeviceID() : paNoDevice), + ((capturing) ? driver->capture_nchannels : 0), + paFloat32, /* 32-bit float input */ + NULL, + ((playing) ? Pa_GetDefaultOutputDeviceID() : paNoDevice), + ((playing) ? driver->playback_nchannels : 0), + paFloat32, /* 32-bit float output */ + NULL, + rate, /* sample rate */ + nframes, /* frames per buffer */ + 0, /* number of buffers = default min */ + paClipOff, /* we won't output out of + * range samples so don't + * bother clipping them */ + paCallback, + driver); + + if (err == paNoError) { + + driver->period_usecs = (((float) driver->frames_per_cycle) + / driver->frame_rate) * 1000000.0f; + driver->frame_rate = rate; + driver->frames_per_cycle = nframes; + + /* tell engine about buffer size */ + if (driver->engine) { + driver->engine->set_buffer_size ( + driver->engine, driver->frames_per_cycle); + } + return 0; + + } else { + + // JOQ: this driver is dead. How do we terminate it? + // Pa_Terminate(); + fprintf(stderr, "Unable to set portaudio parameters\n"); + fprintf(stderr, "Error number: %d\n", err); + fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(err)); + return EIO; + } +} + +static int +portaudio_driver_reset_parameters (portaudio_driver_t* driver, + jack_nframes_t nframes, + jack_nframes_t rate) +{ + if (!jack_power_of_two(nframes)) { + printf("PA: frames must be a power of two " + "(64, 512, 1024, ...)\n"); + return EINVAL; + } + + Pa_CloseStream(driver->stream); + + return portaudio_driver_set_parameters (driver, nframes, rate); +} + +static int +portaudio_driver_bufsize (portaudio_driver_t* driver, jack_nframes_t nframes) +{ + int rc; + + /* This gets called from the engine server thread, so it must + * be serialized with the driver thread. Stopping the audio + * also stops that thread. */ + + if (portaudio_driver_audio_stop (driver)) { + jack_error ("PA: cannot stop to set buffer size"); + return EIO; + } + + rc = portaudio_driver_reset_parameters (driver, nframes, + driver->frame_rate); + + if (portaudio_driver_audio_start (driver)) { + jack_error ("PA: cannot restart after setting buffer size"); + rc = EIO; + } + + return rc; +} + //== instance creation/destruction ============================================= /** create a new driver instance @@ -245,19 +334,19 @@ portaudio_driver_new (char *name, driver = (portaudio_driver_t *) calloc (1, sizeof (portaudio_driver_t)); jack_driver_init ((jack_driver_t *) driver); - - driver->frame_rate = rate; if (!jack_power_of_two(frames_per_cycle)) { - printf("JACK: frames must be a power of two (64, 512, 1024, ...)\n"); + printf("PA: -p must be a power of two.\n"); goto error; } driver->frames_per_cycle = frames_per_cycle; + driver->frame_rate = rate; + driver->capturing = capturing; + driver->playing = playing; driver->attach = (JackDriverAttachFunction) portaudio_driver_attach; driver->detach = (JackDriverDetachFunction) portaudio_driver_detach; - driver->wait = (JackDriverWaitFunction) portaudio_driver_wait; driver->read = (JackDriverReadFunction) portaudio_driver_read; driver->write = (JackDriverReadFunction) portaudio_driver_write; driver->null_cycle = (JackDriverNullCycleFunction) portaudio_driver_null_cycle; @@ -320,7 +409,8 @@ portaudio_driver_new (char *name, driver->capture_nchannels = (driver->capture_nchannels < chan) ? driver->capture_nchannels : chan; driver->playback_nchannels = (driver->playback_nchannels < chan) ? driver->playback_nchannels : chan; } - + + // JOQ: should use portaudio_driver_set_parameters(), instead err = Pa_OpenStream(&driver->stream, ((capturing) ? Pa_GetDefaultInputDeviceID() : paNoDevice), ((capturing) ? driver->capture_nchannels : 0), @@ -371,107 +461,166 @@ portaudio_driver_delete (portaudio_driver_t *driver) /* DRIVER "PLUGIN" INTERFACE */ -const char driver_client_name[] = "portaudio"; - -static void -portaudio_usage () +jack_driver_desc_t * +driver_get_descriptor () { - fprintf (stderr, "\n" - - "portaudio PCM driver args:\n" - " -r sample-rate (default: 44.1kHz)\n" - " -c chan (default: harware)\n" - " -p frames-per-period (default: 128)\n" - " -D (duplex, default: yes)\n" - " -C (capture, default: duplex)\n" - " -P (playback, default: duplex)\n" - " -z[r|t|s|-] (dither, rect|tri|shaped|off, default: off)\n" - ); + jack_driver_desc_t * desc; + unsigned int i; + desc = calloc (1, sizeof (jack_driver_desc_t)); + + strcpy (desc->name, "portaudio"); + desc->nparams = 7; + desc->params = calloc (desc->nparams, + sizeof (jack_driver_param_desc_t)); + + i = 0; + strcpy (desc->params[i].name, "channel"); + desc->params[i].character = 'c'; + desc->params[i].has_arg = required_argument; + desc->params[i].type = JackDriverParamInt; + desc->params[i].value.ui = 0; + strcpy (desc->params[i].short_desc, "Maximium number of channels"); + strcpy (desc->params[i].long_desc, desc->params[i].short_desc); + + i++; + strcpy (desc->params[i].name, "capture"); + desc->params[i].character = 'C'; + desc->params[i].has_arg = no_argument; + desc->params[i].type = JackDriverParamBool; + desc->params[i].value.i = TRUE; + strcpy (desc->params[i].short_desc, "Whether or not to capture"); + strcpy (desc->params[i].long_desc, desc->params[i].short_desc); + + i++; + strcpy (desc->params[i].name, "playback"); + desc->params[i].character = 'P'; + desc->params[i].has_arg = no_argument; + desc->params[i].type = JackDriverParamBool; + desc->params[i].value.i = TRUE; + strcpy (desc->params[i].short_desc, "Whether or not to playback"); + strcpy (desc->params[i].long_desc, desc->params[i].short_desc); + + i++; + strcpy (desc->params[i].name, "duplex"); + desc->params[i].character = 'D'; + desc->params[i].has_arg = no_argument; + desc->params[i].type = JackDriverParamBool; + desc->params[i].value.i = TRUE; + strcpy (desc->params[i].short_desc, "Capture and playback"); + strcpy (desc->params[i].long_desc, desc->params[i].short_desc); + + i++; + strcpy (desc->params[i].name, "rate"); + desc->params[i].character = 'r'; + desc->params[i].has_arg = required_argument; + desc->params[i].type = JackDriverParamUInt; + desc->params[i].value.ui = 48000U; + strcpy (desc->params[i].short_desc, "Sample rate"); + strcpy (desc->params[i].long_desc, desc->params[i].short_desc); + + i++; + strcpy (desc->params[i].name, "period"); + desc->params[i].character = 'p'; + desc->params[i].has_arg = required_argument; + desc->params[i].type = JackDriverParamUInt; + desc->params[i].value.ui = 128U; + strcpy (desc->params[i].short_desc, "Frames per period"); + strcpy (desc->params[i].long_desc, desc->params[i].short_desc); + + i++; + strcpy (desc->params[i].name, "dither"); + desc->params[i].character = 'z'; + desc->params[i].has_arg = optional_argument; + desc->params[i].type = JackDriverParamChar; + desc->params[i].value.c = '-'; + strcpy (desc->params[i].short_desc, "Dithering mode"); + strcpy (desc->params[i].long_desc, + " Dithering Mode:\n" + " r : rectangular\n" + " t : triangular\n" + " s : shaped\n" + " - : no dithering"); + + + return desc; } +const char driver_client_name[] = "portaudio"; + jack_driver_t * -driver_initialize (jack_client_t *client, int argc, char **argv) +driver_initialize (jack_client_t *client, const JSList * params) { - jack_nframes_t srate = 44100; - jack_nframes_t frames_per_interrupt = 128; + jack_nframes_t srate = 48000; + jack_nframes_t frames_per_interrupt = 1024; int capture = FALSE; int playback = FALSE; int chan = -1; DitherAlgorithm dither = None; - int i; + const JSList * node; + const jack_driver_param_t * param; - for (i = 1; i < argc; i++) { - if (argv[i][0] == '-') { - - switch (argv[i][1]) { + + for (node = params; node; node = jack_slist_next (node)) { + param = (const jack_driver_param_t *) node->data; + + switch (param->character) { - case 'D': - capture = TRUE; - playback = TRUE; - break; + case 'D': + capture = TRUE; + playback = TRUE; + break; - case 'c': - chan = atoi (argv[i+1]); - i++; - break; + case 'c': + chan = (int) param->value.ui; + break; - case 'C': - capture = TRUE; - break; + case 'C': + capture = param->value.i; + break; - case 'P': - playback = TRUE; - break; - - case 'r': - srate = atoi (argv[i+1]); - i++; - break; + case 'P': + playback = param->value.i; + break; + + case 'r': + srate = param->value.ui; + break; - case 'p': - frames_per_interrupt = atoi (argv[i+1]); - i++; - break; + case 'p': + frames_per_interrupt = (unsigned int) param->value.ui; + break; - case 'z': - switch (argv[i][2]) { - case '-': - dither = None; - break; + case 'z': + switch ((int) param->value.c) { + case '-': + dither = None; + break; - case 'r': - dither = Rectangular; - break; + case 'r': + dither = Rectangular; + break; - case 's': - dither = Shaped; - break; + case 's': + dither = Shaped; + break; - case 't': - default: - dither = Triangular; - break; - } - break; - - default: - portaudio_usage (); - return NULL; + case 't': + default: + dither = Triangular; + break; } - } else { - portaudio_usage (); - return NULL; + break; } } - /* duplex is the default */ - + /* duplex is the default */ if (!capture && !playback) { capture = TRUE; playback = TRUE; } - return portaudio_driver_new ("portaudio", client, frames_per_interrupt, srate, capture, playback, chan, dither); + return portaudio_driver_new ("portaudio", client, frames_per_interrupt, + srate, capture, playback, chan, dither); } void diff --git a/drivers/portaudio/portaudio_driver.h b/drivers/portaudio/portaudio_driver.h index 5f19bac..99e24ad 100644 --- a/drivers/portaudio/portaudio_driver.h +++ b/drivers/portaudio/portaudio_driver.h @@ -46,6 +46,8 @@ typedef struct { jack_nframes_t frame_rate; jack_nframes_t frames_per_cycle; unsigned long user_nperiods; + int capturing; + int playing; channel_t playback_nchannels; channel_t capture_nchannels; diff --git a/example-clients/.cvsignore b/example-clients/.cvsignore index a204f88..dba6104 100644 --- a/example-clients/.cvsignore +++ b/example-clients/.cvsignore @@ -5,7 +5,7 @@ Makefile.in jack_cache_killer jack_connect jack_disconnect -jack_fltk_client +jack_freewheel jack_impulse_grabber jack_monitor_client jack_simple_client diff --git a/example-clients/Makefile.am b/example-clients/Makefile.am index 48333d8..f19e525 100644 --- a/example-clients/Makefile.am +++ b/example-clients/Makefile.am @@ -35,6 +35,7 @@ bin_PROGRAMS = jack_load \ jack_showtime \ jack_bufsize \ jack_lsp \ + jack_freewheel \ $(JACKREC) \ $(JACK_TRANSPORT) @@ -78,9 +79,12 @@ jack_bufsize_SOURCES = bufsize.c jack_bufsize_LDFLAGS = jack_bufsize_LDADD = ../libjack/libjack.la +jack_freewheel_SOURCES = freewheel.c +jack_freewheel_LDFLAGS = +jack_freewheel_LDADD = ../libjack/libjack.la if HAVE_SNDFILE -jackrec_SOURCES = capture_client.c ringbuffer.c ringbuffer.h +jackrec_SOURCES = capture_client.c jackrec_LDFLAGS = @SNDFILE_LIBS@ -lrt -ldl -lpthread jackrec_LDADD = ../libjack/libjack.la endif diff --git a/example-clients/capture_client.c b/example-clients/capture_client.c index 2c5813b..775554a 100644 --- a/example-clients/capture_client.c +++ b/example-clients/capture_client.c @@ -31,7 +31,7 @@ #include #include #include -#include "ringbuffer.h" +#include typedef struct _thread_info { pthread_t thread_id; @@ -56,7 +56,7 @@ const size_t sample_size = sizeof(jack_default_audio_sample_t); /* Synchronization between process thread and disk thread. */ #define DEFAULT_RB_SIZE 16384 /* ringbuffer size in frames */ -ringbuffer_t *rb; +jack_ringbuffer_t *rb; pthread_mutex_t disk_thread_lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t data_ready = PTHREAD_COND_INITIALIZER; long overruns = 0; @@ -81,9 +81,9 @@ disk_thread (void *arg) /* Write the data one frame at a time. This is * inefficient, but makes things simpler. */ while (info->can_capture && - (ringbuffer_read_space (rb) >= bytes_per_frame)) { + (jack_ringbuffer_read_space (rb) >= bytes_per_frame)) { - ringbuffer_read (rb, framebuf, bytes_per_frame); + jack_ringbuffer_read (rb, framebuf, bytes_per_frame); if (sf_writef_float (info->sf, framebuf, 1) != 1) { char errstr[256]; @@ -129,7 +129,7 @@ process (jack_nframes_t nframes, void *arg) * just queue interleaved samples to a single ringbuffer. */ for (i = 0; i < nframes; i++) { for (chn = 0; chn < nports; chn++) { - if (ringbuffer_write (rb, (void *) (in[chn]+i), + if (jack_ringbuffer_write (rb, (void *) (in[chn]+i), sample_size) < sample_size) overruns++; @@ -223,7 +223,7 @@ setup_ports (int sources, char *source_names[], thread_info_t *info) ports = (jack_port_t **) malloc (sizeof (jack_port_t *) * nports); in_size = nports * sizeof (jack_default_audio_sample_t *); in = (jack_default_audio_sample_t **) malloc (in_size); - rb = ringbuffer_create (nports * sample_size * info->rb_size); + rb = jack_ringbuffer_create (nports * sample_size * info->rb_size); /* When JACK is running realtime, jack_activate() will have * called mlockall() to lock our pages into memory. But, we @@ -337,7 +337,7 @@ main (int argc, char *argv[]) jack_client_close (client); - ringbuffer_free (rb); + jack_ringbuffer_free (rb); exit (0); } diff --git a/example-clients/freewheel.c b/example-clients/freewheel.c new file mode 100644 index 0000000..4aa6e70 --- /dev/null +++ b/example-clients/freewheel.c @@ -0,0 +1,88 @@ +/* + * freewheel - start/stop JACK "freewheeling" mode + * + * Copyright (C) 2003 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +char *package; /* program name */ +jack_client_t *client; +int onoff; + +void jack_shutdown(void *arg) +{ + fprintf(stderr, "JACK shut down, exiting ...\n"); + exit(1); +} + +void signal_handler(int sig) +{ + jack_client_close(client); + fprintf(stderr, "signal received, exiting ...\n"); + exit(0); +} + +void parse_arguments(int argc, char *argv[]) +{ + if (argc < 2) { + fprintf(stderr, "usage: %s y|n\n", package); + exit(9); + } + + if (argv[1][0] == 'y' || argv[1][0] == 'Y' || argv[1][0] == '1') { + onoff = 1; + } else { + onoff = 0; + } +} + +int +main (int argc, char *argv[]) +{ + int rc; + + parse_arguments (argc, argv); + + /* become a JACK client */ + if ((client = jack_client_new ("freewheel")) == 0) { + fprintf (stderr, "JACK server not running?\n"); + exit(1); + } + + signal (SIGQUIT, signal_handler); + signal (SIGTERM, signal_handler); + signal (SIGHUP, signal_handler); + signal (SIGINT, signal_handler); + + jack_on_shutdown (client, jack_shutdown, 0); + + if (jack_set_freewheel (client, onoff)) { + fprintf (stderr, "failed to reset freewheel mode\n"); + } + + jack_client_close(client); + + return rc; +} diff --git a/example-clients/ringbuffer.h b/example-clients/ringbuffer.h deleted file mode 100644 index acdef99..0000000 --- a/example-clients/ringbuffer.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _RINGBUFFER_H -#define _RINGBUFFER_H - -#include - -typedef struct -{ - char *buf; - size_t len; -} -ringbuffer_data_t ; - -typedef struct -{ - char *buf; - volatile size_t write_ptr; - volatile size_t read_ptr; - size_t size; - size_t size_mask; - int mlocked; -} -ringbuffer_t ; - -ringbuffer_t *ringbuffer_create(int sz); -void ringbuffer_free(ringbuffer_t *rb); - -int ringbuffer_mlock(ringbuffer_t *rb); -void ringbuffer_reset(ringbuffer_t *rb); - -void ringbuffer_write_advance(ringbuffer_t *rb, size_t cnt); -void ringbuffer_read_advance(ringbuffer_t *rb, size_t cnt); - -size_t ringbuffer_write_space(ringbuffer_t *rb); -size_t ringbuffer_read_space(ringbuffer_t *rb); - -size_t ringbuffer_read(ringbuffer_t *rb, char *dest, size_t cnt); -size_t ringbuffer_write(ringbuffer_t *rb, char *src, size_t cnt); - -void ringbuffer_get_read_vector(ringbuffer_t *rb, ringbuffer_data_t *vec); -void ringbuffer_get_write_vector(ringbuffer_t *rb, ringbuffer_data_t *vec); - -#endif diff --git a/example-clients/simple_client.c b/example-clients/simple_client.c index 2bba4f6..0682dd2 100644 --- a/example-clients/simple_client.c +++ b/example-clients/simple_client.c @@ -1,3 +1,9 @@ +/** @file simple_client.c + * + * @brief This is very simple client that demonstrates the basic + * features of JACK as they would be used by many applications. + */ + #include #include #include @@ -9,9 +15,12 @@ jack_port_t *input_port; jack_port_t *output_port; +/** + * The process callback for this JACK application. + * It is called by JACK at the appropriate times. + */ int process (jack_nframes_t nframes, void *arg) - { jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) jack_port_get_buffer (output_port, nframes); jack_default_audio_sample_t *in = (jack_default_audio_sample_t *) jack_port_get_buffer (input_port, nframes); @@ -21,23 +30,15 @@ process (jack_nframes_t nframes, void *arg) return 0; } -int -srate (jack_nframes_t nframes, void *arg) - -{ - printf ("the sample rate is now %" PRIu32 "/sec\n", nframes); - return 0; -} - -void -error (const char *desc) -{ - fprintf (stderr, "JACK error: %s\n", desc); -} - +/** + * This is the shutdown callback for this JACK application. + * It is called by JACK if the server ever shuts down or + * decides to disconnect the client. + */ void jack_shutdown (void *arg) { + exit (1); } @@ -52,15 +53,6 @@ main (int argc, char *argv[]) return 1; } - /* tell the JACK server to call error() whenever it - experiences an error. Notice that this callback is - global to this process, not specific to each client. - - This is set here so that it can catch errors in the - connection process - */ - jack_set_error_function (error); - /* try to become a client of the JACK server */ if ((client = jack_client_new (argv[1])) == 0) { @@ -74,13 +66,6 @@ main (int argc, char *argv[]) jack_set_process_callback (client, process, 0); - /* tell the JACK server to call `srate()' whenever - the sample rate of the system changes. - */ - - - jack_set_sample_rate_callback (client, srate, 0); - /* tell the JACK server to call `jack_shutdown()' if it ever shuts down, either entirely, or if it just decides to stop calling us. @@ -88,10 +73,8 @@ main (int argc, char *argv[]) jack_on_shutdown (client, jack_shutdown, 0); - /* display the current sample rate. once the client is activated - (see below), you should rely on your own sample rate - callback (see above) for this value. - */ + /* display the current sample rate. + */ printf ("engine sample rate: %" PRIu32 "\n", jack_get_sample_rate (client)); @@ -114,7 +97,6 @@ main (int argc, char *argv[]) running. */ - if ((ports = jack_get_ports (client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput)) == NULL) { fprintf(stderr, "Cannot find any physical capture ports\n"); exit(1); diff --git a/jack/Makefile.am b/jack/Makefile.am index bbd36d5..3f8f706 100644 --- a/jack/Makefile.am +++ b/jack/Makefile.am @@ -4,11 +4,12 @@ libjackincludedir = $(includedir)/jack libjackinclude_HEADERS = \ jack.h \ - timestamps.h \ - transport.h \ + ringbuffer.h \ + timestamps.h \ + transport.h \ types.h -noinst_HEADERS = \ +noinst_HEADERS = \ cycles.h \ driver.h \ engine.h \ @@ -20,5 +21,7 @@ noinst_HEADERS = \ shm.h \ start.h \ time.h \ - version.h + version.h \ + driver_interface.h \ + driver_parse.h diff --git a/jack/driver.h b/jack/driver.h index 8061874..9804d1d 100644 --- a/jack/driver.h +++ b/jack/driver.h @@ -24,6 +24,7 @@ #include #include #include +#include typedef float gain_t; typedef long channel_t; @@ -58,24 +59,21 @@ typedef int (*JackDriverNullCycleFunction)(struct _jack_driver *, jack_nframes_t nframes); typedef int (*JackDriverStopFunction)(struct _jack_driver *); typedef int (*JackDriverStartFunction)(struct _jack_driver *); -typedef jack_nframes_t (*JackDriverWaitFunction)(struct _jack_driver *, - int fd, int *status, - float *delayed_usecs); 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 lookup "driver_initialize" and execute it + - we call dlsym for "driver_initialize" and execute it 2) engine attaches to driver 3) engine starts driver - 4) while (1) { - engine->wait (); - engine->audio_cycle (); - } + 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 @@ -84,7 +82,6 @@ typedef int (*JackDriverBufSizeFunction)(struct _jack_driver *, error return from the `wait' function. */ - typedef struct _jack_driver { /* The _jack_driver structure fields are included at the beginning of @@ -98,6 +95,7 @@ typedef struct _jack_driver { 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 @@ -105,11 +103,13 @@ typedef struct _jack_driver { jack_time_t last_wait_ust; + This is not used by the driver. it should not be written to or modified in any way - + void *handle; + 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 @@ -118,6 +118,7 @@ typedef struct _jack_driver { 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 @@ -126,10 +127,12 @@ typedef struct _jack_driver { 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. @@ -151,6 +154,7 @@ typedef struct _jack_driver { 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. @@ -159,6 +163,7 @@ typedef struct _jack_driver { 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. @@ -167,6 +172,7 @@ typedef struct _jack_driver { 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 @@ -174,23 +180,30 @@ typedef struct _jack_driver { 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) + 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) + 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 - first, and the start function afterwards. + prior to this, and the start function after this one has returned. JackDriverBufSizeFunction bufsize; */ @@ -203,7 +216,6 @@ typedef struct _jack_driver { void (*finish)(struct _jack_driver *);\ JackDriverAttachFunction attach; \ JackDriverDetachFunction detach; \ - JackDriverWaitFunction wait; \ JackDriverReadFunction read; \ JackDriverWriteFunction write; \ JackDriverNullCycleFunction null_cycle; \ @@ -215,10 +227,78 @@ typedef struct _jack_driver { } jack_driver_t; + +typedef jack_driver_desc_t * (*JackDriverDescFunction) (); + 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/jack/driver_interface.h b/jack/driver_interface.h new file mode 100644 index 0000000..0dfba53 --- /dev/null +++ b/jack/driver_interface.h @@ -0,0 +1,97 @@ +/* + Copyright (C) 2003 Bob Ham + + 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 __jack_driver_interface_h__ +#define __jack_driver_interface_h__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#define JACK_DRIVER_NAME_MAX 15 +#define JACK_DRIVER_PARAM_NAME_MAX 15 +#define JACK_DRIVER_PARAM_STRING_MAX 31 + + +/** Driver parameter types */ +typedef enum +{ + JackDriverParamInt = 1, + JackDriverParamUInt, + JackDriverParamChar, + JackDriverParamString, + JackDriverParamBool + +} jack_driver_param_type_t; + +/** Driver parameter value */ +typedef union +{ + uint32_t ui; + int32_t i; + char c; + char str[JACK_DRIVER_PARAM_STRING_MAX+1]; +} jack_driver_param_value_t; + + +/** A driver parameter descriptor */ +typedef struct +{ + char name[JACK_DRIVER_NAME_MAX+1]; /**< The parameter's name */ + char character; /**< The parameter's character (for getopt, etc) */ + int32_t has_arg; /**< as with getopt's struct option */ + jack_driver_param_type_t type; /**< The parameter's type */ + jack_driver_param_value_t value; /**< The parameter's (default) value */ + char short_desc[64]; /**< A short (~30 chars) description for the user */ + char long_desc[1024]; /**< A longer description for the user */ + +} jack_driver_param_desc_t; + +/** A driver parameter */ +typedef struct +{ + char character; + jack_driver_param_value_t value; +} jack_driver_param_t; + + +/** A struct for describing a jack driver */ +typedef struct +{ + char name[JACK_DRIVER_NAME_MAX+1]; /**< The driver's canonical name */ + char file[PATH_MAX+1]; /**< The filename of the driver's shared object file */ + uint32_t nparams; /**< The number of parameters the driver has */ + jack_driver_param_desc_t * params; /**< An array of parameter descriptors */ + +} jack_driver_desc_t; + + + + +#ifdef __cplusplus +} +#endif + +#endif /* __jack_driver_interface_h__ */ + + diff --git a/jack/driver_parse.h b/jack/driver_parse.h new file mode 100644 index 0000000..85fd8b1 --- /dev/null +++ b/jack/driver_parse.h @@ -0,0 +1,208 @@ +/* -*- mode: c; c-file-style: "bsd"; -*- */ +/* + Copyright (C) 2003 Bob Ham +#include + +static void +jack_print_driver_options (jack_driver_desc_t * desc, FILE *file) +{ + unsigned long i; + const char * arg_string; + char arg_default[JACK_DRIVER_PARAM_STRING_MAX + 1]; + + for (i = 0; i < desc->nparams; i++) { + switch (desc->params[i].has_arg) { + case no_argument: + arg_string = "\t"; + break; + case optional_argument: + arg_string = " []"; + break; + case required_argument: + arg_string = " "; + break; + } + + switch (desc->params[i].type) { + case JackDriverParamInt: + sprintf (arg_default, "%" PRId32, desc->params[i].value.i); + break; + case JackDriverParamUInt: + sprintf (arg_default, "%" PRIu32, desc->params[i].value.ui); + break; + case JackDriverParamChar: + sprintf (arg_default, "%c", desc->params[i].value.c); + break; + case JackDriverParamString: + if (strcmp (desc->params[i].value.str, "") != 0) + sprintf (arg_default, "%s", desc->params[i].value.str); + else + sprintf (arg_default, "none"); + break; + case JackDriverParamBool: + sprintf (arg_default, "%s", desc->params[i].value.i ? "true" : "false"); + break; + } + + fprintf (file, "\t-%c, --%s%s\t%s (default: %s)\n", + desc->params[i].character, + desc->params[i].name, + arg_string, + desc->params[i].short_desc, + arg_default); + } +} + +static void +jack_print_driver_param_usage (jack_driver_desc_t * desc, unsigned long param, FILE *file) +{ + fprintf (file, "Usage information for the '%s' option for driver '%s':\n", + desc->params[param].name, desc->name); + + fprintf (file, "%s\n", desc->params[param].long_desc); +} + + +static int +jack_parse_driver_params (jack_driver_desc_t * desc, int argc, char **argv, JSList ** param_ptr) +{ + struct option * long_options; + char * options, * options_ptr; + unsigned long i; + int opt, param_index; + JSList * params = NULL; + jack_driver_param_t * driver_param; + + /* check for help */ + if (argc > 1) { + if (strcmp (argv[1], "-h") == 0 || strcmp (argv[1], "--help") == 0) { + if (argc > 2) { + for (i = 0; i < desc->nparams; i++) { + if (strcmp (desc->params[i].name, argv[2]) == 0) { + jack_print_driver_param_usage (desc, i, stdout); + return 1; + } + } + + fprintf (stderr, "jackd: unknown option '%s' for driver '%s'\n", + argv[2], desc->name); + } + + printf ("Options for driver '%s':\n", desc->name); + jack_print_driver_options (desc, stdout); + return 1; + } + } else { + /* save some processing */ + *param_ptr = NULL; + return 0; + } + + + /* set up the stuff for getopt */ + options = calloc (desc->nparams*3 + 1, sizeof (char)); + options_ptr = options; + long_options = calloc (desc->nparams + 1, sizeof (struct option)); + + for (i = 0; i < desc->nparams; i++) { + *options_ptr = desc->params[i].character; + options_ptr++; + if (desc->params[i].has_arg > 0) { + *options_ptr = ':'; + options_ptr++; + + if (desc->params[i].has_arg == optional_argument) { + *options_ptr = ':'; + options_ptr++; + } + } + + long_options[i].name = desc->params[i].name; + long_options[i].has_arg = desc->params[i].has_arg; + long_options[i].flag = NULL; + long_options[i].val = desc->params[i].character; + } + + /* create the params */ + optind = 0; + opterr = 0; + while ((opt = getopt_long(argc, argv, options, long_options, NULL)) != -1) { + + if (opt == ':' || opt == '?') { + if (opt == ':') { + fprintf (stderr, "Missing option to argument '%c'\n", optopt); + } else { + fprintf (stderr, "Unknown option '%c'\n", optopt); + } + + fprintf (stderr, "Options for driver '%s':\n", desc->name); + jack_print_driver_options (desc, stderr); + exit (1); + } + + for (param_index = 0; desc->nparams; param_index++) { + if (opt == desc->params[param_index].character) + break; + } + + driver_param = calloc (1, sizeof (jack_driver_param_t)); + + driver_param->character = desc->params[param_index].character; + + if (optarg) { + switch (desc->params[param_index].type) { + case JackDriverParamInt: + driver_param->value.i = atoi (optarg); + break; + case JackDriverParamUInt: + driver_param->value.ui = strtoul (optarg, NULL, 10); + break; + case JackDriverParamChar: + driver_param->value.c = optarg[0]; + break; + case JackDriverParamString: + strncpy (driver_param->value.str, optarg, JACK_DRIVER_PARAM_STRING_MAX); + break; + case JackDriverParamBool: + driver_param->value.i = 1; + break; + } + } + + params = jack_slist_append (params, driver_param); + } + + free (options); + free (long_options); + + if (param_ptr) + *param_ptr = params; + + return 0; +} + + +#endif /* __jack_driver_parse_h__ */ + + diff --git a/jack/engine.h b/jack/engine.h index 433803d..adb25eb 100644 --- a/jack/engine.h +++ b/jack/engine.h @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "bsd"; -*- */ /* Copyright (C) 2001-2003 Paul Davis @@ -23,6 +24,7 @@ #include #include +#include #define VERBOSE(engine,format,args...) \ if ((engine)->verbose) fprintf (stderr, format, ## args) @@ -32,10 +34,11 @@ struct _jack_client_internal; struct _jack_port_internal; /* Structures is allocated by the engine in local memory to keep track - * of port buffers and connections. */ + * of port buffers and connections. + */ typedef struct { - const char *shm_name; - jack_shmsize_t offset; + jack_shm_info_t* shm_info; + jack_shmsize_t offset; } jack_port_buffer_info_t; /* The engine keeps an array of these in its local memory. */ @@ -46,91 +49,96 @@ typedef struct _jack_port_internal { } jack_port_internal_t; /* The engine's internal port type structure. */ -typedef struct _jack_port_type_internal { - pthread_mutex_t buffer_lock; /* only lock within server */ - JSList *buffer_freelist; /* list of free buffers */ - void *buffer_info; /* jack_buffer_info_t array */ - void *seg_addr; /* buffer segment address */ -} jack_port_type_internal_t; - +typedef struct _jack_port_buffer_list { + pthread_mutex_t lock; /* only lock within server */ + JSList *freelist; /* list of free buffers */ + jack_port_buffer_info_t *info; /* jack_buffer_info_t array */ +} jack_port_buffer_list_t; /* The main engine structure in local memory. */ struct _jack_engine { jack_control_t *control; + + JSList *drivers; struct _jack_driver *driver; + jack_driver_desc_t *driver_desc; + JSList *driver_params; /* these are "callbacks" made by the driver */ - - int (*set_buffer_size)(struct _jack_engine *, jack_nframes_t frames); - int (*set_sample_rate)(struct _jack_engine *, jack_nframes_t frames); - int (*run_cycle)(struct _jack_engine *, jack_nframes_t nframes, - float delayed_usecs); - void (*transport_cycle_start)(struct _jack_engine *, jack_time_t time); + int (*set_buffer_size) (struct _jack_engine *, jack_nframes_t frames); + int (*set_sample_rate) (struct _jack_engine *, jack_nframes_t frames); + int (*run_cycle) (struct _jack_engine *, jack_nframes_t nframes, + float delayed_usecs); + void (*delay) (struct _jack_engine *); + void (*transport_cycle_start) (struct _jack_engine *, jack_time_t time); + void (*driver_exit) (struct _jack_engine *); /* "private" sections starts here */ /* engine serialization -- use precedence for deadlock avoidance */ - pthread_mutex_t port_lock; - pthread_mutex_t request_lock; /* precedes driver_lock */ - pthread_mutex_t driver_lock; /* precedes client_lock */ + pthread_mutex_t request_lock; /* precedes client_lock */ pthread_mutex_t client_lock; - int process_errors; - int period_msecs; - int client_timeout_msecs; /* Time to wait for clients in - * msecs. Used when jackd is run in - * non-ASIO mode and without realtime - * priority enabled. */ - - /* internal port type info, indexed by the port type_id */ - jack_port_type_internal_t port_type[JACK_MAX_PORT_TYPES]; - - unsigned int port_max; - shm_name_t control_shm_name; - size_t control_size; - shm_name_t port_segment_name; /* XXX fix me */ - size_t port_segment_size; /* XXX fix me */ - void *port_segment_address; /* XXX fix me */ - pthread_t main_thread; - pthread_t server_thread; - - /* these lists are all protected by `client_lock' */ + pthread_mutex_t port_lock; + int process_errors; + int period_msecs; + + /* Time to wait for clients in msecs. Used when jackd is run in + * non-ASIO mode and without realtime priority enabled. */ + int client_timeout_msecs; + + /* info on the shm segment containing this->control */ + + jack_shm_info_t control_shm; - JSList *clients; - JSList *clients_waiting; + /* address-space local port buffer and segment info, + indexed by the port type_id + */ + jack_port_buffer_list_t port_buffers[JACK_MAX_PORT_TYPES]; + jack_shm_info_t port_segment[JACK_MAX_PORT_TYPES]; - struct _jack_port_internal *internal_ports; + unsigned int port_max; + pthread_t server_thread; + pthread_t watchdog_thread; - int fds[2]; + int fds[2]; jack_client_id_t next_client_id; - size_t pfd_size; - size_t pfd_max; - struct pollfd *pfd; - struct _jack_client_internal *timebase_client; + size_t pfd_size; + size_t pfd_max; + struct pollfd *pfd; + char fifo_prefix[PATH_MAX+1]; + int *fifo; + unsigned long fifo_size; + unsigned long external_client_cnt; + int rtpriority; + char asio_mode; + char freewheeling; + char verbose; + int reordered; + int watchdog_check; + pid_t wait_pid; + pthread_t freewheel_thread; + + /* these lists are protected by `client_lock' */ + JSList *clients; + JSList *clients_waiting; + + jack_port_internal_t *internal_ports; + jack_client_internal_t *timebase_client; jack_port_buffer_info_t *silent_buffer; - char fifo_prefix[PATH_MAX+1]; - int *fifo; - unsigned long fifo_size; - unsigned long external_client_cnt; - int rtpriority; - char verbose; - char asio_mode; - int reordered; - int watchdog_check; - - struct _jack_client_internal *current_client; + jack_client_internal_t *current_client; #define JACK_ENGINE_ROLLING_COUNT 32 #define JACK_ENGINE_ROLLING_INTERVAL 1024 jack_time_t rolling_client_usecs[JACK_ENGINE_ROLLING_COUNT]; - int rolling_client_usecs_cnt; - int rolling_client_usecs_index; - int rolling_interval; - float spare_usecs; - float usecs_per_cycle; + int rolling_client_usecs_cnt; + int rolling_client_usecs_index; + int rolling_interval; + float spare_usecs; + float usecs_per_cycle; #if defined(__APPLE__) && defined(__POWERPC__) - /* specific ressources for server/client real-time thread communication */ + /* specific resources for server/client real-time thread communication */ mach_port_t servertask, bp; int portnum; #endif @@ -139,11 +147,13 @@ struct _jack_engine { /* public functions */ -jack_engine_t *jack_engine_new (int real_time, int real_time_priority, int verbose, int client_timeout); +jack_engine_t *jack_engine_new (int real_time, int real_time_priority, + int verbose, int client_timeout, + pid_t waitpid, JSList * drivers); int jack_engine_delete (jack_engine_t *); int jack_run (jack_engine_t *engine); int jack_wait (jack_engine_t *engine); -int jack_engine_load_driver (jack_engine_t *, int, char **); +int jack_engine_load_driver (jack_engine_t *engine, jack_driver_desc_t * driver_desc, JSList * driver_params); void jack_set_asio_mode (jack_engine_t *, int yn); void jack_dump_configuration(jack_engine_t *engine, int take_lock); diff --git a/jack/internal.h b/jack/internal.h index a71cc3e..d81a32d 100644 --- a/jack/internal.h +++ b/jack/internal.h @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "bsd"; -*- */ /* Internal shared data and functions. @@ -126,6 +127,8 @@ typedef enum { PortRegistered, PortUnregistered, XRun, + StartFreewheel, + StopFreewheel } EventType; typedef struct { @@ -134,16 +137,12 @@ typedef struct { uint32_t n; jack_port_id_t port_id; jack_port_id_t self_id; - shm_name_t shm_name; } x; union { uint32_t n; jack_port_type_id_t ptid; jack_port_id_t other_id; } y; - union { - jack_shmsize_t size; - } z; } jack_event_t; typedef enum { @@ -228,8 +227,8 @@ typedef struct { uint32_t protocol_v; - shm_name_t client_shm_name; - shm_name_t control_shm_name; + jack_shm_info_t client_shm; + jack_shm_info_t engine_shm; char fifo_prefix[PATH_MAX+1]; @@ -237,16 +236,12 @@ typedef struct { int32_t realtime_priority; /* these two are valid only if the connect request - was for type == ClientDriver. */ + was for type == ClientDriver. + */ + jack_client_control_t *client_control; /* JOQ: 64/32 problem */ jack_control_t *engine_control; /* JOQ: 64/32 problem */ - jack_shmsize_t control_size; - - /* when we write this response, we deliver n_port_types - of jack_port_type_info_t after it. */ - uint32_t n_port_types; - #if defined(__APPLE__) && defined(__POWERPC__) /* specific resources for server/client real-time thread communication */ int32_t portnum; @@ -279,6 +274,8 @@ typedef enum { ResetSyncClient = 14, SetSyncTimeout = 15, SetBufferSize = 16, + FreeWheel = 17, + StopFreeWheel = 18, } RequestType; struct _jack_request { @@ -313,7 +310,9 @@ struct _jack_request { }; /* per-client structure allocated in the server's address space - * JOQ: then why isn't this in engine.h? */ + * its here because its not part of the engine structure. + */ + typedef struct _jack_client_internal { jack_client_control_t *control; @@ -324,7 +323,7 @@ typedef struct _jack_client_internal { int subgraph_wait_fd; JSList *ports; /* protected by engine->client_lock */ JSList *fed_by; /* protected by engine->client_lock */ - shm_name_t shm_name; + jack_shm_info_t control_shm; unsigned long execution_order; struct _jack_client_internal *next_client; /* not a linked list! */ dlhandle handle; @@ -346,19 +345,18 @@ extern void jack_cleanup_files (); extern int jack_client_handle_port_connection (jack_client_t *client, jack_event_t *event); -extern void jack_client_set_port_segment (jack_client_t *client, shm_name_t, - jack_port_type_id_t ptid, - jack_shmsize_t, void *addr); extern jack_client_t *jack_driver_client_new (jack_engine_t *, const char *client_name); -jack_client_t *jack_client_alloc_internal (jack_client_control_t*, - jack_control_t*); +extern jack_client_t *jack_client_alloc_internal (jack_client_control_t*, + jack_engine_t*); /* internal clients call this. it's defined in jack/engine.c */ void handle_internal_client_request (jack_control_t*, jack_request_t*); extern char *jack_server_dir; +extern void *jack_zero_filled_buffer; + extern void jack_error (const char *fmt, ...); extern jack_port_functions_t jack_builtin_audio_functions; @@ -373,5 +371,8 @@ extern void jack_call_sync_client (jack_client_t *client); extern void jack_call_timebase_master (jack_client_t *client); +extern int jack_acquire_real_time_scheduling (pthread_t, int priority); +extern int jack_drop_real_time_scheduling (pthread_t); + #endif /* __jack_internal_h__ */ diff --git a/jack/jack.h b/jack/jack.h index 212aa10..2309bc3 100644 --- a/jack/jack.h +++ b/jack/jack.h @@ -110,6 +110,26 @@ int jack_set_process_callback (jack_client_t *client, JackProcessCallback process_callback, void *arg); +/** + * Start/Stop JACK's "freewheel" mode. + * + * When in "freewheel" mode, JACK no longer waits for + * any external event to begin the start of the next process + * cycle. + * + * As a result, freewheel mode causes "faster than realtime" + * execution of a JACK graph. If possessed, real-time + * scheduling is dropped when entering freewheel mode, and + * if appropriate it is reacquired when stopping. + * + * @param client pointer to JACK client structure + * @param onoff if non-zero, freewheel mode starts. Otherwise + * freewheel mode ends. + * + * @return 0 on success, otherwise a non-zero error code. + */ +int jack_set_freewheel(jack_client_t* client, int onoff); + /** * Change the buffer size passed to the @a process_callback. * diff --git a/jack/port.h b/jack/port.h index c8761e6..c142ca6 100644 --- a/jack/port.h +++ b/jack/port.h @@ -24,12 +24,21 @@ #include #include #include +#include #define JACK_PORT_NAME_SIZE 32 #define JACK_PORT_TYPE_SIZE 32 /* The relatively low value of this constant reflects the fact that * JACK currently only knows about *1* port type. (March 2003) + * + * Further, the 4 covers: + * - a single non-negotiated audio format + * - music data (ie. MIDI) + * - video + * - one other + * + * which is probably enough for more than just the foreseeable future. */ #define JACK_MAX_PORT_TYPES 4 #define JACK_AUDIO_PORT_TYPE 0 @@ -39,16 +48,11 @@ typedef uint32_t jack_client_id_t; /* JACK shared memory segments are limited to MAX_INT32, they can be - * shared between 32-bit and 64-bit clients. */ + * shared between 32-bit and 64-bit clients. + */ #define JACK_SHM_MAX (MAX_INT32) -typedef int32_t jack_shmsize_t; typedef int32_t jack_port_type_id_t; -typedef struct { - shm_name_t shm_name; - jack_shmsize_t size; -} jack_port_segment_info_t; - /* Port type structure. * * (1) One for each port type is part of the engine's jack_control_t @@ -65,18 +69,22 @@ typedef struct _jack_port_type_info { const char type_name[JACK_PORT_TYPE_SIZE]; /* If == 1, then a buffer to handle nframes worth of data has - * sizeof(jack_default_audio_sample_t) * nframes bytes. If - * anything other than 1, the buffer allocated for input mixing - * will be this value times sizeof(jack_default_audio_sample_t) * - * nframes bytes in size. For non-audio data types, it may have a - * different value. If < 0, the value should be ignored, and - * buffer_size should be used. */ + * sizeof(jack_default_audio_sample_t) * nframes bytes. + * + * If > 1, the buffer allocated for input mixing will be + * this value times sizeof(jack_default_audio_sample_t) + * * nframes bytes in size. For non-audio data types, + * it may have a different value. + * + * If < 0, the value should be ignored, and buffer_size + * should be used. + */ int32_t buffer_scale_factor; /* ignored unless buffer_scale_factor is < 0. see above */ jack_shmsize_t buffer_size; - jack_port_segment_info_t shm_info; + jack_shm_registry_index_t shm_registry_index; } jack_port_type_info_t; @@ -94,9 +102,9 @@ typedef struct _jack_port_shared { volatile jack_nframes_t total_latency; volatile uint8_t monitor_requests; - int8_t has_mixdown; /* port has a mixdown function */ - char in_use : 1; - char locked : 1; + char has_mixdown; /* port has a mixdown function */ + char in_use; + char locked; } jack_port_shared_t; @@ -104,18 +112,19 @@ typedef struct _jack_port_functions { /* Function to mixdown multiple inputs to a buffer. Can be NULL, * indicating that multiple input connections are not legal for - * this data type. */ + * this data type. + */ void (*mixdown)(jack_port_t *, jack_nframes_t); } jack_port_functions_t; /* Allocated by the client in local memory. */ struct _jack_port { - void **client_segment_base; + void **client_segment_base; void *mix_buffer; jack_port_type_info_t *type_info; /* shared memory type info */ - struct _jack_port_shared *shared; /* corresponding shm struct */ - struct _jack_port *tied; /* locally tied source port */ + struct _jack_port_shared *shared; /* corresponding shm struct */ + struct _jack_port *tied; /* locally tied source port */ jack_port_functions_t fptr; pthread_mutex_t connection_lock; JSList *connections; diff --git a/jack/ringbuffer.h b/jack/ringbuffer.h new file mode 100644 index 0000000..19a2feb --- /dev/null +++ b/jack/ringbuffer.h @@ -0,0 +1,141 @@ +#ifndef _RINGBUFFER_H +#define _RINGBUFFER_H + +#include + +/** @file ringbuffer.h + * + * A set of library functions to make lock-free ringbuffers available + * to JACK clients. The `capture_client.c' (in the example_clients + * directory) is a fully functioning user of this API. + * + * The key attribute of a ringbuffer is that it can be safely accessed + * by two threads simultaneously -- one reading from the buffer and + * the other writing to it -- without using any synchronization or + * mutual exclusion primitives. For this to work correctly, there can + * only be a single reader and a single writer thread. Their + * identities cannot be interchanged. + * + */ + +typedef struct +{ + char *buf; + size_t len; +} +jack_ringbuffer_data_t ; + +typedef struct +{ + char *buf; + volatile size_t write_ptr; + volatile size_t read_ptr; + size_t size; + size_t size_mask; + int mlocked; +} +jack_ringbuffer_t ; + +/** + * jack_ringbuffer_create + * + * Allocates a ringbuffer data structure of a specified size. The + * caller must arrange for a call to jack_ringbuffer_free to release + * the memory associated with the ringbuffer. + * + * @param sz the ringbuffer size in bytes. + * + * @return a pointer to a new jack_ringbuffer_t, if successful; NULL + * otherwise. + */ + +jack_ringbuffer_t *jack_ringbuffer_create(size_t sz); +void jack_ringbuffer_free(jack_ringbuffer_t *rb); + +size_t jack_ringbuffer_write_space(jack_ringbuffer_t *rb); +size_t jack_ringbuffer_read_space(jack_ringbuffer_t *rb); + +/** + * jack_ringbuffer_read + * + * read a specified number of bytes from the ringbuffer. + * + * @param rb a pointer to the ringbuffer structure + * @param dest a pointer to a buffer where the data read from the ringbuffer + * will be placed + * @param cnt the number of bytes to be read + * + * @return the number of bytes read, which may range from 0 to cnt + */ +size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, char *dest, size_t cnt); + +/** + * jack_ringbuffer_write + * + * write a specified number of bytes from the ringbuffer. + * + * @param rb a pointer to the ringbuffer structure + * @param src a pointer to a buffer where the data written to the ringbuffer + * will be read from + * @param cnt the number of bytes to be write + * + * @return the number of bytes write, which may range from 0 to cnt + */ +size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, char *src, size_t cnt); + +/** + * jack_ringbuffer_get_read_vector + * + * fill a data structure with a description of the current readable data + * held in the ringbuffer. the description is returned in a 2 element + * array of jack_ringbuffer_data_t. two elements are necessary + * because the data to be read may be split across the end of the ringbuffer. + * + * the first element will always contain + * a valid len field, which may be zero or greater. if the len field + * is non-zero, then data can be read in a contiguous fashion using the address given + * in the corresponding buf field. + * + * if the second element has a non-zero len field, then a second contiguous + * stretch of data can be read from the address given in the corresponding buf + * field. + * + * @param rb a pointer to the ringbuffer structure + * @param vec a pointer to a 2 element array of jack_ringbuffer_data_t + * + */ +void jack_ringbuffer_get_read_vector(jack_ringbuffer_t *rb, jack_ringbuffer_data_t *vec); + +/** + * jack_ringbuffer_get_write_vector + * + * fill a data structure with a description of the current writable space + * in the ringbuffer. the description is returned in a 2 element + * array of jack_ringbuffer_data_t. two elements are necessary + * because the space available to write in may be split across the end + * of the ringbuffer. + * + * the first element will always contain + * a valid len field, which may be zero or greater. if the len field + * is non-zero, then data can be written in a contiguous fashion using the address given + * in the corresponding buf field. + * + * if the second element has a non-zero len field, then a second contiguous + * stretch of data can be written to the address given in the corresponding buf + * field. + * + * @param rb a pointer to the ringbuffer structure + * @param vec a pointer to a 2 element array of jack_ringbuffer_data_t + * + */ +void jack_ringbuffer_get_write_vector(jack_ringbuffer_t *rb, jack_ringbuffer_data_t *vec); + +int jack_ringbuffer_mlock(jack_ringbuffer_t *rb); + +void jack_ringbuffer_reset(jack_ringbuffer_t *rb); + +void jack_ringbuffer_write_advance(jack_ringbuffer_t *rb, size_t cnt); +void jack_ringbuffer_read_advance(jack_ringbuffer_t *rb, size_t cnt); + + +#endif diff --git a/jack/shm.h b/jack/shm.h index 82cee1f..8e5a1b8 100644 --- a/jack/shm.h +++ b/jack/shm.h @@ -1,19 +1,67 @@ #ifndef __jack_shm_h__ #define __jack_shm_h__ +#include +#include #include -#define MAX_SHM_ID 256 /* likely use is more like 16 */ +#define MAX_SHM_ID 256 /* likely use is more like 16 per jackd */ -extern int jack_initialize_shm (); -extern void jack_cleanup_shm (); +#ifdef USE_POSIX_SHM +typedef shm_name_t jack_shm_id_t; +#else +typedef int jack_shm_id_t; +#endif /* !USE_POSIX_SHM */ + +typedef int16_t jack_shm_registry_index_t; + +/** + * A structure holding information about shared memory + * allocated by JACK. this version persists across + * invocations of JACK, and can be used by multiple + * JACK servers. It contains no pointers and is valid + * across address spaces. + */ +typedef struct _jack_shm_registry { + pid_t allocator; /* PID that created shm segment */ + jack_shmsize_t size; /* needed for POSIX unattach */ + jack_shm_registry_index_t index; /* offset into the registry */ + jack_shm_id_t id; /* API specific, see above */ +} jack_shm_registry_t; + +/** + * a structure holding information about shared memory + * allocated by JACK. this version is valid only + * for a given address space. It contains a pointer + * indicating where the shared memory has been + * attached to the address space. + */ +typedef struct _jack_shm_info { + jack_shm_registry_index_t index; /* offset into the registry */ + char* attached_at; /* address where attached */ +} jack_shm_info_t; -extern void jack_register_shm (char *shm_name, char *address, int id); +/* utility functions used only within JACK */ -extern char *jack_get_shm (const char *shm_name, size_t size, int perm, int mode, int prot, int *id); -extern void jack_release_shm (char* addr, size_t size); -extern void jack_destroy_shm (const char *shm_name); +extern void jack_shm_copy_from_registry (jack_shm_info_t*, + jack_shm_registry_index_t); +extern void jack_shm_copy_to_registry (jack_shm_info_t*, + jack_shm_registry_index_t*); +extern void jack_release_shm_info (jack_shm_registry_index_t); + +static inline char* jack_shm_addr (jack_shm_info_t* si) { + return si->attached_at; +} + +/* here beginneth the API */ + +extern int jack_initialize_shm (); +extern void jack_cleanup_shm (); -extern char *jack_resize_shm (const char *shm_name, size_t size, int perm, int mode, int prot); +extern int jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* result); +extern void jack_release_shm (jack_shm_info_t*); +extern void jack_destroy_shm (jack_shm_info_t*); +extern int jack_attach_shm (jack_shm_info_t*); +extern int jack_resize_shm (jack_shm_info_t*, jack_shmsize_t size); #endif /* __jack_shm_h__ */ diff --git a/jack/transport.h b/jack/transport.h index a3c1d71..f3257eb 100644 --- a/jack/transport.h +++ b/jack/transport.h @@ -278,9 +278,18 @@ int jack_transport_locate (jack_client_t *client, * * @return Current transport state. */ -jack_transport_state_t jack_transport_query (jack_client_t *client, +jack_transport_state_t jack_transport_query (const jack_client_t *client, jack_position_t *pos); +/** + * Return an estimate of the current transport frame, + * including any time elapsed since the last transport + * positional update. + * + * @param client the JACK client structure + */ +jack_nframes_t jack_get_current_transport_frame (const jack_client_t *client); + /** * Request a new transport position. * diff --git a/jack/types.h b/jack/types.h index 18dee99..73edb2d 100644 --- a/jack/types.h +++ b/jack/types.h @@ -23,7 +23,8 @@ #include -typedef char shm_name_t[32]; +typedef char shm_name_t[32]; +typedef int32_t jack_shmsize_t; /** * Type used to represent sample frame counts. diff --git a/jackd/engine.c b/jackd/engine.c index 0c92e8f..2dff664 100644 --- a/jackd/engine.c +++ b/jackd/engine.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "bsd"; -*- */ /* Copyright (C) 2001-2003 Paul Davis @@ -71,7 +72,7 @@ typedef struct { } jack_connection_internal_t; typedef struct _jack_driver_info { - jack_driver_t *(*initialize)(jack_client_t*, int, char**); + jack_driver_t *(*initialize)(jack_client_t*, const JSList *); void (*finish); char (*client_name); dlhandle handle; @@ -127,6 +128,11 @@ static int internal_client_request (void*, jack_request_t *); static int jack_use_driver (jack_engine_t *engine, jack_driver_t *driver); static int jack_run_cycle (jack_engine_t *engine, jack_nframes_t nframes, float delayed_usecs); +static void jack_engine_notify_clients_about_delay (jack_engine_t *engine); +static void jack_engine_driver_exit (jack_engine_t* engine); +static int jack_start_freewheeling (jack_engine_t* engine); +static int jack_stop_freewheeling (jack_engine_t* engine); +static int jack_start_watchdog (jack_engine_t *engine); static char *client_state_names[] = { "Not triggered", @@ -167,18 +173,19 @@ jack_engine_reset_rolling_usecs (jack_engine_t *engine) } static inline jack_port_type_info_t * -jack_global_port_type_info (jack_engine_t *engine, jack_port_internal_t *port) +jack_port_type_info (jack_engine_t *engine, jack_port_internal_t *port) { /* Returns a pointer to the port type information in the - engine's shared control structure. */ + engine's shared control structure. + */ return &engine->control->port_types[port->shared->ptype_id]; } -static inline jack_port_type_internal_t * -jack_local_port_type_info (jack_engine_t *engine, jack_port_internal_t *port) +static inline jack_port_buffer_list_t * +jack_port_buffer_list (jack_engine_t *engine, jack_port_internal_t *port) { - /* Points to the engine's private port type struct. */ - return &engine->port_type[port->shared->ptype_id]; + /* Points to the engine's private port buffer list struct. */ + return &engine->port_buffers[port->shared->ptype_id]; } static int @@ -298,23 +305,92 @@ jack_cleanup_files () closedir (dir); } +void +jack_engine_place_port_buffers (jack_engine_t* engine, + jack_port_type_id_t ptid, + jack_shmsize_t one_buffer, + jack_shmsize_t size, + unsigned long nports) +{ + jack_shmsize_t offset; /* shared memory offset */ + jack_port_buffer_info_t *bi; + jack_port_buffer_list_t* pti = &engine->port_buffers[ptid]; + + pthread_mutex_lock (&pti->lock); + offset = 0; + + if (pti->info) { + + /* Buffer info array already allocated for this port + * type. This must be a resize operation, so + * recompute the buffer offsets, but leave the free + * list alone. + */ + int i; + + bi = pti->info; + while (offset < size) { + bi->offset = offset; + offset += one_buffer; + ++bi; + } + + /* update any existing output port offsets */ + for (i = 0; i < engine->port_max; i++) { + jack_port_shared_t *port = &engine->control->ports[i]; + if (port->in_use && + (port->flags & JackPortIsOutput) && + port->ptype_id == ptid) { + bi = engine->internal_ports[i].buffer_info; + if (bi) { + port->offset = bi->offset; + } + } + } + + } else { + + /* Allocate an array of buffer info structures for all + * the buffers in the segment. Chain them to the free + * list in memory address order, offset zero must come + * first. + */ + bi = pti->info = (jack_port_buffer_info_t *) + malloc (nports * sizeof (jack_port_buffer_info_t)); + + while (offset < size) { + bi->offset = offset; + pti->freelist = jack_slist_append (pti->freelist, bi); + offset += one_buffer; + ++bi; + } + + /* allocate the first buffer of the audio port segment + * for a zero-filled area + */ + if (ptid == JACK_AUDIO_PORT_TYPE) { + engine->silent_buffer = (jack_port_buffer_info_t *) + pti->freelist->data; + pti->freelist = + jack_slist_remove_link (pti->freelist, + pti->freelist); + } + } + + pthread_mutex_unlock (&pti->lock); +} + // JOQ: this should have a return code... static void jack_resize_port_segment (jack_engine_t *engine, - jack_port_type_info_t *port_type, + jack_port_type_id_t ptid, unsigned long nports) { jack_event_t event; - void *addr; - jack_shmsize_t offset; /* shared memory offset */ - size_t one_buffer; /* size of one buffer */ - size_t size; /* segment size */ - int shmid; - int perm; - jack_port_buffer_info_t *bi; - jack_port_type_id_t ptid = port_type->ptype_id; - jack_port_type_internal_t *pti = &engine->port_type[ptid]; - jack_port_id_t i; + jack_shmsize_t one_buffer; /* size of one buffer */ + jack_shmsize_t size; /* segment size */ + jack_port_type_info_t* port_type = &engine->control->port_types[ptid]; + jack_shm_info_t* shm_info = &engine->port_segment[ptid]; if (port_type->buffer_scale_factor < 0) { one_buffer = port_type->buffer_size; @@ -325,88 +401,71 @@ jack_resize_port_segment (jack_engine_t *engine, } size = nports * one_buffer; - perm = O_RDWR|O_CREAT; - if (port_type->shm_info.size == 0) { - + if (shm_info->attached_at == 0) { + + char name[64]; + /* no segment allocated, yet */ - snprintf (port_type->shm_info.shm_name, - sizeof(port_type->shm_info.shm_name), - "/jck-[%s]", port_type->type_name); + snprintf (name, sizeof(name), "/jck-[%s]", port_type->type_name); - if ((addr = jack_get_shm (port_type->shm_info.shm_name, size, - perm, 0666, PROT_READ|PROT_WRITE, - &shmid)) == MAP_FAILED) { + if (jack_shmalloc (name, size, shm_info)) { jack_error ("cannot create new port segment of %d" - " bytes, shm_name = %s (%s)", - size, port_type->shm_info.shm_name, + " bytes, name = %s (%s)", + size, name, strerror (errno)); return; } - - jack_register_shm (port_type->shm_info.shm_name, addr, shmid); - - /* Allocate an array of buffer info structures for all - * the buffers in the segment. Chain them to the free - * list in memory address order, offset zero must come - * first. */ - pthread_mutex_lock (&pti->buffer_lock); - offset = 0; - bi = pti->buffer_info = (jack_port_buffer_info_t *) - malloc (nports * sizeof (jack_port_buffer_info_t)); - while (offset < size) { - bi->shm_name = port_type->shm_info.shm_name; - bi->offset = offset; - pti->buffer_freelist = - jack_slist_append (pti->buffer_freelist, bi); - offset += one_buffer; - ++bi; + + if (jack_attach_shm (shm_info)) { + jack_error ("cannot attach to new port segment (name=%s) (%s)", + name, strerror (errno)); + return; } - pthread_mutex_unlock (&pti->buffer_lock); + + engine->control->port_types[ptid].shm_registry_index = + shm_info->index; } else { /* resize existing buffer segment */ - if ((addr = jack_resize_shm (port_type->shm_info.shm_name, - size, perm, 0666, - PROT_READ|PROT_WRITE)) - == MAP_FAILED) { + if (jack_resize_shm (shm_info, size)) { jack_error ("cannot resize port segment to %d bytes," - " shm_name = %s (%s)", size, - port_type->shm_info.shm_name, + " (%s)", size, strerror (errno)); return; } + } - /* recompute the buffer offsets */ - pthread_mutex_lock (&pti->buffer_lock); - offset = 0; - bi = pti->buffer_info; - while (offset < size) { - bi->offset = offset; - offset += one_buffer; - ++bi; - } + jack_engine_place_port_buffers (engine, ptid, one_buffer, size, nports); - /* update any existing output port offsets */ - for (i = 0; i < engine->port_max; i++) { - if (engine->control->ports[i].flags & JackPortIsOutput - && engine->control->ports[i].ptype_id == ptid) { - bi = engine->internal_ports[i].buffer_info; - if (bi) { - engine->control->ports[i].offset = - bi->offset; - } - } - } - pthread_mutex_unlock (&pti->buffer_lock); - } + if (ptid == JACK_AUDIO_PORT_TYPE) { + + /* Always zero `nframes' samples, it could have + * changed. The server's global variable + * jack_zero_filled_buffer is for internal clients. + * External clients will set their copies during the + * AttachPortSegment event. */ - port_type->shm_info.size = size; - engine->port_type[ptid].seg_addr = addr; + jack_zero_filled_buffer = + jack_shm_addr (shm_info) + + engine->silent_buffer->offset; + memset (jack_zero_filled_buffer, 0, + sizeof (jack_default_audio_sample_t) * one_buffer); + } if (engine->control->real_time) { - int rc = mlock (addr, size); + + /* Although we've called mlockall(CURRENT|FUTURE), the + * Linux VM manager still allows newly allocated pages + * to fault on first reference. This mlock() ensures + * that any new pages are present before restarting + * the process cycle. Since memory locks do not + * stack, they can still be unlocked with a single + * munlockall(). + */ + + int rc = mlock (jack_shm_addr (shm_info), size); if (rc < 0) { jack_error("JACK: unable to mlock() port buffers: " "%s", strerror(errno)); @@ -415,51 +474,31 @@ jack_resize_port_segment (jack_engine_t *engine, /* Tell everybody about this segment. */ event.type = AttachPortSegment; - strcpy (event.x.shm_name, port_type->shm_info.shm_name); event.y.ptid = ptid; - event.z.size = size; jack_deliver_event_to_all (engine, &event); } /* The driver invokes this callback both initially and whenever its - * buffer size changes. */ + * buffer size changes. + */ static int jack_driver_buffer_size (jack_engine_t *engine, jack_nframes_t nframes) { int i; jack_event_t event; - jack_port_type_internal_t *pti; JSList *node; VERBOSE (engine, "new buffer size %" PRIu32 "\n", nframes); engine->control->buffer_size = nframes; - if (engine->driver) engine->rolling_interval = jack_rolling_interval (engine->driver->period_usecs); for (i = 0; i < engine->control->n_port_types; ++i) { - jack_resize_port_segment (engine, - &engine->control->port_types[i], - engine->control->port_max); - } - - /* allocate the first buffer of the audio port segment for a - * zero-filled area */ - pti = &engine->port_type[0]; - if (engine->silent_buffer == NULL) { - engine->silent_buffer = (jack_port_buffer_info_t *) - pti->buffer_freelist->data; - pti->buffer_freelist = - jack_slist_remove_link (pti->buffer_freelist, - pti->buffer_freelist); + jack_resize_port_segment (engine, i, engine->control->port_max); } - /* always zero `nframes' samples, it could have changed */ - memset (engine->port_type[0].seg_addr + engine->silent_buffer->offset, - 0, sizeof (jack_default_audio_sample_t) * nframes); - /* update shared client copy of nframes */ jack_lock_graph (engine); for (node = engine->clients; node; node = jack_slist_next (node)) { @@ -491,15 +530,10 @@ jack_set_buffer_size_request (jack_engine_t *engine, jack_nframes_t nframes) return EINVAL; } - /* this halts the jack_main_thread() loop while we reset the - * driver's parameters */ - pthread_mutex_lock (&engine->driver_lock); - rc = driver->bufsize(driver, nframes); - if (rc) + if (rc != 0) jack_error("driver does not support %" PRIu32 "-frame buffers", nframes); - pthread_mutex_unlock (&engine->driver_lock); return rc; } @@ -569,10 +603,10 @@ jack_process_external(jack_engine_t *engine, JSList *node) static JSList * jack_process_external(jack_engine_t *engine, JSList *node) { - /* precondition: caller has driver_lock */ int status; char c; - float delayed_usecs; + struct pollfd pfd[1]; + int poll_timeout; jack_client_internal_t *client; jack_client_control_t *ctl; jack_time_t now, then; @@ -604,65 +638,56 @@ jack_process_external(jack_engine_t *engine, JSList *node) then = jack_get_microseconds (); - if (engine->asio_mode) { - engine->driver->wait (engine->driver, client->subgraph_wait_fd, - &status, &delayed_usecs); - } else { - struct pollfd pfd[1]; - int poll_timeout = (engine->control->real_time == 0 ? - engine->client_timeout_msecs : - engine->driver->period_usecs/1000); + poll_timeout = (engine->control->real_time == 0 ? + engine->client_timeout_msecs : + engine->driver->period_usecs/1000); - pfd[0].fd = client->subgraph_wait_fd; - pfd[0].events = POLLERR|POLLIN|POLLHUP|POLLNVAL; + pfd[0].fd = client->subgraph_wait_fd; + pfd[0].events = POLLERR|POLLIN|POLLHUP|POLLNVAL; - DEBUG ("waiting on fd==%d for process() subgraph to finish", - client->subgraph_wait_fd); + DEBUG ("waiting on fd==%d for process() subgraph to finish", + client->subgraph_wait_fd); - if (poll (pfd, 1, poll_timeout) < 0) { - jack_error ("poll on subgraph processing failed (%s)", - strerror (errno)); - status = -1; - } + if (poll (pfd, 1, poll_timeout) < 0) { + jack_error ("poll on subgraph processing failed (%s)", + strerror (errno)); + status = -1; + } - if (pfd[0].revents & ~POLLIN) { - jack_error ("subgraph starting at %s lost client", - client->control->name); - status = -2; - } + if (pfd[0].revents & ~POLLIN) { + jack_error ("subgraph starting at %s lost client", + client->control->name); + status = -2; + } - if (pfd[0].revents & POLLIN) { - status = 0; - } else { - jack_error ("subgraph starting at %s timed out " - "(subgraph_wait_fd=%d, status = %d, " - "state = %s)", - client->control->name, - client->subgraph_wait_fd, status, - client_state_names[client->control->state]); - status = 1; - } + if (pfd[0].revents & POLLIN) { + status = 0; + } else { + jack_error ("subgraph starting at %s timed out " + "(subgraph_wait_fd=%d, status = %d, state = %s)", + client->control->name, + client->subgraph_wait_fd, status, + client_state_names[client->control->state]); + status = 1; } now = jack_get_microseconds (); if (status != 0) { - if (engine->verbose) { - fprintf (stderr, "at %" PRIu64 - " client waiting on %d took %" PRIu64 - " usecs, status = %d sig = %" PRIu64 - " awa = %" PRIu64 " fin = %" PRIu64 - " dur=%" PRIu64 "\n", - now, - client->subgraph_wait_fd, - now - then, - status, - ctl->signalled_at, - ctl->awake_at, - ctl->finished_at, - ctl->finished_at? (ctl->finished_at - - ctl->signalled_at): 0); - } + VERBOSE (engine, "at %" PRIu64 + " client waiting on %d took %" PRIu64 + " usecs, status = %d sig = %" PRIu64 + " awa = %" PRIu64 " fin = %" PRIu64 + " dur=%" PRIu64 "\n", + now, + client->subgraph_wait_fd, + now - then, + status, + ctl->signalled_at, + ctl->awake_at, + ctl->finished_at, + ctl->finished_at? (ctl->finished_at - + ctl->signalled_at): 0); /* we can only consider the timeout a client error if * it actually woke up. its possible that the kernel @@ -708,7 +733,7 @@ jack_process_external(jack_engine_t *engine, JSList *node) static int jack_engine_process (jack_engine_t *engine, jack_nframes_t nframes) { - /* precondition: caller has graph_lock and driver_lock */ + /* precondition: caller has graph_lock */ jack_client_internal_t *client; JSList *node; @@ -782,11 +807,9 @@ jack_calc_cpu_load(jack_engine_t *engine) engine->driver->period_usecs)) * 50.0f + (engine->control->cpu_load * 0.5f); - if (engine->verbose) { - fprintf (stderr, "load = %.4f max usecs: %.3f, " - "spare = %.3f\n", engine->control->cpu_load, - max_usecs, engine->spare_usecs); - } + VERBOSE (engine, "load = %.4f max usecs: %.3f, " + "spare = %.3f\n", engine->control->cpu_load, + max_usecs, engine->spare_usecs); } } @@ -816,30 +839,26 @@ jack_remove_clients (jack_engine_t* engine) finally remove the client. */ if (client->error >= JACK_ERROR_WITH_SOCKETS) { - if (engine->verbose) { - fprintf (stderr, "removing failed " - "client %s state = %s errors" - " = %d\n", - client->control->name, - client_state_names[ - client->control->state - ], - client->error); - } + VERBOSE (engine, "removing failed " + "client %s state = %s errors" + " = %d\n", + client->control->name, + client_state_names[ + client->control->state + ], + client->error); jack_remove_client (engine, (jack_client_internal_t *) node->data); } else { - if (engine->verbose) { - fprintf (stderr, "zombifying failed " - "client %s state = %s errors" - " = %d\n", - client->control->name, - client_state_names[ - client->control->state - ], - client->error); - } + VERBOSE (engine, "zombifying failed " + "client %s state = %s errors" + " = %d\n", + client->control->name, + client_state_names[ + client->control->state + ], + client->error); jack_zombify_client (engine, (jack_client_internal_t *) node->data); @@ -955,7 +974,6 @@ jack_load_client (jack_engine_t *engine, jack_client_internal_t *client, static void jack_client_unload (jack_client_internal_t *client) { - /* precondition: caller has driver_lock */ if (client->handle) { if (client->finish) { client->finish (client->control->process_arg); @@ -989,25 +1007,21 @@ setup_client (jack_engine_t *engine, int client_fd, return 0; } - if (engine->verbose) { - fprintf (stderr, "new client: %s, id = %" PRIu32 - " type %d @ %p fd = %d\n", - client->control->name, client->control->id, - req->type, client->control, client_fd); - } + VERBOSE (engine, "new client: %s, id = %" PRIu32 + " type %d @ %p fd = %d\n", + client->control->name, client->control->id, + req->type, client->control, client_fd); res->protocol_v = jack_protocol_version; - strcpy (res->client_shm_name, client->shm_name); - strcpy (res->control_shm_name, engine->control_shm_name); - res->control_size = engine->control_size; + res->client_shm = client->control_shm; + res->engine_shm = engine->control_shm; res->realtime = engine->control->real_time; res->realtime_priority = engine->rtpriority - 1; - res->n_port_types = engine->control->n_port_types; #if defined(__APPLE__) && defined(__POWERPC__) - /* specific resources for server/client real-time thread - * communication */ - res->portnum = client->portnum; + /* specific resources for server/client real-time thread + * communication */ + res->portnum = client->portnum; #endif if (jack_client_is_internal(client)) { @@ -1044,28 +1058,13 @@ setup_client (jack_engine_t *engine, int client_fd, */ client->control->private_client = - jack_client_alloc_internal (client->control, - engine->control); - + jack_client_alloc_internal (client->control, engine); + jack_unlock_graph (engine); /* call its initialization function */ if (client->control->type == ClientInternal) { - /* tell it about current port types and their - * shared memory information */ - jack_port_type_id_t i; - for (i = 0; i < engine->control->n_port_types; ++i) { - jack_port_type_info_t *port_type = - &engine->control->port_types[i]; - jack_client_set_port_segment ( - client->control->private_client, - port_type->shm_info.shm_name, - i, - port_type->shm_info.size, - engine->port_type[i].seg_addr); - } - if (client->initialize (client->control->private_client, req->object_data)) { jack_client_delete (engine, client); @@ -1098,27 +1097,23 @@ setup_client (jack_engine_t *engine, int client_fd, } static jack_driver_info_t * -jack_load_driver (jack_engine_t *engine, char *so_name) +jack_load_driver (jack_engine_t *engine, jack_driver_desc_t * driver_desc) { - /* precondition: caller has driver_lock */ const char *errstr; - char path_to_so[PATH_MAX+1]; jack_driver_info_t *info; info = (jack_driver_info_t *) calloc (1, sizeof (*info)); - snprintf (path_to_so, sizeof (path_to_so), ADDON_DIR "/jack_%s.so", - so_name); - - info->handle = dlopen (path_to_so, RTLD_NOW|RTLD_GLOBAL); + + info->handle = dlopen (driver_desc->file, RTLD_NOW|RTLD_GLOBAL); if (info->handle == NULL) { if ((errstr = dlerror ()) != 0) { - jack_error ("can't load \"%s\": %s", path_to_so, + jack_error ("can't load \"%s\": %s", driver_desc->file, errstr); } else { jack_error ("bizarre error loading driver shared " - "object %s", path_to_so); + "object %s", driver_desc->file); } goto fail; } @@ -1127,7 +1122,7 @@ jack_load_driver (jack_engine_t *engine, char *so_name) if ((errstr = dlerror ()) != 0) { jack_error ("no initialize function in shared object %s\n", - path_to_so); + driver_desc->file); goto fail; } @@ -1135,7 +1130,7 @@ jack_load_driver (jack_engine_t *engine, char *so_name) if ((errstr = dlerror ()) != 0) { jack_error ("no finish function in in shared driver object %s", - path_to_so); + driver_desc->file); goto fail; } @@ -1143,7 +1138,7 @@ jack_load_driver (jack_engine_t *engine, char *so_name) if ((errstr = dlerror ()) != 0) { jack_error ("no client name in in shared driver object %s", - path_to_so); + driver_desc->file); goto fail; } @@ -1161,13 +1156,12 @@ jack_load_driver (jack_engine_t *engine, char *so_name) void jack_driver_unload (jack_driver_t *driver) { - /* precondition: caller has driver_lock */ driver->finish (driver); dlclose (driver->handle); } int -jack_engine_load_driver (jack_engine_t *engine, int argc, char *argv[]) +jack_engine_load_driver (jack_engine_t *engine, jack_driver_desc_t * driver_desc, JSList * driver_params) { jack_client_connect_request_t req; jack_client_connect_result_t res; @@ -1175,39 +1169,43 @@ jack_engine_load_driver (jack_engine_t *engine, int argc, char *argv[]) jack_driver_t *driver; jack_driver_info_t *info; - pthread_mutex_lock (&engine->driver_lock); - - if ((info = jack_load_driver (engine, argv[0])) == NULL) { - goto unlock; + if ((info = jack_load_driver (engine, driver_desc)) == NULL) { + return -1; } req.type = ClientDriver; snprintf (req.name, sizeof (req.name), "%s", info->client_name); if ((client = setup_client (engine, -1, &req, &res)) == NULL) { - goto unlock; + return -1; } if ((driver = info->initialize (client->control->private_client, - argc, argv)) != 0) { - driver->handle = info->handle; - driver->finish = info->finish; + driver_params)) == NULL) { + free (info); + return -1; } + driver->handle = info->handle; + driver->finish = info->finish; free (info); if (jack_use_driver (engine, driver)) { jack_driver_unload (driver); jack_client_delete (engine, client); - goto unlock; + return -1; } - pthread_mutex_unlock (&engine->driver_lock); - return 0; + engine->driver_desc = driver_desc; + engine->driver_params = driver_params; - unlock: - pthread_mutex_unlock (&engine->driver_lock); - return -1; + if (engine->control->real_time) { + if (jack_start_watchdog (engine)) { + return -1; + } + engine->watchdog_check = 1; + } + return 0; } static int @@ -1219,9 +1217,7 @@ handle_unload_client (jack_engine_t *engine, int client_fd, res.status = -1; - if (engine->verbose) { - fprintf (stderr, "unloading client \"%s\"\n", req->name); - } + VERBOSE (engine, "unloading client \"%s\"\n", req->name); jack_lock_graph (engine); for (node = engine->clients; node; node = jack_slist_next (node)) { @@ -1245,7 +1241,6 @@ handle_new_client (jack_engine_t *engine, int client_fd) jack_client_internal_t *client; jack_client_connect_request_t req; jack_client_connect_result_t res; - unsigned long i; if (read (client_fd, &req, sizeof (req)) != sizeof (req)) { jack_error ("cannot read connection request from client"); @@ -1266,25 +1261,13 @@ handle_new_client (jack_engine_t *engine, int client_fd) return -1; } - /* give external clients a chance to find out about all known - * port types */ switch (client->control->type) { case ClientDriver: case ClientInternal: close (client_fd); break; - + default: - for (i = 0; i < engine->control->n_port_types; ++i) { - if (write (client->request_fd, - &engine->control->port_types[i], - sizeof (jack_port_type_info_t)) - != sizeof (jack_port_type_info_t)) { - jack_error ("cannot send port type information" - " to new client"); - jack_client_delete (engine, client); - } - } break; } @@ -1331,19 +1314,15 @@ static int check_capabilities (jack_engine_t *engine) int have_all_caps = 1; if (caps == NULL) { - if (engine->verbose) { - fprintf (stderr, "check: could not allocate capability" - " working storage\n"); - } + VERBOSE (engine, "check: could not allocate capability" + " working storage\n"); return 0; } pid = getpid (); cap_clear (caps); if (capgetp (pid, caps)) { - if (engine->verbose) { - fprintf (stderr, "check: could not get capabilities " - "for process %d\n", pid); - } + VERBOSE (engine, "check: could not get capabilities " + "for process %d\n", pid); return 0; } /* check that we are able to give capabilites to other processes */ @@ -1381,18 +1360,14 @@ static int give_capabilities (jack_engine_t *engine, pid_t pid) cap_value_t cap_list[] = {CAP_SYS_NICE, CAP_SYS_RESOURCE, CAP_IPC_LOCK}; if (caps == NULL) { - if (engine->verbose) { - fprintf (stderr, "give: could not allocate capability" - " working storage\n"); - } + VERBOSE (engine, "give: could not allocate capability" + " working storage\n"); return -1; } cap_clear(caps); if (capgetp (pid, caps)) { - if (engine->verbose) { - fprintf (stderr, "give: could not get current " - "capabilities for process %d\n", pid); - } + VERBOSE (engine, "give: could not get current " + "capabilities for process %d\n", pid); cap_clear(caps); } cap_set_flag(caps, CAP_EFFECTIVE, caps_size, cap_list , CAP_SET); @@ -1434,11 +1409,9 @@ jack_set_client_capabilities (jack_engine_t *engine, jack_client_id_t id) "process %d\n", client->control->pid); } else { - if (engine->verbose) { - fprintf (stderr, "gave capabilities to" - " process %d\n", - client->control->pid); - } + VERBOSE (engine, "gave capabilities to" + " process %d\n", + client->control->pid); } } } @@ -1494,9 +1467,8 @@ jack_client_do_deactivate (jack_engine_t *engine, jack_client_internal_t *client, int sort_graph) { - /* called must hold engine->client_lock and must have checked - for and/or cleared all connections held by client. - */ + /* caller must hold engine->client_lock and must have checked for and/or + * cleared all connections held by client. */ client->control->active = FALSE; if (!jack_client_is_internal (client) && @@ -1593,12 +1565,10 @@ handle_client_socket_error (jack_engine_t *engine, int fd) } if (client) { - if (engine->verbose) { - fprintf (stderr, "removing failed client %s state = " - "%s errors = %d\n", client->control->name, - client_state_names[client->control->state], - client->error); - } + VERBOSE (engine, "removing failed client %s state = " + "%s errors = %d\n", client->control->name, + client_state_names[client->control->state], + client->error); jack_remove_client(engine, client); jack_sort_graph (engine); } @@ -1716,6 +1686,14 @@ do_request (jack_engine_t *engine, jack_request_t *req, int *reply_fd) } break; + case FreeWheel: + req->status = jack_start_freewheeling (engine); + break; + + case StopFreeWheel: + req->status = jack_stop_freewheeling (engine); + break; + case SetBufferSize: req->status = jack_set_buffer_size_request (engine, req->x.nframes); @@ -1932,14 +1910,12 @@ jack_server_thread (void *arg) } jack_engine_t * -jack_engine_new (int realtime, int rtpriority, int verbose, int client_timeout) +jack_engine_new (int realtime, int rtpriority, int verbose, + int client_timeout, pid_t wait_pid, JSList * drivers) { jack_engine_t *engine; - void *addr; unsigned int i; - int shmid; - int perm; - + #ifdef USE_CAPABILITIES uid_t uid = getuid (); uid_t euid = geteuid (); @@ -1948,10 +1924,16 @@ jack_engine_new (int realtime, int rtpriority, int verbose, int client_timeout) engine = (jack_engine_t *) malloc (sizeof (jack_engine_t)); - engine->driver = 0; + engine->drivers = drivers; + engine->driver = NULL; + engine->driver_desc = NULL; + engine->driver_params = NULL; + engine->set_sample_rate = jack_set_sample_rate; engine->set_buffer_size = jack_driver_buffer_size; engine->run_cycle = jack_run_cycle; + engine->delay = jack_engine_notify_clients_about_delay; + engine->driver_exit = jack_engine_driver_exit; engine->transport_cycle_start = jack_transport_cycle_start; engine->client_timeout_msecs = client_timeout; @@ -1961,13 +1943,14 @@ jack_engine_new (int realtime, int rtpriority, int verbose, int client_timeout) engine->silent_buffer = 0; engine->verbose = verbose; engine->asio_mode = FALSE; + engine->freewheeling = 0; + engine->wait_pid = wait_pid; jack_engine_reset_rolling_usecs (engine); pthread_mutex_init (&engine->client_lock, 0); pthread_mutex_init (&engine->port_lock, 0); pthread_mutex_init (&engine->request_lock, 0); - pthread_mutex_init (&engine->driver_lock, 0); engine->clients = 0; @@ -1986,29 +1969,26 @@ jack_engine_new (int realtime, int rtpriority, int verbose, int client_timeout) srandom (time ((time_t *) 0)); - snprintf (engine->control_shm_name, - sizeof (engine->control_shm_name), "/jack-engine"); - engine->control_size = sizeof (jack_control_t) + - (sizeof (jack_port_shared_t) * engine->port_max); - if (jack_initialize_shm (engine)) { return 0; } - perm = O_RDWR|O_CREAT; - - if ((addr = jack_get_shm (engine->control_shm_name, - engine->control_size, - perm, 0644, PROT_READ|PROT_WRITE, &shmid)) - == MAP_FAILED) { + if (jack_shmalloc ("/jack-engine", + sizeof (jack_control_t) + ((sizeof (jack_port_shared_t) * engine->port_max)), + &engine->control_shm)) { jack_error ("cannot create engine control shared memory " "segment (%s)", strerror (errno)); return 0; } - jack_register_shm (engine->control_shm_name, addr, shmid); - - engine->control = (jack_control_t *) addr; + if (jack_attach_shm (&engine->control_shm)) { + jack_error ("cannot attach to engine control shared memory (%s)", + strerror (errno)); + jack_destroy_shm (&engine->control_shm); + return 0; + } + + engine->control = (jack_control_t *) jack_shm_addr (&engine->control_shm); /* Setup port type information from builtins. buffer space is * allocated when the driver calls jack_driver_buffer_size(). @@ -2019,14 +1999,22 @@ jack_engine_new (int realtime, int rtpriority, int verbose, int client_timeout) &jack_builtin_port_types[i], sizeof (jack_port_type_info_t)); + VERBOSE (engine, "registered builtin port type %s\n", + engine->control->port_types[i].type_name); + /* the port type id is index into port_types array */ engine->control->port_types[i].ptype_id = i; /* be sure to initialize mutex correctly */ - pthread_mutex_init (&engine->port_type[i].buffer_lock, NULL); + pthread_mutex_init (&engine->port_buffers[i].lock, NULL); - /* indicate no shared memory allocation for this port type */ - engine->control->port_types[i].shm_info.size = 0; + /* set buffer list info correctly */ + engine->port_buffers[i].freelist = NULL; + engine->port_buffers[i].info = NULL; + + /* mark each port segment as not allocated */ + engine->port_segment[i].index = -1; + engine->port_segment[i].attached_at = 0; } engine->control->n_port_types = i; @@ -2059,8 +2047,8 @@ jack_engine_new (int realtime, int rtpriority, int verbose, int client_timeout) engine->control->cpu_load = 0; engine->control->buffer_size = 0; - jack_set_sample_rate (engine, 0); jack_transport_init (engine); + jack_set_sample_rate (engine, 0); engine->control->internal = 0; engine->control->has_capabilities = 0; @@ -2079,25 +2067,21 @@ jack_engine_new (int realtime, int rtpriority, int verbose, int client_timeout) #ifdef USE_CAPABILITIES if (uid == 0 || euid == 0) { - if (engine->verbose) { - fprintf (stderr, "running with uid=%d and euid=%d, " - "will not try to use capabilites\n", - uid, euid); - } + VERBOSE (engine, "running with uid=%d and euid=%d, " + "will not try to use capabilites\n", + uid, euid); } else { /* only try to use capabilities if not running as root */ engine->control->has_capabilities = check_capabilities (engine); if (engine->control->has_capabilities == 0) { - if (engine->verbose) { - fprintf (stderr, "required capabilities not " - "available\n"); - } + VERBOSE (engine, "required capabilities not " + "available\n"); } if (engine->verbose) { size_t size; cap_t cap = cap_init(); capgetp(0, cap); - fprintf (stderr, "capabilities: %s\n", + VERBOSE (engine, "capabilities: %s\n", cap_to_text(cap, &size)); } } @@ -2143,39 +2127,8 @@ jack_become_real_time (pthread_t thread, int priority) return 0; } -#ifdef HAVE_ON_EXIT -static void -cancel_cleanup (int status, void *arg) - -{ - jack_engine_t *engine = (jack_engine_t *) arg; - engine->control->engine_ok = 0; - if (pthread_mutex_trylock (&engine->driver_lock) == 0) { - engine->driver->stop (engine->driver); - engine->driver->finish (engine->driver); - } -} -#else -#ifdef HAVE_ATEXIT -jack_engine_t *global_engine; - -static void -cancel_cleanup (void) - -{ - jack_engine_t *engine = global_engine; - if (pthread_mutex_trylock (&engine->driver_lock) == 0) { - engine->driver->stop (engine->driver); - engine->driver->finish (engine->driver); - } -} -#else -#error "Don't know how to make an exit handler" -#endif /* HAVE_ATEXIT */ -#endif /* HAVE_ON_EXIT */ - static void * -watchdog_thread (void *arg) +jack_watchdog_thread (void *arg) { jack_engine_t *engine = (jack_engine_t *) arg; int watchdog_priority = engine->rtpriority + 10; @@ -2210,6 +2163,11 @@ watchdog_thread (void *arg) kill (-getpgrp(), SIGABRT); /*NOTREACHED*/ exit (1); + +#if 0 /* suppress watchdog message */ + } else { + VERBOSE(engine, "jackd watchdog: all is well.\n"); +#endif } engine->watchdog_check = 0; } @@ -2218,13 +2176,12 @@ watchdog_thread (void *arg) static int jack_start_watchdog (jack_engine_t *engine) { - pthread_t watchdog; - - if (pthread_create (&watchdog, 0, watchdog_thread, engine)) { + if (pthread_create (&engine->watchdog_thread, 0, + jack_watchdog_thread, engine)) { jack_error ("cannot start watchdog thread"); return -1; } - pthread_detach (watchdog); + pthread_detach (engine->watchdog_thread); return 0; } @@ -2262,12 +2219,106 @@ jack_inc_frame_time (jack_engine_t *engine, jack_nframes_t amount) time->guard2++; } +static void* +jack_engine_freewheel (void *arg) +{ + jack_engine_t* engine = (jack_engine_t *) arg; + + VERBOSE (engine, "freewheel thread starting ...\n"); + + /* we should not be running SCHED_FIFO, so we don't + have to do anything about scheduling. + */ + + while (engine->freewheeling) { + + jack_lock_graph (engine); + + if (jack_engine_process (engine, engine->control->buffer_size)) { + jack_error ("process cycle within freewheel failed"); + jack_unlock_graph (engine); + break; + } + + jack_unlock_graph (engine); + } + + VERBOSE (engine, "freewheel came to an end, naturally\n"); + return 0; +} + +static int +jack_start_freewheeling (jack_engine_t* engine) +{ + jack_event_t event; + + if (engine->driver == NULL) { + jack_error ("cannot start freewheeling without a driver!"); + return -1; + } + + event.type = StartFreewheel; + jack_deliver_event_to_all (engine, &event); + + if (engine->driver->stop (engine->driver)) { + jack_error ("could not stop driver for freewheeling"); + return -1; + } + + engine->freewheeling = 1; + + if (pthread_create (&engine->freewheel_thread, NULL, + jack_engine_freewheel, engine)) { + jack_error ("could not start create freewheel thread"); + return -1; + } + + return 0; +} + +static int +jack_stop_freewheeling (jack_engine_t* engine) +{ + jack_event_t event; + void *ftstatus; + + if (engine->driver == NULL) { + jack_error ("cannot start freewheeling without a driver!"); + return -1; + } + + if (!engine->freewheeling) { + VERBOSE (engine, "stop freewheel when not freewheeling\n"); + return 0; + } + + /* tell the freewheel thread to stop, and wait for it + to exit. + */ + + engine->freewheeling = 0; + VERBOSE (engine, "freewheeling stopped, waiting for thread\n"); + pthread_join (engine->freewheel_thread, &ftstatus); + VERBOSE (engine, "freewheel thread has returned\n"); + + /* tell everyone we've stopped */ + + event.type = StopFreewheel; + jack_deliver_event_to_all (engine, &event); + + /* restart the driver */ + + if (engine->driver->start (engine->driver)) { + jack_error ("could not restart driver after freewheeling"); + return -1; + } + return 0; +} + static int jack_run_one_cycle (jack_engine_t *engine, jack_nframes_t nframes, float delayed_usecs) { - /* precondition: caller has driver lock */ - int restart = 0; jack_driver_t* driver = engine->driver; int ret = -1; static int consecutive_excessive_delays = 0; @@ -2288,19 +2339,8 @@ jack_run_one_cycle (jack_engine_t *engine, jack_nframes_t nframes, return -1; /* will exit the thread loop */ } - if (driver->stop (driver)) { - jack_error ("cannot stop current driver"); - return -1; /* will exit the thread loop */ - } - jack_engine_notify_clients_about_delay (engine); - if (driver->start (driver)) { - jack_error ("cannot restart current driver after " - "delay"); - return -1; /* will exit the thread loop */ - } - return 0; } else { @@ -2313,37 +2353,39 @@ jack_run_one_cycle (jack_engine_t *engine, jack_nframes_t nframes, return 0; } - if (driver->read (driver, nframes)) { - goto unlock; + if (!engine->freewheeling) { + if (driver->read (driver, nframes)) { + goto unlock; + } } engine->watchdog_check = 1; - if (jack_engine_process (engine, nframes)) { - driver->stop (driver); - restart = 1; - } else { - if (driver->write (driver, nframes)) { - goto unlock; + if (jack_engine_process (engine, nframes) == 0) { + if (!engine->freewheeling) { + if (driver->write (driver, nframes)) { + goto unlock; + } } } jack_engine_post_process (engine); - jack_inc_frame_time (engine, nframes); - ret = 0; unlock: jack_unlock_graph (engine); - - if (restart) { - driver->start (driver); - } - return ret; } +static void +jack_engine_driver_exit (jack_engine_t* engine) +{ + /* tell anyone waiting that the driver exited. */ + kill (engine->wait_pid, SIGUSR2); + engine->driver = NULL; +} + static int jack_run_cycle (jack_engine_t *engine, jack_nframes_t nframes, float delayed_usecs) @@ -2353,7 +2395,7 @@ jack_run_cycle (jack_engine_t *engine, jack_nframes_t nframes, if (engine->verbose) { if (nframes != b_size) { - fprintf(stderr, + VERBOSE (engine, "late driver wakeup: nframes to process = %" PRIu32 ".\n", nframes); } @@ -2370,155 +2412,38 @@ jack_run_cycle (jack_engine_t *engine, jack_nframes_t nframes, return 0; } -static void * -jack_main_thread (void *arg) +int +jack_engine_delete (jack_engine_t *engine) { - jack_engine_t *engine = (jack_engine_t *) arg; - jack_driver_t *driver = engine->driver; - int wait_status; - jack_nframes_t nframes; - float delayed_usecs; - - if (engine->control->real_time) { + int i; - if (jack_start_watchdog (engine)) { - pthread_exit (0); - } + if (engine && engine->driver) { + jack_driver_t* driver = engine->driver; - if (jack_become_real_time (pthread_self(), - engine->rtpriority)) { - engine->control->real_time = 0; - } + VERBOSE (engine, "stopping driver\n"); + driver->stop (driver); + VERBOSE (engine, "detaching driver\n"); + driver->detach (driver, engine); + VERBOSE (engine, "unloading driver\n"); + jack_driver_unload (driver); + engine->driver = NULL; } - pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - - engine->watchdog_check = 1; - - while (1) { - - pthread_mutex_lock (&engine->driver_lock); - - nframes = driver->wait (driver, -1, &wait_status, - &delayed_usecs); - - if (nframes == 0) { - - /* the driver detected an xrun and restarted */ - jack_engine_notify_clients_about_delay (engine); - - } else if (wait_status == 0) { - - if (jack_run_cycle (engine, nframes, - delayed_usecs) != 0) { - break; - } - - } else if (wait_status < 0) { - - break; - - } else { - /* driver restarted, just continue */ - } - - pthread_mutex_unlock (&engine->driver_lock); + for (i = 0; i < engine->control->n_port_types; ++i) { + jack_release_shm (&engine->port_segment[i]); + jack_destroy_shm (&engine->port_segment[i]); } - pthread_mutex_unlock (&engine->driver_lock); - pthread_exit (0); - /*NOTREACHED*/ - return 0; -} - -int -jack_run (jack_engine_t *engine) -{ - int rc; - -#ifdef HAVE_ON_EXIT - on_exit (cancel_cleanup, engine); -#else -#ifdef HAVE_ATEXIT - global_engine = engine; - atexit (cancel_cleanup); -#else -#error "Don't know how to install an exit handler" -#endif /* HAVE_ATEXIT */ -#endif /* HAVE_ON_EXIT */ - - if (engine->driver == NULL) { - jack_error ("engine driver not set; cannot start"); - return -1; - } - - pthread_mutex_lock (&engine->driver_lock); - rc = engine->driver->start (engine->driver); - pthread_mutex_unlock (&engine->driver_lock); - if (rc != 0) { - jack_error ("cannot start driver"); - return -1; - } - -#if defined(__APPLE__) && defined(__POWERPC__) - return 0; -#else - return pthread_create (&engine->main_thread, 0, - jack_main_thread, engine); -#endif - -} + /* stop the other engine threads */ + pthread_cancel (engine->server_thread); + pthread_cancel (engine->watchdog_thread); + /* free engine control shm segment */ + engine->control = NULL; + jack_release_shm (&engine->control_shm); + jack_destroy_shm (&engine->control_shm); -#if defined(__APPLE__) && defined(__POWERPC__) -int -jack_wait (jack_engine_t *engine) -{ - while(1) sleep(1); -} -#else -int -jack_wait (jack_engine_t *engine) -{ - void *ret = 0; - int err; - - if ((err = pthread_join (engine->main_thread, &ret)) != 0) { - switch (err) { - case EINVAL: - jack_error ("cannot join with audio thread (thread " - "detached, or another thread is waiting)"); - break; - case ESRCH: - jack_error ("cannot join with audio thread (thread " - "no longer exists)"); - break; - case EDEADLK: - jack_error ("programming error: jack_wait() called by" - " audio thread"); - break; - default: - jack_error ("cannot join with audio thread (%s)", - strerror (errno)); - } - } - return (int) ((intptr_t)ret); -} -#endif - -int -jack_engine_delete (jack_engine_t *engine) -{ - if (engine) { - -#if defined(__APPLE__) && defined(__POWERPC__) - /* the jack_run_cycle() function is directly called - * from the CoreAudo audio callback */ - return 0; -#else - return pthread_cancel (engine->main_thread); -#endif - } + free (engine); return 0; } @@ -2527,33 +2452,7 @@ jack_client_internal_new (jack_engine_t *engine, int fd, jack_client_connect_request_t *req) { jack_client_internal_t *client; - shm_name_t shm_name; - int shmid; - int perm; - void *addr = 0; - - switch (req->type) { - case ClientInternal: - case ClientDriver: - break; - case ClientExternal: - - perm = O_RDWR|O_CREAT; - snprintf (shm_name, sizeof (shm_name), "/jack-c-%s", req->name); - - if ((addr = jack_get_shm (shm_name, - sizeof (jack_client_control_t), - perm, 0666, PROT_READ|PROT_WRITE, - &shmid)) == MAP_FAILED) { - jack_error ("cannot create client control block for %s", - req->name); - return 0; - } - jack_register_shm (shm_name, addr, shmid); - break; - } - client = (jack_client_internal_t *) malloc (sizeof (jack_client_internal_t)); @@ -2573,8 +2472,29 @@ jack_client_internal_new (jack_engine_t *engine, int fd, malloc (sizeof (jack_client_control_t)); } else { - strcpy (client->shm_name, shm_name); - client->control = (jack_client_control_t *) addr; + + char shm_name[PATH_MAX+1]; + + snprintf (shm_name, sizeof (shm_name), "/jack-c-%s", req->name); + + if (jack_shmalloc (shm_name, + sizeof (jack_client_control_t), + &client->control_shm)) { + jack_error ("cannot create client control block for %s", + req->name); + free (client); + return 0; + } + + if (jack_attach_shm (&client->control_shm)) { + jack_error ("cannot attach to client control block for %s (%s)", + req->name, strerror (errno)); + jack_destroy_shm (&client->control_shm); + free (client); + return 0; + } + + client->control = (jack_client_control_t *) jack_shm_addr (&client->control_shm); } client->control->type = req->type; @@ -2665,9 +2585,7 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client) /* caller must hold the client_lock */ - if (engine->verbose) { - fprintf (stderr, "adios senor %s\n", client->control->name); - } + VERBOSE (engine, "adios senor %s\n", client->control->name); /* if its not already a zombie, make it so */ @@ -2716,14 +2634,20 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client) static void jack_client_delete (jack_engine_t *engine, jack_client_internal_t *client) { - /* precondition: caller has driver_lock */ if (jack_client_is_internal (client)) { + jack_client_unload (client); free ((char *) client->control); + } else { - jack_destroy_shm (client->shm_name); - jack_release_shm ((char*)client->control, - sizeof (jack_client_control_t)); + + /* release the client segment, mark it for + destruction, and free up the shm registry + information so that it can be reused. + */ + + jack_release_shm (&client->control_shm); + jack_destroy_shm (&client->control_shm); } free (client); @@ -2841,17 +2765,6 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client, } break; - case AttachPortSegment: - - /* Internal clients don't need to attach, but - * they still need to set the port_segment. */ - jack_client_set_port_segment - (client->control->private_client, - event->x.shm_name, event->y.ptid, - event->z.size, - engine->port_type[event->y.ptid].seg_addr); - break; - default: /* internal clients don't need to know */ break; @@ -2865,7 +2778,7 @@ jack_deliver_event (jack_engine_t *engine, jack_client_internal_t *client, * it's worth telling the client */ DEBUG ("engine writing on event fd"); - + if (write (client->event_fd, event, sizeof (*event)) != sizeof (*event)) { jack_error ("cannot send event to client [%s]" @@ -2913,9 +2826,7 @@ jack_rechain_graph (jack_engine_t *engine) subgraph_client = 0; - if (engine->verbose) { - fprintf(stderr, "++ jack_rechain_graph():\n"); - } + VERBOSE(engine, "++ jack_rechain_graph():\n"); event.type = GraphReordered; @@ -2959,25 +2870,20 @@ jack_rechain_graph (jack_engine_t *engine) if (subgraph_client) { subgraph_client->subgraph_wait_fd = jack_get_fifo_fd (engine, n); - if (engine->verbose) { - fprintf(stderr, - "client %s: wait_fd=" - "%d, execution_order=" - "%lu.\n", - subgraph_client-> - control->name, - subgraph_client-> - subgraph_wait_fd, n); - } + VERBOSE (engine, "client %s: wait_fd=" + "%d, execution_order=" + "%lu.\n", + subgraph_client-> + control->name, + subgraph_client-> + subgraph_wait_fd, n); n++; } - if (engine->verbose) { - fprintf(stderr, "client %s: internal " - "client, execution_order=" - "%lu.\n", - client->control->name, n); - } + VERBOSE (engine, "client %s: internal " + "client, execution_order=" + "%lu.\n", + client->control->name, n); /* this does the right thing for * internal clients too */ @@ -2998,26 +2904,22 @@ jack_rechain_graph (jack_engine_t *engine) subgraph_client = client; subgraph_client->subgraph_start_fd = jack_get_fifo_fd (engine, n); - if (engine->verbose) { - fprintf(stderr, "client %s: " - "start_fd=%d, execution" - "_order=%lu.\n", - subgraph_client-> - control->name, - subgraph_client-> - subgraph_start_fd, n); - } + VERBOSE (engine, "client %s: " + "start_fd=%d, execution" + "_order=%lu.\n", + subgraph_client-> + control->name, + subgraph_client-> + subgraph_start_fd, n); } else { - if (engine->verbose) { - fprintf(stderr, "client %s: in" - " subgraph after %s, " - "execution_order=" - "%lu.\n", - client->control->name, - subgraph_client-> - control->name, n); - } + VERBOSE (engine, "client %s: in" + " subgraph after %s, " + "execution_order=" + "%lu.\n", + client->control->name, + subgraph_client-> + control->name, n); subgraph_client->subgraph_wait_fd = -1; } @@ -3036,17 +2938,13 @@ jack_rechain_graph (jack_engine_t *engine) if (subgraph_client) { subgraph_client->subgraph_wait_fd = jack_get_fifo_fd (engine, n); - if (engine->verbose) { - fprintf(stderr, "client %s: wait_fd=%d, " - "execution_order=%lu (last client).\n", - subgraph_client->control->name, - subgraph_client->subgraph_wait_fd, n); - } + VERBOSE (engine, "client %s: wait_fd=%d, " + "execution_order=%lu (last client).\n", + subgraph_client->control->name, + subgraph_client->subgraph_wait_fd, n); } - if (engine->verbose) { - fprintf(stderr, "-- jack_rechain_graph()\n"); - } + VERBOSE (engine, "-- jack_rechain_graph()\n"); return err; } @@ -3515,18 +3413,16 @@ jack_port_do_connect (jack_engine_t *engine, if (dstport->connections && !dstport->shared->has_mixdown) { jack_port_type_info_t *port_type = - jack_global_port_type_info (engine, dstport); + jack_port_type_info (engine, dstport); jack_error ("cannot make multiple connections to a port of" " type [%s]", port_type->type_name); free (connection); jack_unlock_graph (engine); return -1; } else { - if (engine->verbose) { - fprintf (stderr, "connect %s and %s\n", - srcport->shared->name, - dstport->shared->name); - } + VERBOSE (engine, "connect %s and %s\n", + srcport->shared->name, + dstport->shared->name); dstport->connections = jack_slist_prepend (dstport->connections, connection); @@ -3571,11 +3467,9 @@ jack_port_disconnect_internal (jack_engine_t *engine, if (connect->source == srcport && connect->destination == dstport) { - if (engine->verbose) { - fprintf (stderr, "DIS-connect %s and %s\n", - srcport->shared->name, - dstport->shared->name); - } + VERBOSE (engine, "DIS-connect %s and %s\n", + srcport->shared->name, + dstport->shared->name); srcport->connections = jack_slist_remove (srcport->connections, @@ -3629,10 +3523,8 @@ jack_port_do_disconnect_all (jack_engine_t *engine, return -1; } - if (engine->verbose) { - fprintf (stderr, "clear connections for %s\n", - engine->internal_ports[port_id].shared->name); - } + VERBOSE (engine, "clear connections for %s\n", + engine->internal_ports[port_id].shared->name); jack_lock_graph (engine); jack_port_clear_connections (engine, &engine->internal_ports[port_id]); @@ -3694,6 +3586,7 @@ jack_get_fifo_fd (jack_engine_t *engine, unsigned int which_fifo) strerror (errno)); return -1; } + } else { jack_error ("cannot check on FIFO %d\n", which_fifo); return -1; @@ -3759,7 +3652,6 @@ jack_clear_fifos (jack_engine_t *engine) static int jack_use_driver (jack_engine_t *engine, jack_driver_t *driver) { - /* precondition: caller has driver_lock */ if (engine->driver) { engine->driver->detach (engine->driver, engine); engine->driver = 0; @@ -3777,6 +3669,7 @@ jack_use_driver (jack_engine_t *engine, jack_driver_t *driver) return 0; } + /* PORT RELATED FUNCTIONS */ @@ -3811,14 +3704,14 @@ jack_port_release (jack_engine_t *engine, jack_port_internal_t *port) port->shared->in_use = 0; if (port->buffer_info) { - jack_port_type_internal_t *info = - jack_local_port_type_info (engine, port); - pthread_mutex_lock (&info->buffer_lock); - info->buffer_freelist = - jack_slist_prepend (info->buffer_freelist, + jack_port_buffer_list_t *blist = + jack_port_buffer_list (engine, port); + pthread_mutex_lock (&blist->lock); + blist->freelist = + jack_slist_prepend (blist->freelist, port->buffer_info); port->buffer_info = NULL; - pthread_mutex_unlock (&info->buffer_lock); + pthread_mutex_unlock (&blist->lock); } pthread_mutex_unlock (&engine->port_lock); } @@ -3907,10 +3800,8 @@ jack_port_do_register (jack_engine_t *engine, jack_request_t *req) jack_port_registration_notify (engine, port_id, TRUE); jack_unlock_graph (engine); - if (engine->verbose) { - fprintf (stderr, "registered port %s, offset = %u\n", - shared->name, (unsigned int)shared->offset); - } + VERBOSE (engine, "registered port %s, offset = %u\n", + shared->name, (unsigned int)shared->offset); req->x.port_info.port_id = port_id; @@ -4092,8 +3983,8 @@ jack_port_registration_notify (jack_engine_t *engine, int jack_port_assign_buffer (jack_engine_t *engine, jack_port_internal_t *port) { - jack_port_type_internal_t *pti = - jack_local_port_type_info (engine, port); + jack_port_buffer_list_t *blist = + jack_port_buffer_list (engine, port); jack_port_buffer_info_t *bi; if (port->shared->flags & JackPortIsInput) { @@ -4101,30 +3992,29 @@ jack_port_assign_buffer (jack_engine_t *engine, jack_port_internal_t *port) return 0; } - pthread_mutex_lock (&pti->buffer_lock); + pthread_mutex_lock (&blist->lock); - if (pti->buffer_freelist == NULL) { + if (blist->freelist == NULL) { jack_port_type_info_t *port_type = - jack_global_port_type_info (engine, port); + jack_port_type_info (engine, port); jack_error ("all %s port buffers in use!", port_type->type_name); - pthread_mutex_unlock (&pti->buffer_lock); + pthread_mutex_unlock (&blist->lock); return -1; } - bi = (jack_port_buffer_info_t *) pti->buffer_freelist->data; - pti->buffer_freelist = jack_slist_remove (pti->buffer_freelist, bi); + bi = (jack_port_buffer_info_t *) blist->freelist->data; + blist->freelist = jack_slist_remove (blist->freelist, bi); port->shared->offset = bi->offset; port->buffer_info = bi; - pthread_mutex_unlock (&pti->buffer_lock); + pthread_mutex_unlock (&blist->lock); return 0; } static jack_port_internal_t * jack_get_port_by_name (jack_engine_t *engine, const char *name) - { jack_port_id_t id; diff --git a/jackd/jackd.1.in b/jackd/jackd.1.in index f92bc66..cb6e6b5 100644 --- a/jackd/jackd.1.in +++ b/jackd/jackd.1.in @@ -1,4 +1,4 @@ -.TH JACKD "1" @VERSION@ "Sept. 2003" +.TH JACKD "1" @VERSION@ "October 2003" .SH NAME jackd, jackstart \- JACK Audio Connection Kit sound server .SH SYNOPSYS @@ -87,7 +87,7 @@ is \fBalsa\fR (see below). .TP \fB\-d, \-\-device \fIname\fR .br -The ALSA pcm device \fIname\fR to use ("default" if none specified). +The ALSA pcm device \fIname\fR to use ("hw:0" if none specified). .TP \fB\-r, \-\-rate \fIint\fR Specify the sample rate. The default is 48000. @@ -110,11 +110,11 @@ the JACK buffer size in bytes. The JACK output latency in seconds is \fB\-D, \-\-duplex\fR Provide both capture and playback ports (the default). .TP -\fB\-C, \-\-capture\fR -Provide only capture ports. +\fB\-C, \-\-capture\fR [ \fIname\fR ] +Provide only capture ports. Optionally set device name. .TP -\fB\-P, \-\-playback\fR -Provide only playback ports. +\fB\-P, \-\-playback\fR [ \fIname\fR ] +Provide only playback ports. Optionally set device name. .TP \fB\-H, \-\-hwmon\fR .br @@ -151,6 +151,19 @@ to disconnect unresponsive ports when running without \fB\-z, --dither [rectangular,triangular,shaped,none] Set dithering mode. If \fBnone\fR or unspecified, dithering is off. Only the first letter of the mode name is required. +.TP +\fB\-i, \-\-inchannels \fIint\fR +.br +Number of capture channels. Default is maximum supported by hardware. +.TP +\fB\-o, \-\-outchannels \fIint\fR +.br +Number of playback channels. Default is maximum supported by hardware. +.TP +\fB\-S, \-\-shorts +.br +Try to configure card for 16-bit samples first, only trying 32-bits if +unsuccessful. Default is to prefer 32-bit samples. .SH EXAMPLES .PP Print usage message for options specific to the \fBalsa\fR driver. @@ -193,5 +206,5 @@ Please send bug reports to <\fBjackit-devel@lists.sourceforge.net\fR>. Paul Davis and others. .PP Manpage originally written by Stefan Schwandter - and later adapted by Jack O'Quin +, later adapted by Jack O'Quin . diff --git a/jackd/jackd.c b/jackd/jackd.c index 55683ad..38ef615 100644 --- a/jackd/jackd.c +++ b/jackd/jackd.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "bsd"; -*- */ /* Copyright (C) 2001-2003 Paul Davis @@ -26,6 +27,9 @@ #include #include #include +#include +#include +#include #include @@ -33,6 +37,7 @@ #include #include #include +#include #ifdef USE_CAPABILITIES @@ -46,30 +51,15 @@ static struct stat pipe_stat; #endif +static JSList * drivers = NULL; static sigset_t signals; static jack_engine_t *engine = 0; -static int jackd_pid; static int realtime = 0; static int realtime_priority = 10; -static int with_fork = 1; static int verbose = 0; static int asio_mode = 0; static int client_timeout = 500; /* msecs */ -typedef struct { - pid_t pid; - int argc; - char **argv; -} waiter_arg_t; - -static void -signal_handler (int sig) -{ - /* this is used by the parent (waiter) process */ - fprintf (stderr, "jackd: signal %d received\n", sig); - kill (jackd_pid, SIGTERM); -} - static void do_nothing_handler (int sig) { @@ -83,69 +73,20 @@ do_nothing_handler (int sig) write (1, buf, strlen (buf)); } -static void * -jack_engine_waiter_thread (void *arg) -{ - waiter_arg_t *warg = (waiter_arg_t *) arg; - - pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - - if ((engine = jack_engine_new (realtime, realtime_priority, - verbose, client_timeout)) == 0) { - fprintf (stderr, "cannot create engine\n"); - kill (warg->pid, SIGTERM); - return 0; - } - - if (warg->argc) { - - fprintf (stderr, "loading driver ..\n"); - - if (jack_engine_load_driver (engine, warg->argc, warg->argv)) { - fprintf (stderr, "cannot load driver module %s\n", - warg->argv[0]); - kill (warg->pid, SIGTERM); - return 0; - } - - } else { - - fprintf (stderr, "No driver specified ... hmm. JACK won't do" - " anything when run like this.\n"); - } - - if (asio_mode) { - jack_set_asio_mode (engine, TRUE); - } - - fprintf (stderr, "starting engine\n"); - - if (jack_run (engine)) { - fprintf (stderr, "cannot start main JACK thread\n"); - kill (warg->pid, SIGTERM); - return 0; - } - - jack_wait (engine); - - fprintf (stderr, "telling signal thread that the engine is done\n"); - kill (warg->pid, SIGHUP); - - return 0; /* nobody cares what this returns */ -} - -static void -jack_main (int argc, char **argv) +static int +jack_main (jack_driver_desc_t * driver_desc, JSList * driver_params) { int sig; int i; - pthread_t waiter_thread; - waiter_arg_t warg; sigset_t allsignals; struct sigaction action; + int waiting; - /* remove any existing files from a previous instance */ - jack_cleanup_files (); + /* ensure that we are in our own process group so that + kill (SIG, -pgrp) does the right thing. + */ + + setsid (); pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); @@ -182,55 +123,44 @@ jack_main (int argc, char **argv) sigaddset(&signals, SIGPIPE); sigaddset(&signals, SIGTERM); sigaddset(&signals, SIGUSR1); - -#if 0 - /* POSIX defines these as "synchronous" signals, which must be - * delivered to the offending thread. I think it's a bad idea - * to block them. (JOQ) */ - sigaddset(&signals, SIGILL); - sigaddset(&signals, SIGTRAP); - sigaddset(&signals, SIGABRT); - sigaddset(&signals, SIGIOT); - sigaddset(&signals, SIGFPE); - sigaddset(&signals, SIGSEGV); -#endif + sigaddset(&signals, SIGUSR2); /* all child threads will inherit this mask unless they - * explicitly reset it */ + * explicitly reset it + */ + pthread_sigmask (SIG_BLOCK, &signals, 0); - /* what we'd really like to do here is to be able to wait for - either the engine to stop or a POSIX signal, whichever - arrives sooner. but there is no mechanism to do that, so - instead we create a thread to wait for the engine to - finish, and here we stop and wait for any (reasonably - likely) POSIX signal. - - if the engine finishes first, the waiter thread will tell - us about it via a signal. + /* get the engine/driver started */ - if a signal arrives, we'll stop the engine and then exit. + if ((engine = jack_engine_new (realtime, realtime_priority, + verbose, client_timeout, + getpid(), drivers)) == 0) { + fprintf (stderr, "cannot create engine\n"); + return -1; + } - in normal operation, our parent process will be waiting for - us and will cleanup. - */ + fprintf (stderr, "loading driver ..\n"); + + if (jack_engine_load_driver (engine, driver_desc, driver_params)) { + fprintf (stderr, "cannot load driver module %s\n", driver_desc->name); + return -1; + } - warg.pid = getpid(); - warg.argc = argc; - warg.argv = argv; + if (asio_mode) { + jack_set_asio_mode (engine, TRUE); + } - if (pthread_create (&waiter_thread, 0, jack_engine_waiter_thread, - &warg)) { - fprintf (stderr, - "jackd: cannot create engine waiting thread\n"); - return; + if (engine->driver->start (engine->driver) != 0) { + jack_error ("cannot start driver"); + return -1; } /* install a do-nothing handler because otherwise pthreads behaviour is undefined when we enter sigwait. */ - sigfillset (&allsignals); + sigfillset (&allsignals); action.sa_handler = do_nothing_handler; action.sa_mask = allsignals; action.sa_flags = SA_RESTART|SA_RESETHAND; @@ -245,15 +175,23 @@ jack_main (int argc, char **argv) fprintf (stderr, "%d waiting for signals\n", getpid()); } - while(1) { + waiting = TRUE; + + while (waiting) { sigwait (&signals, &sig); fprintf (stderr, "jack main caught signal %d\n", sig); - if (sig == SIGUSR1) { + switch (sig) { + case SIGUSR1: jack_dump_configuration(engine, 1); - } else { - /* continue to kill engine */ + break; + case SIGUSR2: + /* driver exit */ + waiting = FALSE; + break; + default: + waiting = FALSE; break; } } @@ -267,10 +205,131 @@ jack_main (int argc, char **argv) sigprocmask (SIG_UNBLOCK, &signals, 0); } - pthread_cancel (waiter_thread); jack_engine_delete (engine); + return 1; +} - return; +static jack_driver_desc_t * +jack_drivers_get_descriptor (JSList * drivers, const char * sofile) +{ + jack_driver_desc_t * descriptor, * other_descriptor; + JackDriverDescFunction so_get_descriptor; + JSList * node; + void * dlhandle; + char * filename; + const char * dlerr; + int err; + + filename = malloc (strlen (ADDON_DIR) + 1 + strlen (sofile) + 1); + sprintf (filename, "%s/%s", ADDON_DIR, sofile); + + if (verbose) + printf ("getting driver descriptor from %s\n", filename); + + dlhandle = dlopen (filename, RTLD_NOW|RTLD_GLOBAL); + if (!dlhandle) { + jack_error ("could not open driver .so '%s': %s\n", filename, dlerror ()); + free (filename); + return NULL; + } + + dlerror (); + + so_get_descriptor = (JackDriverDescFunction) + dlsym (dlhandle, "driver_get_descriptor"); + + dlerr = dlerror (); + if (dlerr) { + dlclose (dlhandle); + free (filename); + return NULL; + } + + descriptor = so_get_descriptor (); + if (!descriptor) { + jack_error ("driver from '%s' returned NULL descriptor\n", filename); + dlclose (dlhandle); + free (filename); + return NULL; + } + + err = dlclose (dlhandle); + if (err) { + jack_error ("error closing driver .so '%s': %s\n", filename, dlerror ()); + } + + + /* check it doesn't exist already */ + for (node = drivers; node; node = jack_slist_next (node)) { + other_descriptor = (jack_driver_desc_t *) node->data; + + if (strcmp (descriptor->name, other_descriptor->name) == 0) { + jack_error ("the drivers in '%s' and '%s' both have the name '%s'; using the first\n", + other_descriptor->file, filename, other_descriptor->name); + /* FIXME: delete the descriptor */ + free (filename); + return NULL; + } + } + + strncpy (descriptor->file, filename, PATH_MAX); + free (filename); + + return descriptor; + +} + +static JSList * +jack_drivers_load () +{ + struct dirent * dir_entry; + DIR * dir_stream; + const char * ptr; + int err; + JSList * driver_list = NULL; + jack_driver_desc_t * desc; + + /* search through the ADDON_DIR and add get descriptors + from the .so files in it */ + dir_stream = opendir (ADDON_DIR); + if (!dir_stream) { + jack_error ("could not open driver directory %s: %s\n", ADDON_DIR, strerror (errno)); + return NULL; + } + + while ( (dir_entry = readdir (dir_stream)) ) { + /* check the filename is of the right format */ + if (strncmp ("jack_", dir_entry->d_name, 5) != 0) { + continue; + } + + ptr = strrchr (dir_entry->d_name, '.'); + if (!ptr) { + continue; + } + ptr++; + if (strncmp ("so", ptr, 2) != 0) { + continue; + } + + desc = jack_drivers_get_descriptor (drivers, dir_entry->d_name); + if (desc) { + driver_list = jack_slist_append (driver_list, desc); + } + + } + + err = closedir (dir_stream); + if (err) { + jack_error ("error closing driver directory %s: %s\n", ADDON_DIR, strerror (errno)); + } + + if (!driver_list) { + jack_error ("could not find any drivers in %s!\n", ADDON_DIR); + return NULL; + } + + return driver_list; } static void copyright (FILE* file) @@ -295,10 +354,30 @@ static void usage (FILE *file) " -d driver [ ... driver args ... ]\n"); } +static jack_driver_desc_t * +jack_find_driver_descriptor (const char * name) +{ + jack_driver_desc_t * desc; + JSList * node; + + for (node = drivers; node; node = jack_slist_next (node)) { + desc = (jack_driver_desc_t *) node->data; + + if (strcmp (desc->name, name) != 0) { + desc = NULL; + } else { + break; + } + } + + return desc; +} + int main (int argc, char *argv[]) { + jack_driver_desc_t * desc; const char *options = "-ad:D:P:vhVRFl:t:"; struct option long_options[] = { @@ -310,7 +389,6 @@ main (int argc, char *argv[]) { "realtime", 0, 0, 'R' }, { "realtime-priority", 1, 0, 'P' }, { "timeout", 1, 0, 't' }, - { "spoon", 0, 0, 'F' }, { "version", 0, 0, 'V' }, { 0, 0, 0, 0 } }; @@ -319,6 +397,7 @@ main (int argc, char *argv[]) int seen_driver = 0; char *driver_name = 0; char **driver_args = 0; + JSList * driver_params; int driver_nargs = 1; int show_version = 0; int i; @@ -359,7 +438,6 @@ main (int argc, char *argv[]) } } #endif - opterr = 0; while (!seen_driver && (opt = getopt_long (argc, argv, options, long_options, @@ -382,10 +460,6 @@ main (int argc, char *argv[]) verbose = 1; break; - case 'F': - with_fork = 0; - break; - case 'P': realtime_priority = atoi (optarg); break; @@ -427,12 +501,33 @@ main (int argc, char *argv[]) exit (1); } + + drivers = jack_drivers_load (); + if (!drivers) + { + fprintf (stderr, "jackd: no drivers found; exiting\n"); + exit (1); + } + + desc = jack_find_driver_descriptor (driver_name); + if (!desc) + { + fprintf (stderr, "jackd: unknown driver '%s'\n", driver_name); + exit (1); + } + if (optind < argc) { driver_nargs = 1 + argc - optind; } else { driver_nargs = 1; } + if (driver_nargs == 0) { + fprintf (stderr, "No driver specified ... hmm. JACK won't do" + " anything when run like this.\n"); + return -1; + } + driver_args = (char **) malloc (sizeof (char *) * driver_nargs); driver_args[0] = driver_name; @@ -440,41 +535,22 @@ main (int argc, char *argv[]) driver_args[i] = argv[optind++]; } - copyright (stdout); - - if (!with_fork) { - - /* This is really here so that we can run gdb easily */ - jack_main (driver_nargs, driver_args); + i = jack_parse_driver_params (desc, driver_nargs, driver_args, &driver_params); + if (i) + exit (0); - } else { - - int pid = fork (); - - if (pid < 0) { - - fprintf (stderr, "could not fork jack server (%s)", - strerror (errno)); - exit (1); - - } else if (pid == 0) { - - jack_main (driver_nargs, driver_args); - - } else { - jackd_pid = pid; + copyright (stdout); - signal (SIGHUP, signal_handler); - signal (SIGINT, signal_handler); - signal (SIGQUIT, signal_handler); - signal (SIGTERM, signal_handler); + jack_cleanup_shm (); + jack_cleanup_files (); - waitpid (pid, NULL, 0); - } - } + jack_main (desc, driver_params); jack_cleanup_shm (); jack_cleanup_files (); - return 0; + exit (0); } + + + diff --git a/libjack/ChangeLog b/libjack/ChangeLog index f442e78..8e474e3 100644 --- a/libjack/ChangeLog +++ b/libjack/ChangeLog @@ -1,3 +1,14 @@ +2003-10-15 Paul Davis + + * new ring buffer interface: + +2003-10-07 Paul Davis + + * new function jack_set_freewheel(). + + No compatibility issues: this introduces new functionality to + JACK and doesn't alter any existing functionality. + 2003-09-18 Jack O'Quin * new function jack_set_buffer_size(). diff --git a/libjack/Makefile.am b/libjack/Makefile.am index 6ff53c8..71b8af6 100644 --- a/libjack/Makefile.am +++ b/libjack/Makefile.am @@ -6,6 +6,7 @@ SOURCE_FILES = \ driver.c \ pool.c \ port.c \ + ringbuffer.c \ timestamps.c \ transclient.c diff --git a/libjack/client.c b/libjack/client.c index bb03481..d5bfbdb 100644 --- a/libjack/client.c +++ b/libjack/client.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "bsd"; -*- */ /* Copyright (C) 2001-2003 Paul Davis @@ -138,7 +139,6 @@ jack_client_t * jack_client_alloc () { jack_client_t *client; - jack_port_type_id_t ptid; client = (jack_client_t *) malloc (sizeof (jack_client_t)); client->pollfd = (struct pollfd *) malloc (sizeof (struct pollfd) * 2); @@ -149,30 +149,29 @@ jack_client_alloc () client->graph_next_fd = -1; client->ports = NULL; client->engine = NULL; - client->control = 0; + client->control = NULL; client->thread_ok = FALSE; client->first_active = TRUE; client->on_shutdown = NULL; - client->n_port_types = 0; - for (ptid = 0; ptid < JACK_MAX_PORT_TYPES; ++ptid) { - client->port_segment[ptid].shm_name[0] = '\0'; - client->port_segment[ptid].address = NULL; - client->port_segment[ptid].size = 0; - } + client->port_segment = NULL; return client; } jack_client_t * -jack_client_alloc_internal (jack_client_control_t *cc, jack_control_t *ec) +jack_client_alloc_internal (jack_client_control_t *cc, jack_engine_t* engine) { jack_client_t* client; client = jack_client_alloc (); + client->control = cc; - client->engine = ec; + client->engine = engine->control; + client->n_port_types = client->engine->n_port_types; + client->port_segment = &engine->port_segment[0]; + return client; } @@ -459,35 +458,67 @@ jack_request_client (ClientType type, const char* client_name, const char* so_na return -1; } -void -jack_attach_port_segment (jack_client_t *client, shm_name_t shm_name, - jack_port_type_id_t ptid, jack_shmsize_t size) +int +jack_attach_port_segment (jack_client_t *client, jack_port_type_id_t ptid) { - int shmid; - void *addr; - /* Lookup, attach and register the port/buffer segments in use - * right now. */ + * right now. + */ + if (client->control->type != ClientExternal) { jack_error("Only external clients need attach port segments"); abort(); } - /* release any previous segment */ - if (client->port_segment[ptid].size) { - jack_release_shm (client->port_segment[ptid].address, - client->port_segment[ptid].size); - client->port_segment[ptid].size = 0; + /* make sure we have space to store the port + segment information. + */ + + if (ptid >= client->n_port_types) { + + client->port_segment = (jack_shm_info_t*) + realloc (client->port_segment, + sizeof (jack_shm_info_t) * (ptid+1)); + + memset (&client->port_segment[client->n_port_types], + 0, + sizeof (jack_shm_info_t) * + (ptid - client->n_port_types)); + + client->n_port_types = ptid + 1; + + } else { + + /* release any previous segment */ + + jack_release_shm (&client->port_segment[ptid]); } - if ((addr = jack_get_shm (shm_name, size, O_RDWR, 0, - (PROT_READ|PROT_WRITE), - &shmid)) == MAP_FAILED) { + /* get the index into the shm registry */ + + client->port_segment[ptid].index = + client->engine->port_types[ptid].shm_registry_index; + + /* attach the relevant segment */ + + if (jack_attach_shm (&client->port_segment[ptid])) { jack_error ("cannot attach port segment shared memory" " (%s)", strerror (errno)); + return -1; } - jack_client_set_port_segment (client, shm_name, ptid, size, addr); + /* The first chunk of the audio port segment will be set by + * the engine to be a zero-filled buffer. This hasn't been + * done yet, but it will happen before the process cycle + * (re)starts. + */ + + if (ptid == JACK_AUDIO_PORT_TYPE) { + jack_zero_filled_buffer = + jack_shm_addr (&client->port_segment[ptid]); + } + + return 0; } jack_client_t * @@ -497,15 +528,13 @@ jack_client_new (const char *client_name) int ev_fd = -1; jack_client_connect_result_t res; jack_client_t *client; - void *addr; - int shmid; - jack_port_type_info_t* type_info; jack_port_type_id_t ptid; /* external clients need this initialized; internal clients will use the setup in the server's address space. */ jack_init_time (); + jack_initialize_shm (); if (jack_request_client (ClientExternal, client_name, "", "", &res, &req_fd)) { @@ -521,61 +550,44 @@ jack_client_new (const char *client_name) client->pollfd[1].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; /* attach the engine control/info block */ - if ((addr = jack_get_shm (res.control_shm_name, res.control_size, - O_RDWR, 0, (PROT_READ|PROT_WRITE), - &shmid)) == MAP_FAILED) { + client->engine_shm = res.engine_shm; + if (jack_attach_shm (&client->engine_shm)) { jack_error ("cannot attached engine control shared memory" " segment"); goto fail; } - - client->engine = (jack_control_t *) addr; + client->engine = (jack_control_t *) jack_shm_addr (&client->engine_shm); /* now attach the client control block */ - if ((addr = jack_get_shm (res.client_shm_name, - sizeof (jack_client_control_t), O_RDWR, - 0, (PROT_READ|PROT_WRITE), - &shmid)) == MAP_FAILED) { + client->control_shm = res.client_shm; + if (jack_attach_shm (&client->control_shm)) { jack_error ("cannot attached client control shared memory" " segment"); goto fail; } - - client->control = (jack_client_control_t *) addr; + + client->control = (jack_client_control_t *) jack_shm_addr (&client->control_shm); /* nobody else needs to access this shared memory any more, so - destroy it. because we have our own link to it, it won't - vanish till we exit. - */ - jack_destroy_shm (res.client_shm_name); - - /* read incoming port type information so that we can get - shared memory information for each one. + destroy it. because we have our own attachment to it, it won't + vanish till we exit (and release it). */ - type_info = (jack_port_type_info_t *) - malloc (sizeof (jack_port_type_info_t) * res.n_port_types); - - if (read (req_fd, type_info, - sizeof (jack_port_type_info_t) * res.n_port_types) != - sizeof (jack_port_type_info_t) * res.n_port_types) { - jack_error ("cannot read port type information during client" - " connection"); - free (type_info); - goto fail; - } + jack_destroy_shm (&client->control_shm); - client->n_port_types = res.n_port_types; - for (ptid = 0; ptid < res.n_port_types; ++ptid) { - jack_attach_port_segment (client, - type_info[ptid].shm_info.shm_name, - ptid, type_info[ptid].shm_info.size); + client->n_port_types = client->engine->n_port_types; + client->port_segment = (jack_shm_info_t *) malloc (sizeof (jack_shm_info_t) * + client->n_port_types); + + for (ptid = 0; ptid < client->n_port_types; ++ptid) { + client->port_segment[ptid].index = + client->engine->port_types[ptid].shm_registry_index; + jack_attach_port_segment (client, ptid); } - free (type_info); - /* set up the client so that it does the right thing for an - * external client */ + * external client + */ client->control->deliver_request = oop_client_deliver_request; client->control->deliver_arg = client; @@ -606,11 +618,12 @@ jack_client_new (const char *client_name) fail: if (client->engine) { - munmap ((char *) client->engine, res.control_size); + jack_release_shm (&client->engine_shm); + client->engine = 0; } if (client->control) { - munmap ((char *) client->control, - sizeof (jack_client_control_t)); + jack_release_shm (&client->control_shm); + client->control = 0; } if (req_fd >= 0) { close (req_fd); @@ -655,23 +668,59 @@ jack_internal_client_close (const char *client_name) return; } -void -jack_client_set_port_segment (jack_client_t *client, shm_name_t shm_name, - jack_port_type_id_t ptid, jack_shmsize_t size, - void *addr) +int +jack_drop_real_time_scheduling (pthread_t thread) { - client->port_segment[ptid].address = addr; - client->port_segment[ptid].size = size; - strncpy (client->port_segment[ptid].shm_name, - shm_name, sizeof (shm_name_t)); + struct sched_param rtparam; + int x; + + memset (&rtparam, 0, sizeof (rtparam)); + rtparam.sched_priority = 0; + + if ((x = pthread_setschedparam (thread, SCHED_OTHER, &rtparam)) != 0) { + jack_error ("cannot switch to normal scheduling priority(%s)\n", strerror (errno)); + return -1; + } + return 0; +} - /* The first chunk of the audio port segment will be set by - * the engine to be a zero-filled buffer. This hasn't been - * done yet, but it will happen before the process cycle - * (re)starts. */ - if (ptid == JACK_AUDIO_PORT_TYPE) { - jack_zero_filled_buffer = client->port_segment[ptid].address; +int +jack_acquire_real_time_scheduling (pthread_t thread, int priority) +{ + struct sched_param rtparam; + int x; + + memset (&rtparam, 0, sizeof (rtparam)); + rtparam.sched_priority = priority; + + if ((x = pthread_setschedparam (thread, SCHED_FIFO, &rtparam)) != 0) { + jack_error ("cannot use real-time scheduling (FIFO/%d) " + "(%d: %s)", rtparam.sched_priority, x, + strerror (errno)); + return -1; } + return 0; +} + +int +jack_set_freewheel (jack_client_t* client, int onoff) +{ + jack_request_t request; + request.type = onoff ? FreeWheel : StopFreeWheel; + return jack_client_deliver_request (client, &request); +} + +void +jack_start_freewheel (jack_client_t* client) +{ + jack_drop_real_time_scheduling (client->thread); +} + +void +jack_stop_freewheel (jack_client_t* client) +{ + jack_acquire_real_time_scheduling (client->thread, + client->engine->client_priority); } static void * @@ -722,15 +771,21 @@ jack_client_thread (void *arg) break; } - pthread_testcancel(); - /* get an accurate timestamp on waking from poll for a - * process() cycle. */ + * process() cycle. + */ + if (client->pollfd[1].revents & POLLIN) { control->awake_at = jack_get_microseconds(); } - if (client->pollfd[0].revents & ~POLLIN || + DEBUG ("pfd[0].revents = 0x%x pfd[1].revents = 0x%x", + client->pollfd[0].revents, + client->pollfd[1].revents); + + pthread_testcancel(); + + if ((client->pollfd[0].revents & ~POLLIN) || client->control->dead) { goto zombie; } @@ -806,10 +861,15 @@ jack_client_thread (void *arg) break; case AttachPortSegment: - jack_attach_port_segment (client, - event.x.shm_name, - event.y.ptid, - event.z.size); + jack_attach_port_segment (client, event.y.ptid); + break; + + case StartFreewheel: + jack_start_freewheel (client); + break; + + case StopFreewheel: + jack_stop_freewheel (client); break; } @@ -825,6 +885,10 @@ jack_client_thread (void *arg) } } + if (client->pollfd[1].revents & ~POLLIN) { + goto zombie; + } + if (client->pollfd[1].revents & POLLIN) { #ifdef WITH_TIMESTAMPS @@ -929,7 +993,8 @@ jack_client_thread (void *arg) jack_error ("zombified - exiting from JACK"); jack_client_close (client); /* Need a fix : possibly make client crash if - * zombified without shutdown handler */ + * zombified without shutdown handler + */ } pthread_exit (0); @@ -1023,7 +1088,7 @@ jack_start_thread (jack_client_t *client) #endif if (client->engine->real_time) { - + /* Get the client thread to run as an RT-FIFO scheduled thread of appropriate priority. */ @@ -1300,23 +1365,29 @@ jack_client_close (jack_client_t *client) if (client->control->type == ClientExternal) { /* stop the thread that communicates with the jack - * server, only if it was actually running */ + * server, only if it was actually running + */ if (client->thread_ok){ pthread_cancel (client->thread); pthread_join (client->thread, &status); } - munmap ((char *) client->control, - sizeof (jack_client_control_t)); - munmap ((char *) client->engine, - sizeof (jack_control_t)); + if (client->port_segment) { + free (client->port_segment); + } + + if (client->control) { + jack_release_shm (&client->control_shm); + client->control = NULL; + } + if (client->engine) { + jack_release_shm (&client->engine_shm); + client->engine = NULL; + } for (ptid = 0; ptid < client->n_port_types; ++ptid) { - if (client->port_segment[ptid].size) { - munmap (client->port_segment[ptid].address, - client->port_segment[ptid].size); - } + jack_release_shm (&client->port_segment[ptid]); } if (client->graph_wait_fd) { diff --git a/libjack/driver.c b/libjack/driver.c index cb5ea04..878f242 100644 --- a/libjack/driver.c +++ b/libjack/driver.c @@ -1,3 +1,4 @@ +/* -*- mode: c; c-file-style: "linux"; -*- */ /* Copyright (C) 2001-2003 Paul Davis @@ -23,21 +24,17 @@ #include #include #include +#include +#include #include #include #include +#include static int dummy_attach (jack_driver_t *drv, jack_engine_t *eng) { return 0; } static int dummy_detach (jack_driver_t *drv, jack_engine_t *eng) { return 0; } -static jack_nframes_t dummy_wait (jack_driver_t *drv, int fd, - int *status, float *delayed_usecs) -{ - *status = 0; - *delayed_usecs = 0; - return 0; -} static int dummy_write (jack_driver_t *drv, jack_nframes_t nframes) { return 0; } static int dummy_read (jack_driver_t *drv, jack_nframes_t nframes) { return 0; } @@ -55,7 +52,6 @@ jack_driver_init (jack_driver_t *driver) driver->attach = dummy_attach; driver->detach = dummy_detach; - driver->wait = dummy_wait; driver->write = dummy_write; driver->read = dummy_read; driver->null_cycle = dummy_null_cycle; @@ -63,3 +59,201 @@ jack_driver_init (jack_driver_t *driver) driver->start = dummy_start; driver->stop = dummy_stop; } + + + +/**************************** + *** Non-Threaded Drivers *** + ****************************/ + +static int dummy_nt_run_cycle (jack_driver_nt_t *drv) { return 0; } +static int dummy_nt_attach (jack_driver_nt_t *drv) { return 0; } +static int dummy_nt_detach (jack_driver_nt_t *drv) { return 0; } + + +/* + * These are used in driver->nt_run for controlling whether or not + * driver->engine->driver_exit() gets called (EXIT = call it, PAUSE = don't) + */ +#define DRIVER_NT_RUN 0 +#define DRIVER_NT_EXIT 1 +#define DRIVER_NT_PAUSE 2 + +static int +jack_driver_nt_attach (jack_driver_nt_t * driver, jack_engine_t * engine) +{ + driver->engine = engine; + return driver->nt_attach (driver); +} + +static int +jack_driver_nt_detach (jack_driver_nt_t * driver, jack_engine_t * engine) +{ + int ret; + + ret = driver->nt_detach (driver); + driver->engine = NULL; + + return ret; +} + +static int +jack_driver_nt_become_real_time (jack_driver_nt_t* driver) +{ + if (jack_acquire_real_time_scheduling (driver->nt_thread, + driver->engine->rtpriority)) { + return -1; + } + + if (mlockall (MCL_CURRENT | MCL_FUTURE) != 0) { + jack_error ("cannot lock down memory for RT thread (%s)", + strerror (errno)); + return -1; + } + + return 0; +} + + +static void * +jack_driver_nt_thread (void * arg) +{ + jack_driver_nt_t * driver = (jack_driver_nt_t *) arg; + int rc = 0; + int run; + + if (driver->engine->control->real_time) + jack_driver_nt_become_real_time (driver); + + pthread_mutex_lock (&driver->nt_run_lock); + + while ( (run = driver->nt_run) == DRIVER_NT_RUN) { + pthread_mutex_unlock (&driver->nt_run_lock); + + rc = driver->nt_run_cycle (driver); + if (rc) { + jack_error ("DRIVER NT: could not run driver cycle"); + goto out; + } + + pthread_mutex_lock (&driver->nt_run_lock); + } + + pthread_mutex_unlock (&driver->nt_run_lock); + + out: + if (rc || run == DRIVER_NT_EXIT) + driver->engine->driver_exit (driver->engine); + + pthread_exit (NULL); +} + +static int +jack_driver_nt_start (jack_driver_nt_t * driver) +{ + int err; + + err = driver->nt_start (driver); + if (err) { + jack_error ("DRIVER NT: could not start driver"); + return err; + } + + driver->nt_run = DRIVER_NT_RUN; + + err = pthread_create (&driver->nt_thread, NULL, + jack_driver_nt_thread, driver); + if (err) { + jack_error ("DRIVER NT: could not start driver thread!"); + driver->nt_stop (driver); + return err; + } + + return 0; +} + +static int +jack_driver_nt_do_stop (jack_driver_nt_t * driver, int run) +{ + int err; + + pthread_mutex_lock (&driver->nt_run_lock); + driver->nt_run = run; + pthread_mutex_unlock (&driver->nt_run_lock); + + err = pthread_join (driver->nt_thread, NULL); + if (err) { + jack_error ("DRIVER NT: error waiting for driver thread: %s", + strerror (err)); + return err; + } + + err = driver->nt_stop (driver); + if (err) { + jack_error ("DRIVER NT: error stopping driver"); + return err; + } + + return 0; +} + +static int +jack_driver_nt_stop (jack_driver_nt_t * driver) +{ + return jack_driver_nt_do_stop (driver, DRIVER_NT_EXIT); +} + +static int +jack_driver_nt_bufsize (jack_driver_nt_t * driver, jack_nframes_t nframes) +{ + int err; + int ret; + + err = jack_driver_nt_do_stop (driver, DRIVER_NT_PAUSE); + if (err) { + jack_error ("DRIVER NT: could not stop driver to change buffer size"); + driver->engine->driver_exit (driver->engine); + return err; + } + + ret = driver->nt_bufsize (driver, nframes); + + err = jack_driver_nt_start (driver); + if (err) { + jack_error ("DRIVER NT: could not restart driver during buffer size change"); + driver->engine->driver_exit (driver->engine); + return err; + } + + return ret; +} + +void +jack_driver_nt_init (jack_driver_nt_t * driver) +{ + memset (driver, 0, sizeof (*driver)); + + jack_driver_init ((jack_driver_t *) driver); + + driver->attach = (JackDriverAttachFunction) jack_driver_nt_attach; + driver->detach = (JackDriverDetachFunction) jack_driver_nt_detach; + driver->bufsize = (JackDriverBufSizeFunction) jack_driver_nt_bufsize; + driver->stop = (JackDriverStartFunction) jack_driver_nt_stop; + driver->start = (JackDriverStopFunction) jack_driver_nt_start; + + driver->nt_bufsize = (JackDriverNTBufSizeFunction) dummy_bufsize; + driver->nt_start = (JackDriverNTStartFunction) dummy_start; + driver->nt_stop = (JackDriverNTStopFunction) dummy_stop; + driver->nt_attach = dummy_nt_attach; + driver->nt_detach = dummy_nt_detach; + driver->nt_run_cycle = dummy_nt_run_cycle; + + + pthread_mutex_init (&driver->nt_run_lock, NULL); +} + +void +jack_driver_nt_finish (jack_driver_nt_t * driver) +{ + pthread_mutex_destroy (&driver->nt_run_lock); +} diff --git a/libjack/local.h b/libjack/local.h index 0473de4..5518b0f 100644 --- a/libjack/local.h +++ b/libjack/local.h @@ -6,17 +6,21 @@ struct _jack_client { jack_control_t *engine; jack_client_control_t *control; - struct pollfd *pollfd; - int pollmax; - int graph_next_fd; - int request_fd; + jack_shm_info_t engine_shm; + jack_shm_info_t control_shm; + + struct pollfd* pollfd; + int pollmax; + int graph_next_fd; + int request_fd; + + /* these two are copied from the engine when the + * client is created. + */ jack_port_type_id_t n_port_types; - struct { - shm_name_t shm_name; - void *address; - jack_shmsize_t size; - } port_segment[JACK_MAX_PORT_TYPES]; + jack_shm_info_t* port_segment; + JSList *ports; pthread_t thread; diff --git a/libjack/port.c b/libjack/port.c index cc9af6d..3b9fd0a 100644 --- a/libjack/port.c +++ b/libjack/port.c @@ -76,7 +76,8 @@ jack_port_new (const jack_client_t *client, jack_port_id_t port_id, /* It's our port, so initialize the pointers to port * functions within this address space. These builtin - * definitions can be overridden by the client. */ + * definitions can be overridden by the client. + */ if (ptid == JACK_AUDIO_PORT_TYPE) { @@ -96,8 +97,8 @@ jack_port_new (const jack_client_t *client, jack_port_id_t port_id, port->offset can change if the buffer size or port counts are changed. */ - port->client_segment_base = - (void *) &client->port_segment[ptid].address; + + port->client_segment_base = (void **) &client->port_segment[ptid].attached_at; return port; } @@ -374,6 +375,7 @@ jack_port_get_buffer (jack_port_t *port, jack_nframes_t nframes) if (port->tied) { return jack_port_get_buffer (port->tied, nframes); } + return jack_output_port_buffer (port); } diff --git a/example-clients/ringbuffer.c b/libjack/ringbuffer.c similarity index 85% rename from example-clients/ringbuffer.c rename to libjack/ringbuffer.c index 1561839..0408ece 100644 --- a/example-clients/ringbuffer.c +++ b/libjack/ringbuffer.c @@ -23,18 +23,18 @@ #include #include #include -#include "ringbuffer.h" +#include /* Create a new ringbuffer to hold at least `sz' bytes of data. The actual buffer size is rounded up to the next power of two. */ -ringbuffer_t * -ringbuffer_create (int sz) +jack_ringbuffer_t * +jack_ringbuffer_create (size_t sz) { int power_of_two; - ringbuffer_t *rb; + jack_ringbuffer_t *rb; - rb = malloc (sizeof (ringbuffer_t)); + rb = malloc (sizeof (jack_ringbuffer_t)); for (power_of_two = 1; 1 << power_of_two < sz; power_of_two++); @@ -52,7 +52,7 @@ ringbuffer_create (int sz) /* Free all data associated with the ringbuffer `rb'. */ void -ringbuffer_free (ringbuffer_t * rb) +jack_ringbuffer_free (jack_ringbuffer_t * rb) { if (rb->mlocked) { munlock (rb->buf, rb->size); @@ -63,7 +63,7 @@ ringbuffer_free (ringbuffer_t * rb) /* Lock the data block of `rb' using the system call 'mlock'. */ int -ringbuffer_mlock (ringbuffer_t * rb) +jack_ringbuffer_mlock (jack_ringbuffer_t * rb) { if (mlock (rb->buf, rb->size)) { return -1; @@ -76,7 +76,7 @@ ringbuffer_mlock (ringbuffer_t * rb) safe. */ void -ringbuffer_reset (ringbuffer_t * rb) +jack_ringbuffer_reset (jack_ringbuffer_t * rb) { rb->read_ptr = 0; rb->write_ptr = 0; @@ -87,7 +87,7 @@ ringbuffer_reset (ringbuffer_t * rb) pointer. */ size_t -ringbuffer_read_space (ringbuffer_t * rb) +jack_ringbuffer_read_space (jack_ringbuffer_t * rb) { size_t w, r; @@ -106,7 +106,7 @@ ringbuffer_read_space (ringbuffer_t * rb) pointer. */ size_t -ringbuffer_write_space (ringbuffer_t * rb) +jack_ringbuffer_write_space (jack_ringbuffer_t * rb) { size_t w, r; @@ -126,14 +126,14 @@ ringbuffer_write_space (ringbuffer_t * rb) `dest'. Returns the actual number of bytes copied. */ size_t -ringbuffer_read (ringbuffer_t * rb, char *dest, size_t cnt) +jack_ringbuffer_read (jack_ringbuffer_t * rb, char *dest, size_t cnt) { size_t free_cnt; size_t cnt2; size_t to_read; size_t n1, n2; - if ((free_cnt = ringbuffer_read_space (rb)) == 0) { + if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0) { return 0; } @@ -166,14 +166,14 @@ ringbuffer_read (ringbuffer_t * rb, char *dest, size_t cnt) `src'. Returns the actual number of bytes copied. */ size_t -ringbuffer_write (ringbuffer_t * rb, char *src, size_t cnt) +jack_ringbuffer_write (jack_ringbuffer_t * rb, char *src, size_t cnt) { size_t free_cnt; size_t cnt2; size_t to_write; size_t n1, n2; - if ((free_cnt = ringbuffer_write_space (rb)) == 0) { + if ((free_cnt = jack_ringbuffer_write_space (rb)) == 0) { return 0; } @@ -205,7 +205,7 @@ ringbuffer_write (ringbuffer_t * rb, char *src, size_t cnt) /* Advance the read pointer `cnt' places. */ void -ringbuffer_read_advance (ringbuffer_t * rb, size_t cnt) +jack_ringbuffer_read_advance (jack_ringbuffer_t * rb, size_t cnt) { rb->read_ptr += cnt; rb->read_ptr &= rb->size_mask; @@ -214,7 +214,7 @@ ringbuffer_read_advance (ringbuffer_t * rb, size_t cnt) /* Advance the write pointer `cnt' places. */ void -ringbuffer_write_advance (ringbuffer_t * rb, size_t cnt) +jack_ringbuffer_write_advance (jack_ringbuffer_t * rb, size_t cnt) { rb->write_ptr += cnt; rb->write_ptr &= rb->size_mask; @@ -226,8 +226,8 @@ ringbuffer_write_advance (ringbuffer_t * rb, size_t cnt) length. */ void -ringbuffer_get_read_vector (ringbuffer_t * rb, - ringbuffer_data_t * vec) +jack_ringbuffer_get_read_vector (jack_ringbuffer_t * rb, + jack_ringbuffer_data_t * vec) { size_t free_cnt; size_t cnt2; @@ -270,8 +270,8 @@ ringbuffer_get_read_vector (ringbuffer_t * rb, length. */ void -ringbuffer_get_write_vector (ringbuffer_t * rb, - ringbuffer_data_t * vec) +jack_ringbuffer_get_write_vector (jack_ringbuffer_t * rb, + jack_ringbuffer_data_t * vec) { size_t free_cnt; size_t cnt2; diff --git a/libjack/shm.c b/libjack/shm.c index 7f84fc7..22d8f99 100644 --- a/libjack/shm.c +++ b/libjack/shm.c @@ -22,9 +22,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -35,381 +35,432 @@ #include #include -typedef struct { - shm_name_t name; - char *address; - int shmid; /* only needed for SysV shm */ -} jack_shm_registry_entry_t; +static jack_shm_registry_t* jack_shm_registry; -static jack_shm_registry_entry_t *jack_shm_registry; -static int jack_shm_id_cnt; +static void +jack_shm_lock_registry () +{ + /* XXX magic with semaphores here */ +} -void -jack_register_shm (char *shm_name, char *addr, int id) +static void +jack_shm_unlock_registry () +{ + /* XXX magic with semaphores here */ +} + +jack_shm_registry_t * +jack_get_free_shm_info () { - if (jack_shm_id_cnt < MAX_SHM_ID) { - int entry = jack_shm_id_cnt++; - strncpy (jack_shm_registry[entry].name, shm_name, - sizeof (shm_name_t)); - jack_shm_registry[entry].address = addr; - jack_shm_registry[entry].shmid = id; + jack_shm_registry_t* si = NULL; + int i; + + jack_shm_lock_registry (); + + for (i = 0; i < MAX_SHM_ID; ++i) { + if (jack_shm_registry[i].size == 0) { + break; + } + } + + if (i < MAX_SHM_ID) { + si = &jack_shm_registry[i]; } + + jack_shm_unlock_registry (); + + return si; } -static inline int -jack_lookup_shm (const char *shm_name) +void +jack_release_shm_info (jack_shm_registry_index_t index) { - /***** NOT THREAD SAFE *****/ + if (jack_shm_registry[index].allocator == getpid()) { + jack_shm_lock_registry (); + jack_shm_registry[index].size = 0; + jack_shm_registry[index].allocator = 0; + jack_shm_unlock_registry (); + } +} +void +jack_cleanup_shm () +{ int i; + int destroy; + jack_shm_info_t copy; + + jack_initialize_shm (); + jack_shm_lock_registry (); + + for (i = 0; i < MAX_SHM_ID; i++) { + jack_shm_registry_t* r; + + r = &jack_shm_registry[i]; + copy.index = r->index; + destroy = FALSE; + + if (r->allocator == getpid()) { + + /* allocated by this process, so unattach + and destroy. + */ + + jack_release_shm (©); + destroy = TRUE; + + } else { + + if (kill (r->allocator, 0)) { + if (errno == ESRCH) { + + /* allocator no longer exists, so destroy */ + + destroy = TRUE; + } + } + } + + if (destroy) { - for (i = 0; i < jack_shm_id_cnt; ++i) { - if (strcmp (jack_shm_registry[i].name, shm_name) == 0) { - return i; + jack_destroy_shm (©); + + r->size = 0; + r->allocator = 0; } } - return -1; /* not found */ + + jack_shm_unlock_registry (); } +#if USE_POSIX_SHM + int jack_initialize_shm () { - void *addr; - int id; - -#ifdef USE_POSIX_SHM - fprintf (stderr, "JACK compiled with POSIX SHM support\n"); -#else - fprintf (stderr, "JACK compiled with System V SHM support\n"); -#endif + int shm_fd; + jack_shmsize_t size; + int new_registry = FALSE; + int ret = -1; if (jack_shm_registry != NULL) { return 0; } /* grab a chunk of memory to store shm ids in. this is - to allow our parent to clean up all such ids when - if we exit. otherwise, they can get lost in crash - or debugger driven exits. + to allow clean up of all segments whenever JACK + starts (or stops). */ - if ((addr = jack_get_shm ("/jack-shm-registry", - (sizeof (jack_shm_registry_entry_t) - * MAX_SHM_ID), O_RDWR|O_CREAT, 0600, - PROT_READ|PROT_WRITE, &id)) - == MAP_FAILED) { - return -1; - } - jack_shm_registry = (jack_shm_registry_entry_t *) addr; - jack_shm_id_cnt = 0; - - jack_register_shm ("/jack-shm-registry", addr, id); + size = sizeof (jack_shm_registry_t) * MAX_SHM_ID; - return 0; -} + jack_shm_lock_registry (); -void -jack_cleanup_shm () -{ -#if ! USE_POSIX_SHM - char path[PATH_MAX+1]; - DIR *dir; - struct dirent *dirent; -#endif - int i; + /* try without O_CREAT to see if it already exists */ - for (i = 0; i < jack_shm_id_cnt; i++) { - jack_destroy_shm (jack_shm_registry[i].name); - } -#if ! USE_POSIX_SHM + if ((shm_fd = shm_open ("/jack-shm-registry", O_RDWR, 0666)) < 0) { - snprintf (path, sizeof(path), "%s/jack/shm", jack_server_dir); - if ((dir = opendir (path)) == NULL) { - if (errno != ENOENT) { - jack_error ("cannot open jack shm directory (%s)", + if (errno == ENOENT) { + + /* it doesn't exist, so create it */ + + if ((shm_fd = shm_open ("/jack-shm-registry", O_RDWR|O_CREAT, 0666)) < 0) { + jack_error ("cannot create shm registry segment (%s)", + strerror (errno)); + goto out; + } + new_registry = TRUE; + + } else { + + jack_error ("cannot open existing shm registry segment (%s)", strerror (errno)); - } - } else { - while ((dirent = readdir (dir)) != NULL) { - char fullpath[PATH_MAX+1]; - snprintf (fullpath, sizeof (fullpath), - "%s/jack/shm/%s", jack_server_dir, - dirent->d_name); - unlink (fullpath); + goto out; } } - closedir (dir); - snprintf (path, sizeof(path), "%s/jack/shm", jack_server_dir); - if (rmdir (path)) { - if (errno != ENOENT) { - jack_error ("cannot remove JACK shm directory (%s)", - strerror (errno)); - } + if (ftruncate (shm_fd, size) < 0) { + jack_error ("cannot set size of engine shm registry " + "(%s)", strerror (errno)); + goto out; } - snprintf (path, sizeof(path), "%s/jack", jack_server_dir); - if (rmdir (path)) { - if (errno != ENOENT) { - jack_error ("cannot remove JACK directory (%s)", - strerror (errno)); + + if ((jack_shm_registry = mmap (0, size, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) { + jack_error ("cannot mmap shm registry segment (%s)", + strerror (errno)); + goto out; + } + + if (new_registry) { + int i; + + memset (jack_shm_registry, 0, size); + for (i = 0; i < MAX_SHM_ID; ++i) { + jack_shm_registry[i].index = i; } + fprintf (stderr, "JACK compiled with POSIX SHM support\n"); } -#endif -} -#if USE_POSIX_SHM + ret = 0; + + out: + close (shm_fd); + jack_shm_unlock_registry (); + return ret; +} void -jack_destroy_shm (const char *shm_name) +jack_destroy_shm (jack_shm_info_t* si) { - shm_unlink (shm_name); + shm_unlink (jack_shm_registry[si->index].id); + jack_release_shm_info (si->index); } void -jack_release_shm (char *addr, size_t size) +jack_release_shm (jack_shm_info_t* si) { - munmap (addr, size); + if (si->attached_at >= 0) { + munmap (si->attached_at, jack_shm_registry[si->index].size); + } } -char * -jack_get_shm (const char *shm_name, size_t size, int perm, int mode, int prot, - int *not_really_used) +int +jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* si) { + jack_shm_registry_t* registry; int shm_fd; - char *addr; - if ((shm_fd = shm_open (shm_name, perm, mode)) < 0) { + if ((registry = jack_get_free_shm_info ()) == NULL) { + return -1; + } + + if ((shm_fd = shm_open (shm_name, O_RDWR|O_CREAT, 0666)) < 0) { jack_error ("cannot create shm segment %s (%s)", shm_name, strerror (errno)); - return MAP_FAILED; + return -1; } - if (perm & O_CREAT) { - if (ftruncate (shm_fd, size) < 0) { - jack_error ("cannot set size of engine shm registry " - "(%s)", strerror (errno)); - return MAP_FAILED; - } + if (ftruncate (shm_fd, size) < 0) { + jack_error ("cannot set size of engine shm registry " + "(%s)", strerror (errno)); + return -1; } - if ((addr = mmap (0, size, prot, MAP_SHARED, shm_fd, 0)) - == MAP_FAILED) { - jack_error ("cannot mmap shm segment %s (%s)", shm_name, + close (shm_fd); + + registry->size = size; + snprintf (registry->id, sizeof (registry->id), "%s", shm_name); + registry->allocator = getpid(); + + si->index = registry->index; + + return 0; +} + +int +jack_attach_shm (jack_shm_info_t* si) +{ + int shm_fd; + jack_shm_registry_t *registry = &jack_shm_registry[si->index]; + + if ((shm_fd = shm_open (registry->id, + O_RDWR, 0666)) < 0) { + jack_error ("cannot open shm segment %s (%s)", registry->id, + strerror (errno)); + return -1; + } + + if ((si->attached_at = mmap (0, registry->size, PROT_READ|PROT_WRITE, + MAP_SHARED, shm_fd, 0)) == MAP_FAILED) { + jack_error ("cannot mmap shm segment %s (%s)", + registry->id, strerror (errno)); - shm_unlink (shm_name); close (shm_fd); - return MAP_FAILED; + return -1; } close (shm_fd); - *not_really_used = 0; - return addr; + + return 0; } -char * -jack_resize_shm (const char *shm_name, size_t size, int perm, int mode, - int prot) +int +jack_resize_shm (jack_shm_info_t* si, jack_shmsize_t size) { - int entry; int shm_fd; - char *addr; - struct stat statbuf; + jack_shm_registry_t *registry = &jack_shm_registry[si->index]; - if ((entry = jack_lookup_shm (shm_name)) < 0) { - jack_error ("attempt to resize unknown shm segment \"%s\"", - shm_name); - return MAP_FAILED; - } - - if ((shm_fd = shm_open (shm_name, perm, mode)) < 0) { - jack_error ("cannot create shm segment %s (%s)", shm_name, + if ((shm_fd = shm_open (registry->id, O_RDWR, 0666)) < 0) { + jack_error ("cannot create shm segment %s (%s)", registry->id, strerror (errno)); - return MAP_FAILED; + return -1; } - fstat (shm_fd, &statbuf); - - munmap (jack_shm_registry[entry].address, statbuf.st_size); + munmap (si->attached_at, registry->size); - if (perm & O_CREAT) { - if (ftruncate (shm_fd, size) < 0) { - jack_error ("cannot set size of engine shm registry " - "(%s)", strerror (errno)); - return MAP_FAILED; - } + if (ftruncate (shm_fd, size) < 0) { + jack_error ("cannot set size of shm segment %s " + "(%s)", registry->id, strerror (errno)); + return -1; } - if ((addr = mmap (0, size, prot, MAP_SHARED, shm_fd, 0)) + if ((si->attached_at = mmap (0, size, PROT_READ|PROT_WRITE, + MAP_SHARED, shm_fd, 0)) == MAP_FAILED) { - jack_error ("cannot mmap shm segment %s (%s)", shm_name, + jack_error ("cannot mmap shm segment %s (%s)", registry->id, strerror (errno)); - shm_unlink (shm_name); close (shm_fd); - return MAP_FAILED; + return -1; } close (shm_fd); - return addr; + return 0; } #else /* USE_POSIX_SHM */ -char -jack_hash_shm (jack_shmsize_t size) -{ - char log2size = 0; /* log2 of size */ +#define JACK_SHM_REGISTRY_KEY 0x282929 - if (size == 0) - return log2size; /* don't loop forever */ +int +jack_initialize_shm () +{ + int shmflags; + int shmid; + key_t key; + jack_shmsize_t size; + int new_registry = FALSE; + int ret = -1; - /* remove low-order zeroes, counting them */ - while ((size & 1) == 0) { - ++log2size; - size >>= 1; + if (jack_shm_registry != NULL) { + return 0; } - return (char) ((size + log2size) & 0x7f); -} + /* grab a chunk of memory to store shm ids in. this is + to allow our parent to clean up all such ids when + if we exit. otherwise, they can get lost in crash + or debugger driven exits. + */ -void -jack_destroy_shm (const char *shm_name) -{ - int i = jack_lookup_shm (shm_name); + shmflags = 0666; + key = JACK_SHM_REGISTRY_KEY; + size = sizeof (jack_shm_registry_t) * MAX_SHM_ID; - if (i >= 0) - shmctl (IPC_RMID, jack_shm_registry[i].shmid, NULL); -} + jack_shm_lock_registry (); -void -jack_release_shm (char *addr, size_t size) -{ - shmdt (addr); -} + /* try without IPC_CREAT to check if it already exists */ -char * -jack_get_shm (const char *shm_name, size_t size, int perm, int mode, - int prot, int* shmid) -{ - char *addr; - key_t key; - int shmflags; - char path[PATH_MAX+1]; - struct stat statbuf; - int status; - - /* note: no trailing '/' on basic path because we expect shm_name to - begin with one (as per POSIX shm API). */ - snprintf (path, sizeof(path), "%s/jack", jack_server_dir); - if (mkdir (path, 0775)) { - if (errno != EEXIST) { - jack_error ("cannot create JACK directory (%s)", - strerror (errno)); - return MAP_FAILED; - } - } + if ((shmid = shmget (key, size, shmflags)) < 0) { + if (errno == ENOENT) { + if ((shmid = shmget (key, size, shmflags|IPC_CREAT)) < 0) { + jack_error ("cannot create shm registry segment (%s)", + strerror (errno)); + goto out; + } + + new_registry = TRUE; + + } else { - snprintf (path, sizeof(path), "%s/jack/shm", jack_server_dir); - if (mkdir (path, 0775)) { - if (errno != EEXIST) { - jack_error ("cannot create JACK shm directory (%s)", + jack_error ("cannot use existing shm registry segment (%s)", strerror (errno)); - return MAP_FAILED; + goto out; } } - - snprintf (path, sizeof(path), "%s/jack/shm%s", jack_server_dir, - shm_name); - if ((status = stat (path, &statbuf)) < 0) { - int fd; - - if ((fd = open (path, O_RDWR|O_CREAT, 0775)) < 0) { - jack_error ("cannot create shm file node for %s (%s)", - path, strerror (errno)); - return MAP_FAILED; - } - close (fd); + + if ((jack_shm_registry = shmat (shmid, 0, 0)) < 0) { + jack_error ("cannot attach shm registry segment (%s)", + strerror (errno)); + goto out; } - /* Hash the shm size to distinguish differently-sized segments - * with the same path name. This allows jack_resize_shm() to - * allocate a new segment when the size changes with the same - * name but a different shmid. */ - if ((key = ftok (path, jack_hash_shm(size))) < 0) { - jack_error ("cannot generate IPC key for shm segment %s (%s)", - path, strerror (errno)); - unlink (path); - return MAP_FAILED; + if (new_registry) { + int i; + memset (jack_shm_registry, 0, size); + for (i = 0; i < MAX_SHM_ID; ++i) { + jack_shm_registry[i].index = i; + } + fprintf (stderr, "JACK compiled with System V SHM support\n"); } - - /* XXX need to figure out how to do this without causing the - inode reallocation the next time this function is called - resulting in ftok() returning non-unique keys. - */ - /* unlink (path); */ + ret = 0; - shmflags = mode; + out: + jack_shm_unlock_registry (); + return ret; +} - if (perm & O_CREAT) { - shmflags |= IPC_CREAT; - } +void +jack_destroy_shm (jack_shm_info_t* si) +{ + shmctl (jack_shm_registry[si->index].id, IPC_RMID, NULL); + jack_release_shm_info (si->index); +} - if (perm & O_TRUNC) { - shmflags |= IPC_EXCL; +void +jack_release_shm (jack_shm_info_t* si) +{ + if (si->attached_at >= 0) { + shmdt (si->attached_at); } +} - if ((*shmid = shmget (key, size, shmflags)) < 0) { - - if (errno == EEXIST && (shmflags & IPC_EXCL)) { +int +jack_shmalloc (const char* name_not_used, jack_shmsize_t size, jack_shm_info_t* si) +{ + int shmflags; + int shmid; + jack_shm_registry_t* registry; - shmflags &= ~IPC_EXCL; + if ((registry = jack_get_free_shm_info ()) == NULL) { + return -1; + } - if ((*shmid = shmget (key, size, shmflags)) < 0) { - jack_error ("cannot get existing shm segment " - "for %s (%s)", shm_name, - strerror (errno)); - return MAP_FAILED; - } + shmflags = 0666 | IPC_CREAT | IPC_EXCL; - } else { - jack_error ("cannot create shm segment %s (%s)", - shm_name, strerror (errno)); - return MAP_FAILED; - } + if ((shmid = shmget (IPC_PRIVATE, size, shmflags)) < 0) { + jack_error ("cannot create shm segment %s (%s)", + name_not_used, strerror (errno)); + return -1; } - if ((addr = shmat (*shmid, 0, 0)) < 0) { - jack_error ("cannot attach shm segment %s (%s)", - shm_name, strerror (errno)); - return MAP_FAILED; - } + registry->size = size; + registry->id = shmid; + registry->allocator = getpid(); - return addr; + si->index = registry->index; + + return 0; } -char * -jack_resize_shm (const char *shm_name, size_t size, int perm, int mode, - int prot) +int +jack_attach_shm (jack_shm_info_t* si) { - int entry = jack_lookup_shm (shm_name); - - if (entry < 0) { - jack_error ("attempt to resize unknown shm segment \"%s\"", - shm_name); - return MAP_FAILED; + if ((si->attached_at = shmat (jack_shm_registry[si->index].id, 0, 0)) < 0) { + jack_error ("cannot attach shm segment (%s)", + strerror (errno)); + jack_release_shm_info (si->index); + return -1; } + return 0; +} +int +jack_resize_shm (jack_shm_info_t* si, jack_shmsize_t size) +{ /* There is no way to resize a System V shm segment. So, we * delete it and allocate a new one. This is tricky, because * the old segment will not disappear until all the clients - * have released it. */ - jack_destroy_shm (shm_name); - jack_release_shm (jack_shm_registry[entry].address, size); - jack_shm_registry[entry].address = - jack_get_shm (shm_name, size, perm, mode, prot, - &jack_shm_registry[entry].shmid); - - return jack_shm_registry[entry].address; + * have released it. We can only do what we can from here. + */ + + jack_release_shm (si); + jack_destroy_shm (si); + + if (jack_shmalloc ("not used", size, si)) { + return -1; + } + + return jack_attach_shm (si); } -#endif /* USE_POSIX_SHM */ +#endif /* !USE_POSIX_SHM */ diff --git a/libjack/transclient.c b/libjack/transclient.c index 99fe28a..e9abeff 100644 --- a/libjack/transclient.c +++ b/libjack/transclient.c @@ -170,6 +170,7 @@ jack_call_timebase_master (jack_client_t *client) jack_control_t *ectl = client->engine; int new_pos = (int) ectl->pending_pos; + /* Make sure this is still the master; is_timebase is set in a * critical section; timebase_cb is not. */ if (control->is_timebase) { @@ -179,6 +180,7 @@ jack_call_timebase_master (jack_client_t *client) new_pos = 1; } + if ((ectl->transport_state == JackTransportRolling) || new_pos) { @@ -201,6 +203,38 @@ jack_call_timebase_master (jack_client_t *client) /************************* API functions *************************/ +jack_nframes_t +jack_get_current_transport_frame (const jack_client_t *client) +{ + jack_position_t position; + float usecs; + jack_nframes_t elapsed; + jack_transport_state_t tstate; + + /* get the current transport position information. + this is thread-safe and atomic with respect + to the structure contents. + */ + + tstate = jack_transport_query (client, &position); + + if (tstate != JackTransportRolling) { + return position.frame; + } + + /* compute the elapsed usecs then audio frames since + the transport info was last updated + */ + + usecs = jack_get_microseconds() - position.usecs; + elapsed = (jack_nframes_t) floor ((((float) position.frame_rate) / 1000000.0f) * usecs); + + /* return the estimated transport frame position + */ + + return position.frame + elapsed; +} + jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *client) { @@ -338,14 +372,16 @@ jack_transport_locate (jack_client_t *client, jack_nframes_t frame) } jack_transport_state_t -jack_transport_query (jack_client_t *client, jack_position_t *pos) +jack_transport_query (const jack_client_t *client, jack_position_t *pos) { jack_control_t *ectl = client->engine; - if (pos) + if (pos) { /* the guarded copy makes this function work in any - * thread */ + * thread + */ jack_transport_copy_position (&ectl->current_time, pos); + } return ectl->transport_state; }