Browse Source

[0.89.0] merge of EXP branch into HEAD

git-svn-id: svn+ssh://jackaudio.org/trunk/jack@540 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/0.109.0
joq 22 years ago
parent
commit
4100a7bef9
44 changed files with 3816 additions and 2179 deletions
  1. +5
    -0
      .cvsignore
  2. +10
    -7
      AUTHORS
  3. +4
    -8
      TODO
  4. +4
    -4
      autogen.sh
  5. +12
    -8
      configure.in
  6. +2
    -1
      doc/Makefile.am
  7. +84
    -13
      doc/mainpage.dox
  8. +3
    -1
      doc/reference.doxygen.in
  9. +757
    -383
      drivers/alsa/alsa_driver.c
  10. +5
    -2
      drivers/alsa/alsa_driver.h
  11. +232
    -210
      drivers/dummy/dummy_driver.c
  12. +9
    -12
      drivers/dummy/dummy_driver.h
  13. +233
    -84
      drivers/portaudio/portaudio_driver.c
  14. +2
    -0
      drivers/portaudio/portaudio_driver.h
  15. +1
    -1
      example-clients/.cvsignore
  16. +5
    -1
      example-clients/Makefile.am
  17. +7
    -7
      example-clients/capture_client.c
  18. +88
    -0
      example-clients/freewheel.c
  19. +0
    -42
      example-clients/ringbuffer.h
  20. +18
    -36
      example-clients/simple_client.c
  21. +7
    -4
      jack/Makefile.am
  22. +95
    -15
      jack/driver.h
  23. +97
    -0
      jack/driver_interface.h
  24. +208
    -0
      jack/driver_parse.h
  25. +76
    -66
      jack/engine.h
  26. +21
    -20
      jack/internal.h
  27. +20
    -0
      jack/jack.h
  28. +30
    -21
      jack/port.h
  29. +141
    -0
      jack/ringbuffer.h
  30. +56
    -8
      jack/shm.h
  31. +10
    -1
      jack/transport.h
  32. +2
    -1
      jack/types.h
  33. +550
    -660
      jackd/engine.c
  34. +20
    -7
      jackd/jackd.1.in
  35. +226
    -150
      jackd/jackd.c
  36. +11
    -0
      libjack/ChangeLog
  37. +1
    -0
      libjack/Makefile.am
  38. +173
    -102
      libjack/client.c
  39. +202
    -8
      libjack/driver.c
  40. +13
    -9
      libjack/local.h
  41. +5
    -3
      libjack/port.c
  42. +20
    -20
      libjack/ringbuffer.c
  43. +312
    -261
      libjack/shm.c
  44. +39
    -3
      libjack/transclient.c

+ 5
- 0
.cvsignore View File

@@ -17,3 +17,8 @@ configure
*.desc
*diff
*diffs
compile
depcomp
install-sh
missing
mkinstalldirs

+ 10
- 7
AUTHORS View File

@@ -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.

+ 4
- 8
TODO View File

@@ -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)

-----------------------------------------------------------------------

+ 4
- 4
autogen.sh View File

@@ -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

+ 12
- 8
configure.in View File

@@ -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"


+ 2
- 1
doc/Makefile.am View File

@@ -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


+ 84
- 13
doc/mainpage.dox View File

@@ -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.


+ 3
- 1
doc/reference.doxygen.in View File

@@ -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


+ 757
- 383
drivers/alsa/alsa_driver.c
File diff suppressed because it is too large
View File


+ 5
- 2
drivers/alsa/alsa_driver.h View File

@@ -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) {


+ 232
- 210
drivers/dummy/dummy_driver.c View File

@@ -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;
}
}



+ 9
- 12
drivers/dummy/dummy_driver.h View File

@@ -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__ */

+ 233
- 84
drivers/portaudio/portaudio_driver.c View File

@@ -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


+ 2
- 0
drivers/portaudio/portaudio_driver.h View File

@@ -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;


+ 1
- 1
example-clients/.cvsignore View File

@@ -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


+ 5
- 1
example-clients/Makefile.am View File

@@ -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


+ 7
- 7
example-clients/capture_client.c View File

@@ -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);
}

+ 88
- 0
example-clients/freewheel.c View File

@@ -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;
}

+ 0
- 42
example-clients/ringbuffer.h View File

@@ -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

+ 18
- 36
example-clients/simple_client.c View File

@@ -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);


+ 7
- 4
jack/Makefile.am View File

@@ -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


+ 95
- 15
jack/driver.h View File

@@ -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__ */

+ 97
- 0
jack/driver_interface.h View File

@@ -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__ */



+ 208
- 0
jack/driver_parse.h View File

@@ -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__ */



+ 76
- 66
jack/engine.h View File

@@ -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);



+ 21
- 20
jack/internal.h View File

@@ -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__ */


+ 20
- 0
jack/jack.h View File

@@ -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.
*


+ 30
- 21
jack/port.h View File

@@ -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;


+ 141
- 0
jack/ringbuffer.h View File

@@ -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

+ 56
- 8
jack/shm.h View File

@@ -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__ */

+ 10
- 1
jack/transport.h View File

@@ -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.
*


+ 2
- 1
jack/types.h View File

@@ -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.


+ 550
- 660
jackd/engine.c
File diff suppressed because it is too large
View File


+ 20
- 7
jackd/jackd.1.in View File

@@ -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>.

+ 226
- 150
jackd/jackd.c View File

@@ -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);
}




+ 11
- 0
libjack/ChangeLog View File

@@ -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().


+ 1
- 0
libjack/Makefile.am View File

@@ -6,6 +6,7 @@ SOURCE_FILES = \
driver.c \
pool.c \
port.c \
ringbuffer.c \
timestamps.c \
transclient.c



+ 173
- 102
libjack/client.c View File

@@ -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) {


+ 202
- 8
libjack/driver.c View File

@@ -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);
}

+ 13
- 9
libjack/local.h View File

@@ -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;


+ 5
- 3
libjack/port.c View File

@@ -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);
}



example-clients/ringbuffer.c → libjack/ringbuffer.c View File

@@ -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;

+ 312
- 261
libjack/shm.c View File

@@ -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 (&copy);
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 (&copy);
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 */

+ 39
- 3
libjack/transclient.c View File

@@ -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;
}


Loading…
Cancel
Save