git-svn-id: svn+ssh://jackaudio.org/trunk/jack@540 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
| @@ -17,3 +17,8 @@ configure | |||
| *.desc | |||
| *diff | |||
| *diffs | |||
| compile | |||
| depcomp | |||
| install-sh | |||
| missing | |||
| mkinstalldirs | |||
| @@ -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. | |||
| @@ -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) | |||
| ----------------------------------------------------------------------- | |||
| @@ -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 | |||
| @@ -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" | |||
| @@ -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 | |||
| @@ -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 <http://jackit.sourceforge.net> | |||
| @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 <simple_client.c> | |||
| 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. | |||
| - <jack/types.h> defines most of the data types for JACK. | |||
| - <jack/ringbuffer.h> 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, <http://www.gnu.org>. 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, <http://www.gnu.org>. 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. | |||
| @@ -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 | |||
| @@ -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) { | |||
| @@ -1,3 +1,4 @@ | |||
| /* -*- mode: c; c-file-style: "linux"; -*- */ | |||
| /* | |||
| Copyright (C) 2003 Robert Ham <rah@bash.sh> | |||
| Copyright (C) 2001 Paul Davis | |||
| @@ -27,6 +28,7 @@ | |||
| #include <errno.h> | |||
| #include <stdarg.h> | |||
| #include <getopt.h> | |||
| #include <sys/mman.h> | |||
| #include <jack/types.h> | |||
| #include <jack/internal.h> | |||
| @@ -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 <n> \tsample rate (default: 48000)\n" | |||
| " -p,--period <n> \tframes per period (default: 1024)\n" | |||
| " -C,--capture <n> \tnumber of capture ports (default: 2)\n" | |||
| " -P,--playback <n> \tnumber of playback ports (default: 2)\n" | |||
| " -w,--wait <usecs> \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 <getopt.h>. | |||
| */ | |||
| 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; | |||
| } | |||
| } | |||
| @@ -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__ */ | |||
| @@ -1,3 +1,4 @@ | |||
| /* -*- mode: c; c-file-style: "bsd"; -*- */ | |||
| /* | |||
| Copyright © Grame 2003 | |||
| @@ -23,6 +24,7 @@ | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <string.h> | |||
| #include <getopt.h> | |||
| #include <jack/engine.h> | |||
| #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 | |||
| @@ -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; | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -31,7 +31,7 @@ | |||
| #include <pthread.h> | |||
| #include <getopt.h> | |||
| #include <jack/jack.h> | |||
| #include "ringbuffer.h" | |||
| #include <jack/ringbuffer.h> | |||
| 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); | |||
| } | |||
| @@ -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 <stdio.h> | |||
| #include <errno.h> | |||
| #include <unistd.h> | |||
| #include <signal.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/transport.h> | |||
| 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; | |||
| } | |||
| @@ -1,42 +0,0 @@ | |||
| #ifndef _RINGBUFFER_H | |||
| #define _RINGBUFFER_H | |||
| #include <sys/types.h> | |||
| 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 | |||
| @@ -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 <stdio.h> | |||
| #include <errno.h> | |||
| #include <unistd.h> | |||
| @@ -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); | |||
| @@ -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 | |||
| @@ -24,6 +24,7 @@ | |||
| #include <pthread.h> | |||
| #include <jack/types.h> | |||
| #include <jack/port.h> | |||
| #include <jack/driver_interface.h> | |||
| 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__ */ | |||
| @@ -0,0 +1,97 @@ | |||
| /* | |||
| Copyright (C) 2003 Bob Ham <rah@bash.sh> | |||
| 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 <limits.h> | |||
| #include <jack/jack.h> | |||
| #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__ */ | |||
| @@ -0,0 +1,208 @@ | |||
| /* -*- mode: c; c-file-style: "bsd"; -*- */ | |||
| /* | |||
| Copyright (C) 2003 Bob Ham <rah@bash.sh | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU General Public License as published by | |||
| the Free Software Foundation; either version 2 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
| */ | |||
| #ifndef __jack_driver_parse_h__ | |||
| #define __jack_driver_parse_h__ | |||
| #include <jack/jslist.h> | |||
| #include <jack/driver_interface.h> | |||
| 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 = " [<arg>]"; | |||
| break; | |||
| case required_argument: | |||
| arg_string = " <arg>"; | |||
| 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__ */ | |||
| @@ -1,3 +1,4 @@ | |||
| /* -*- mode: c; c-file-style: "bsd"; -*- */ | |||
| /* | |||
| Copyright (C) 2001-2003 Paul Davis | |||
| @@ -23,6 +24,7 @@ | |||
| #include <jack/jack.h> | |||
| #include <jack/internal.h> | |||
| #include <jack/driver_interface.h> | |||
| #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); | |||
| @@ -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__ */ | |||
| @@ -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. | |||
| * | |||
| @@ -24,12 +24,21 @@ | |||
| #include <pthread.h> | |||
| #include <jack/types.h> | |||
| #include <jack/jslist.h> | |||
| #include <jack/shm.h> | |||
| #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; | |||
| @@ -0,0 +1,141 @@ | |||
| #ifndef _RINGBUFFER_H | |||
| #define _RINGBUFFER_H | |||
| #include <sys/types.h> | |||
| /** @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 | |||
| @@ -1,19 +1,67 @@ | |||
| #ifndef __jack_shm_h__ | |||
| #define __jack_shm_h__ | |||
| #include <limits.h> | |||
| #include <sys/types.h> | |||
| #include <jack/types.h> | |||
| #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__ */ | |||
| @@ -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. | |||
| * | |||
| @@ -23,7 +23,8 @@ | |||
| #include <inttypes.h> | |||
| 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. | |||
| @@ -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 | |||
| <e9925373@student.tuwien.ac.at> and later adapted by Jack O'Quin | |||
| <e9925373@student.tuwien.ac.at>, later adapted by Jack O'Quin | |||
| <joq@joq.us>. | |||
| @@ -1,3 +1,4 @@ | |||
| /* -*- mode: c; c-file-style: "bsd"; -*- */ | |||
| /* | |||
| Copyright (C) 2001-2003 Paul Davis | |||
| @@ -26,6 +27,9 @@ | |||
| #include <sys/wait.h> | |||
| #include <string.h> | |||
| #include <errno.h> | |||
| #include <stdlib.h> | |||
| #include <dirent.h> | |||
| #include <dlfcn.h> | |||
| #include <config.h> | |||
| @@ -33,6 +37,7 @@ | |||
| #include <jack/internal.h> | |||
| #include <jack/driver.h> | |||
| #include <jack/shm.h> | |||
| #include <jack/driver_parse.h> | |||
| #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); | |||
| } | |||
| @@ -1,3 +1,14 @@ | |||
| 2003-10-15 Paul Davis <paul@linuxaudiosystems.com> | |||
| * new ring buffer interface: <jack/ringbuffer.h> | |||
| 2003-10-07 Paul Davis <paul@linuxaudiosystems.com> | |||
| * 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 <joq@io.com> | |||
| * new function jack_set_buffer_size(). | |||
| @@ -6,6 +6,7 @@ SOURCE_FILES = \ | |||
| driver.c \ | |||
| pool.c \ | |||
| port.c \ | |||
| ringbuffer.c \ | |||
| timestamps.c \ | |||
| transclient.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) { | |||
| @@ -1,3 +1,4 @@ | |||
| /* -*- mode: c; c-file-style: "linux"; -*- */ | |||
| /* | |||
| Copyright (C) 2001-2003 Paul Davis | |||
| @@ -23,21 +24,17 @@ | |||
| #include <stdarg.h> | |||
| #include <stdlib.h> | |||
| #include <stdio.h> | |||
| #include <sys/mman.h> | |||
| #include <errno.h> | |||
| #include <config.h> | |||
| #include <jack/driver.h> | |||
| #include <jack/internal.h> | |||
| #include <jack/engine.h> | |||
| 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); | |||
| } | |||
| @@ -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; | |||
| @@ -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); | |||
| } | |||
| @@ -23,18 +23,18 @@ | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <sys/mman.h> | |||
| #include "ringbuffer.h" | |||
| #include <jack/ringbuffer.h> | |||
| /* 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; | |||
| @@ -22,9 +22,9 @@ | |||
| #include <fcntl.h> | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <signal.h> | |||
| #include <limits.h> | |||
| #include <errno.h> | |||
| #include <sys/stat.h> | |||
| #include <sys/mman.h> | |||
| #include <sys/types.h> | |||
| #include <dirent.h> | |||
| @@ -35,381 +35,432 @@ | |||
| #include <jack/shm.h> | |||
| #include <jack/internal.h> | |||
| 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 */ | |||
| @@ -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; | |||
| } | |||