git-svn-id: svn+ssh://jackaudio.org/trunk/jack@46 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
@@ -7,6 +7,7 @@ libtool | |||||
jackd | jackd | ||||
jack_simple_client | jack_simple_client | ||||
jack_monitor_client | jack_monitor_client | ||||
jackrec | |||||
*.la | *.la | ||||
*.pc | *.pc | ||||
Makefile | Makefile | ||||
@@ -4,9 +4,9 @@ MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure config.h.in \ | |||||
missing install-sh config.sub ltconfig \ | missing install-sh config.sub ltconfig \ | ||||
ltmain.sh acinclude.m4 | ltmain.sh acinclude.m4 | ||||
SUBDIRS = jack doc | |||||
SUBDIRS = jack | |||||
EXTRA_PROGRAMS = jack_fltk_client | |||||
EXTRA_PROGRAMS = jack_fltk_client jackrec | |||||
bin_PROGRAMS = jackd jack_simple_client jack_monitor_client @XTRA@ | bin_PROGRAMS = jackd jack_simple_client jack_monitor_client @XTRA@ | ||||
@@ -26,6 +26,9 @@ jack_monitor_client_LDFLAGS = -L. -ljack -lltdl -lpthread | |||||
jack_fltk_client_SOURCES = fltk_client.cc | jack_fltk_client_SOURCES = fltk_client.cc | ||||
jack_fltk_client_LDFLAGS = -L. -L/usr/X11R6/lib -lfltk -lX11 -lXext -ljack -lltdl -lpthread | jack_fltk_client_LDFLAGS = -L. -L/usr/X11R6/lib -lfltk -lX11 -lXext -ljack -lltdl -lpthread | ||||
jackrec_SOURCES = capture_client.c | |||||
jackrec_LDFLAGS = -L. -lsndfile -ljack -lltdl -lpthread | |||||
lib_LTLIBRARIES = libjack.la jack_alsa.la | lib_LTLIBRARIES = libjack.la jack_alsa.la | ||||
libjack_la_SOURCES = client.c pool.c driver.c | libjack_la_SOURCES = client.c pool.c driver.c | ||||
@@ -38,9 +38,9 @@ | |||||
static int config_max_level = 0; | static int config_max_level = 0; | ||||
static int config_min_level = 0; | static int config_min_level = 0; | ||||
static unsigned long current_usecs () { | |||||
unsigned long now; | |||||
rdtscl (now); | |||||
static unsigned long long current_usecs () { | |||||
unsigned long long now; | |||||
rdtscll (now); | |||||
return now / 450; | return now / 450; | ||||
} | } | ||||
@@ -704,6 +704,7 @@ alsa_driver_wait (alsa_driver_t *driver) | |||||
channel_t chn; | channel_t chn; | ||||
GSList *node; | GSList *node; | ||||
sample_t *buffer; | sample_t *buffer; | ||||
// unsigned long long end; | |||||
again: | again: | ||||
if (poll (&driver->pfd, 1, 1000) < 0) { | if (poll (&driver->pfd, 1, 1000) < 0) { | ||||
@@ -844,6 +845,9 @@ alsa_driver_wait (alsa_driver_t *driver) | |||||
avail -= contiguous; | avail -= contiguous; | ||||
} | } | ||||
// end = current_usecs(); | |||||
// printf ("entire cycle took %f usecs\n", ((float)(end - driver->time_at_interrupt))/450.0f); | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -0,0 +1,319 @@ | |||||
#include <stdio.h> | |||||
#include <errno.h> | |||||
#include <unistd.h> | |||||
#include <sndfile.h> | |||||
#include <pthread.h> | |||||
#include <glib.h> | |||||
#include <jack/jack.h> | |||||
typedef struct _thread_info { | |||||
pthread_t thread_id; | |||||
SNDFILE *sf; | |||||
nframes_t duration; | |||||
jack_client_t *client; | |||||
unsigned int channels; | |||||
int can_capture; | |||||
char *path; | |||||
int status; | |||||
int process_go; | |||||
} thread_info_t; | |||||
unsigned int nports; | |||||
jack_port_t **ports; | |||||
pthread_mutex_t buffer_lock = PTHREAD_MUTEX_INITIALIZER; | |||||
pthread_cond_t data_ready = PTHREAD_COND_INITIALIZER; | |||||
typedef struct _sample_buffer { | |||||
nframes_t nframes; | |||||
sample_t **data; | |||||
} sample_buffer_t; | |||||
sample_buffer_t * | |||||
sample_buffer_new (nframes_t nframes, unsigned int nchans) | |||||
{ | |||||
sample_buffer_t *buf; | |||||
unsigned int i; | |||||
buf = (sample_buffer_t *) malloc (sizeof (sample_buffer_t)); | |||||
buf->nframes = nframes; | |||||
buf->data = (sample_t **) malloc (sizeof (sample_t *) * nchans); | |||||
for (i = 0; i < nchans; i++) { | |||||
buf->data[i] = (sample_t *) malloc (sizeof (sample_t) * nframes); | |||||
} | |||||
return buf; | |||||
} | |||||
GSList *pending_writes = NULL; | |||||
GSList *free_buffers = NULL; | |||||
sample_buffer_t * | |||||
get_free_buffer (nframes_t nframes, unsigned int nchans) | |||||
{ | |||||
sample_buffer_t *buf; | |||||
if (free_buffers == NULL) { | |||||
buf = sample_buffer_new (nframes, nchans); | |||||
} else { | |||||
buf = (sample_buffer_t *) free_buffers->data; | |||||
free_buffers = g_slist_next (free_buffers); | |||||
} | |||||
return buf; | |||||
} | |||||
sample_buffer_t * | |||||
get_write_buffer () | |||||
{ | |||||
sample_buffer_t *buf; | |||||
if (pending_writes == NULL) { | |||||
return NULL; | |||||
} | |||||
buf = (sample_buffer_t *) pending_writes->data; | |||||
pending_writes = g_slist_next (pending_writes); | |||||
return buf; | |||||
} | |||||
void | |||||
put_write_buffer (sample_buffer_t *buf) | |||||
{ | |||||
pending_writes = g_slist_append (pending_writes, buf); | |||||
} | |||||
void | |||||
put_free_buffer (sample_buffer_t *buf) | |||||
{ | |||||
free_buffers = g_slist_prepend (free_buffers, buf); | |||||
} | |||||
void * | |||||
disk_thread (void *arg) | |||||
{ | |||||
sample_buffer_t *buf; | |||||
thread_info_t *info = (thread_info_t *) arg; | |||||
int i; | |||||
unsigned int chn; | |||||
nframes_t total_captured = 0; | |||||
int done = 0; | |||||
double *fbuf; | |||||
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); | |||||
pthread_mutex_lock (&buffer_lock); | |||||
/* preload the buffer cache */ | |||||
for (i = 0; i < 5; i++) { | |||||
buf = sample_buffer_new (jack_get_buffer_size (info->client), info->channels); | |||||
put_free_buffer (buf); | |||||
} | |||||
info->status = 0; | |||||
while (!done) { | |||||
pthread_cond_wait (&data_ready, &buffer_lock); | |||||
while ((buf = get_write_buffer ()) != 0) { | |||||
pthread_mutex_unlock (&buffer_lock); | |||||
/* grrr ... libsndfile doesn't do float data yet, only double */ | |||||
if (info->can_capture) { | |||||
fbuf = (double *) malloc (sizeof (double) * buf->nframes * info->channels); | |||||
for (chn = 0; chn < info->channels; chn++) { | |||||
for (i = 0; i < buf->nframes; i++) { | |||||
fbuf[chn+(i*info->channels)] = buf->data[chn][i]; | |||||
} | |||||
} | |||||
if (sf_writef_double (info->sf, fbuf, buf->nframes, 1) != buf->nframes) { | |||||
char errstr[256]; | |||||
sf_error_str (0, errstr, sizeof (errstr) - 1); | |||||
fprintf (stderr, "cannot write data to sndfile (%s)\n", errstr); | |||||
info->status = -1; | |||||
done = 1; | |||||
break; | |||||
} | |||||
free (fbuf); | |||||
total_captured += buf->nframes; | |||||
if (total_captured >= info->duration) { | |||||
printf ("disk thread finished\n"); | |||||
done = 1; | |||||
break; | |||||
} | |||||
} | |||||
pthread_mutex_lock (&buffer_lock); | |||||
put_free_buffer (buf); | |||||
} | |||||
} | |||||
pthread_mutex_unlock (&buffer_lock); | |||||
return 0; | |||||
} | |||||
int | |||||
process (nframes_t nframes, void *arg) | |||||
{ | |||||
thread_info_t *info = (thread_info_t *) arg; | |||||
sample_t *in; | |||||
sample_buffer_t *buf; | |||||
unsigned int i; | |||||
if (!info->process_go) { | |||||
return 0; | |||||
} | |||||
pthread_mutex_lock (&buffer_lock); | |||||
buf = get_free_buffer (nframes, nports); | |||||
for (i = 0; i < nports; i++) { | |||||
in = (sample_t *) jack_port_get_buffer (ports[i], nframes); | |||||
memcpy (buf->data[i], in, sizeof (sample_t) * nframes); | |||||
} | |||||
/* we don't like taking locks, but until we have a lock | |||||
free ringbuffer written in C, this is what has to be done | |||||
*/ | |||||
put_write_buffer (buf); | |||||
/* tell the disk thread that there is work to do */ | |||||
pthread_cond_signal (&data_ready); | |||||
pthread_mutex_unlock (&buffer_lock); | |||||
return 0; | |||||
} | |||||
void | |||||
jack_shutdown (void *arg) | |||||
{ | |||||
fprintf (stderr, "JACK shutdown\n"); | |||||
exit (0); | |||||
} | |||||
void | |||||
setup_disk_thread (thread_info_t *info) | |||||
{ | |||||
SF_INFO sf_info; | |||||
sf_info.samplerate = jack_get_sample_rate (info->client); | |||||
sf_info.channels = info->channels; | |||||
sf_info.format = SF_FORMAT_WAV|SF_FORMAT_PCM; | |||||
sf_info.pcmbitwidth = 16; | |||||
// sf_info.samples = info->duration * sf_info.samplerate; | |||||
if ((info->sf = sf_open_write (info->path, &sf_info)) == NULL) { | |||||
char errstr[256]; | |||||
sf_error_str (0, errstr, sizeof (errstr) - 1); | |||||
fprintf (stderr, "cannot open sndfile \"%s\" for output (%s)\n", info->path, errstr); | |||||
jack_client_close (info->client); | |||||
exit (1); | |||||
} | |||||
info->duration = 2 * 48000; | |||||
info->can_capture = 0; | |||||
pthread_create (&info->thread_id, NULL, disk_thread, info); | |||||
} | |||||
void | |||||
run_disk_thread (thread_info_t *info) | |||||
{ | |||||
info->can_capture = 1; | |||||
pthread_join (info->thread_id, NULL); | |||||
sf_close (info->sf); | |||||
if (info->status) { | |||||
unlink (info->path); | |||||
} | |||||
} | |||||
void | |||||
setup_ports (int argc, char *argv[], thread_info_t *info) | |||||
{ | |||||
char **source_names; | |||||
int i; | |||||
nports = argc - 2; | |||||
source_names = (char **) malloc (sizeof (char **) * nports); | |||||
ports = (jack_port_t **) malloc (sizeof (jack_port_t *) * nports); | |||||
for (i = 2; i < argc; i++) { | |||||
source_names[i-2] = argv[i]; | |||||
} | |||||
for (i = 0; i < nports; i++) { | |||||
char name[64]; | |||||
sprintf (name, "input%d\n", i+1); | |||||
if ((ports[i] = jack_port_register (info->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)) == 0) { | |||||
fprintf (stderr, "cannot register input port \"%s\"!\n", name); | |||||
jack_client_close (info->client); | |||||
exit (1); | |||||
} | |||||
} | |||||
for (i = 0; i < nports; i++) { | |||||
if (jack_port_connect (info->client, source_names[i], jack_port_name (ports[i]))) { | |||||
fprintf (stderr, "cannot connect input port %s to %s\n", jack_port_name (ports[i]), source_names[i]); | |||||
jack_client_close (info->client); | |||||
exit (1); | |||||
} | |||||
} | |||||
info->process_go = 1; | |||||
free (source_names); | |||||
} | |||||
int | |||||
main (int argc, char *argv[]) | |||||
{ | |||||
jack_client_t *client; | |||||
thread_info_t thread_info; | |||||
if (argc < 3) { | |||||
fprintf (stderr, "usage: jackrec filename port1 [ port2 ... ]\n"); | |||||
return 1; | |||||
} | |||||
if ((client = jack_client_new ("jackrec")) == 0) { | |||||
fprintf (stderr, "jack server not running?\n"); | |||||
return 1; | |||||
} | |||||
thread_info.client = client; | |||||
thread_info.path = argv[1]; | |||||
thread_info.channels = argc - 2; | |||||
thread_info.process_go = 0; | |||||
setup_disk_thread (&thread_info); | |||||
jack_set_process_callback (client, process, &thread_info); | |||||
jack_on_shutdown (client, jack_shutdown, NULL); | |||||
if (jack_activate (client)) { | |||||
fprintf (stderr, "cannot activate client"); | |||||
} | |||||
setup_ports (argc, argv, &thread_info); | |||||
run_disk_thread (&thread_info); | |||||
jack_client_close (client); | |||||
exit (0); | |||||
} |
@@ -53,6 +53,8 @@ struct _jack_client { | |||||
GSList *ports; | GSList *ports; | ||||
pthread_t thread; | pthread_t thread; | ||||
char fifo_prefix[FIFO_NAME_SIZE+1]; | char fifo_prefix[FIFO_NAME_SIZE+1]; | ||||
void (*on_shutdown)(void *arg); | |||||
void *on_shutdown_arg; | |||||
char thread_ok : 1; | char thread_ok : 1; | ||||
char first_active : 1; | char first_active : 1; | ||||
}; | }; | ||||
@@ -100,7 +102,7 @@ jack_client_alloc () | |||||
client->control = 0; | client->control = 0; | ||||
client->thread_ok = FALSE; | client->thread_ok = FALSE; | ||||
client->first_active = TRUE; | client->first_active = TRUE; | ||||
client->on_shutdown = NULL; | |||||
return client; | return client; | ||||
} | } | ||||
@@ -234,7 +236,7 @@ jack_handle_reorder (jack_client_t *client, jack_event_t *event) | |||||
jack_error ("cannot open specified fifo [%s] for writing (%s)", path, strerror (errno)); | jack_error ("cannot open specified fifo [%s] for writing (%s)", path, strerror (errno)); | ||||
return -1; | return -1; | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
@@ -478,6 +480,9 @@ jack_client_thread (void *arg) | |||||
if (client->pollfd[0].revents & ~POLLIN) { | if (client->pollfd[0].revents & ~POLLIN) { | ||||
jack_error ("engine has shut down socket; thread exiting"); | jack_error ("engine has shut down socket; thread exiting"); | ||||
if (client->on_shutdown) { | |||||
client->on_shutdown (client->on_shutdown_arg); | |||||
} | |||||
pthread_exit (0); | pthread_exit (0); | ||||
} | } | ||||
@@ -575,11 +580,11 @@ jack_client_thread (void *arg) | |||||
} | } | ||||
/* this may fail. if it does, the engine will discover | /* this may fail. if it does, the engine will discover | ||||
that for itcontrol due a cycle timeout, which is about | |||||
it due a cycle timeout, which is about | |||||
the best we can do without a lot of mostly wasted | the best we can do without a lot of mostly wasted | ||||
effort. | effort. | ||||
*/ | */ | ||||
write (client->graph_next_fd, &c, 1); | write (client->graph_next_fd, &c, 1); | ||||
} | } | ||||
} | } | ||||
@@ -960,7 +965,7 @@ jack_port_connect (jack_client_t *client, const char *source_port, const char *d | |||||
jack_error ("cannot read port connection result from server"); | jack_error ("cannot read port connection result from server"); | ||||
return -1; | return -1; | ||||
} | } | ||||
return req.status; | return req.status; | ||||
} | } | ||||
@@ -1220,3 +1225,15 @@ jack_port_request_monitor (jack_client_t *client, const char *port_name, int ono | |||||
return req.status; | return req.status; | ||||
} | } | ||||
const char * | |||||
jack_port_name (const jack_port_t *port) | |||||
{ | |||||
return port->shared->name; | |||||
} | |||||
void | |||||
jack_on_shutdown (jack_client_t *client, void (*function)(void *arg), void *arg) | |||||
{ | |||||
client->on_shutdown = function; | |||||
client->on_shutdown_arg = arg; | |||||
} |
@@ -23,7 +23,7 @@ AC_SUBST(JACK_RELEASE) | |||||
AM_INIT_AUTOMAKE(jack,${JACK_VERSION}) | AM_INIT_AUTOMAKE(jack,${JACK_VERSION}) | ||||
AM_PATH_GLIB(1.0.0,,[AC_MSG_ERROR([*** JACK requires glib, but it doesn't appear to be installed])]) | |||||
AM_PATH_GLIB(1.0.0,,[AC_MSG_ERROR([*** JACK requires glib, but it does not appear to be installed])]) | |||||
JACK_CFLAGS="-g -Wall -D_REENTRANT" | JACK_CFLAGS="-g -Wall -D_REENTRANT" | ||||
JACK_OPT_CFLAGS="-D_REENTRANT -O6 -Wall -fomit-frame-pointer -ffast-math -fstrength-reduce -funroll-loops -fmove-all-movables" | JACK_OPT_CFLAGS="-D_REENTRANT -O6 -Wall -fomit-frame-pointer -ffast-math -fstrength-reduce -funroll-loops -fmove-all-movables" | ||||
@@ -42,10 +42,15 @@ AC_ARG_ENABLE(fltk-client, | |||||
if test "x$enable_fltk_client" != "xno" ; then | if test "x$enable_fltk_client" != "xno" ; then | ||||
AC_CHECK_LIB(fltk,main, | AC_CHECK_LIB(fltk,main, | ||||
[ XTRA="$XTRA jack_fltk_client" ], | [ XTRA="$XTRA jack_fltk_client" ], | ||||
[ AC_MSG_ERROR([*** you can't build the FLTK client without the FLTK library])], | |||||
[ AC_MSG_ERROR([*** you cannot build the FLTK client without the FLTK library])], | |||||
[ -L/usr/X11R6/lib -lX11 -lXext ]) | [ -L/usr/X11R6/lib -lX11 -lXext ]) | ||||
fi | fi | ||||
AC_CHECK_LIB(sndfile,main, | |||||
[ XTRA="$XTRA jackrec" ], | |||||
[AC_MSG_WARN([*** the jackrec client will be skipped since you do not see to have libsndfile installed])] | |||||
) | |||||
AC_SUBST(XTRA) | AC_SUBST(XTRA) | ||||
AC_PROG_CXX | AC_PROG_CXX | ||||
@@ -57,12 +57,13 @@ typedef struct _jack_client_internal { | |||||
int subgraph_start_fd; | int subgraph_start_fd; | ||||
int subgraph_wait_fd; | int subgraph_wait_fd; | ||||
GSList *ports; /* protected by engine->graph_lock */ | GSList *ports; /* protected by engine->graph_lock */ | ||||
GSList *fed_by; /* protected by engine->graph_lock */ | |||||
int shm_id; | int shm_id; | ||||
int shm_key; | int shm_key; | ||||
unsigned long rank; | unsigned long rank; | ||||
struct _jack_client_internal *next_client; /* not a linked list! */ | struct _jack_client_internal *next_client; /* not a linked list! */ | ||||
dlhandle handle; | dlhandle handle; | ||||
} jack_client_internal_t; | } jack_client_internal_t; | ||||
static int jack_port_assign_buffer (jack_engine_t *, jack_port_internal_t *); | static int jack_port_assign_buffer (jack_engine_t *, jack_port_internal_t *); | ||||
@@ -246,14 +247,17 @@ jack_cleanup_clients (jack_engine_t *engine) | |||||
x++; | x++; | ||||
pthread_mutex_lock (&engine->graph_lock); | pthread_mutex_lock (&engine->graph_lock); | ||||
for (node = engine->clients; node; node = g_slist_next (node)) { | |||||
for (node = engine->clients; node; node = g_slist_next (node)) { | |||||
client = (jack_client_internal_t *) node->data; | client = (jack_client_internal_t *) node->data; | ||||
ctl = client->control; | ctl = client->control; | ||||
printf ("client %s state = %d\n", ctl->name, ctl->state); | |||||
if (ctl->state > JACK_CLIENT_STATE_NOT_TRIGGERED) { | if (ctl->state > JACK_CLIENT_STATE_NOT_TRIGGERED) { | ||||
remove = g_slist_prepend (remove, node->data); | remove = g_slist_prepend (remove, node->data); | ||||
printf ("%d: removing failed client %s\n", x, client->control->name); | |||||
printf ("%d: removing failed client %s\n", x, ctl->name); | |||||
} | } | ||||
} | } | ||||
pthread_mutex_unlock (&engine->graph_lock); | pthread_mutex_unlock (&engine->graph_lock); | ||||
@@ -370,7 +374,7 @@ jack_process (jack_engine_t *engine, nframes_t nframes) | |||||
struct pollfd pollfd[1]; | struct pollfd pollfd[1]; | ||||
char c; | char c; | ||||
// unsigned long then, now; | |||||
unsigned long then, now; | |||||
// rdtscl (then); | // rdtscl (then); | ||||
if (pthread_mutex_trylock (&engine->graph_lock) != 0) { | if (pthread_mutex_trylock (&engine->graph_lock) != 0) { | ||||
@@ -430,15 +434,18 @@ jack_process (jack_engine_t *engine, nframes_t nframes) | |||||
pollfd[0].fd = client->subgraph_wait_fd; | pollfd[0].fd = client->subgraph_wait_fd; | ||||
pollfd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; | pollfd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; | ||||
rdtscl (then); | |||||
if (poll (pollfd, 1, engine->driver->period_interval) < 0) { | if (poll (pollfd, 1, engine->driver->period_interval) < 0) { | ||||
jack_error ("engine cannot poll for graph completion (%s)", strerror (errno)); | jack_error ("engine cannot poll for graph completion (%s)", strerror (errno)); | ||||
err++; | err++; | ||||
break; | break; | ||||
} | } | ||||
rdtscl (now); | |||||
if (pollfd[0].revents == 0) { | if (pollfd[0].revents == 0) { | ||||
jack_error ("subgraph starting at %s timed out (state = %d)", | |||||
client->control->name, client->control->state); | |||||
jack_error ("subgraph starting at %s timed out (state = %d) (time = %f usecs)", | |||||
client->control->name, client->control->state, | |||||
((float)(now - then))/450.0f); | |||||
err++; | err++; | ||||
break; | break; | ||||
} else if (pollfd[0].revents & ~POLLIN) { | } else if (pollfd[0].revents & ~POLLIN) { | ||||
@@ -463,7 +470,6 @@ jack_process (jack_engine_t *engine, nframes_t nframes) | |||||
} | } | ||||
} | } | ||||
} | } | ||||
pthread_mutex_unlock (&engine->graph_lock); | pthread_mutex_unlock (&engine->graph_lock); | ||||
if (err) { | if (err) { | ||||
@@ -726,6 +732,8 @@ jack_client_disconnect (jack_engine_t *engine, jack_client_internal_t *client) | |||||
} | } | ||||
g_slist_free (client->ports); | g_slist_free (client->ports); | ||||
g_slist_free (client->fed_by); | |||||
client->fed_by = 0; | |||||
client->ports = 0; | client->ports = 0; | ||||
} | } | ||||
@@ -738,16 +746,12 @@ jack_client_deactivate (jack_engine_t *engine, jack_client_id_t id, int to_wait) | |||||
pthread_mutex_lock (&engine->graph_lock); | pthread_mutex_lock (&engine->graph_lock); | ||||
printf ("trying ... deactivating client id %d\n", id); | |||||
for (node = engine->clients; node; node = g_slist_next (node)) { | for (node = engine->clients; node; node = g_slist_next (node)) { | ||||
jack_client_internal_t *client = (jack_client_internal_t *) node->data; | jack_client_internal_t *client = (jack_client_internal_t *) node->data; | ||||
if (client->control->id == id) { | if (client->control->id == id) { | ||||
printf ("deactivating client id %d\n", id); | |||||
if (client == engine->timebase_client) { | if (client == engine->timebase_client) { | ||||
engine->timebase_client = 0; | engine->timebase_client = 0; | ||||
engine->control->frame_time = 0; | engine->control->frame_time = 0; | ||||
@@ -1192,7 +1196,7 @@ jack_audio_thread (void *arg) | |||||
{ | { | ||||
jack_engine_t *engine = (jack_engine_t *) arg; | jack_engine_t *engine = (jack_engine_t *) arg; | ||||
jack_driver_t *driver = engine->driver; | jack_driver_t *driver = engine->driver; | ||||
unsigned long start, end; | |||||
// unsigned long start, end; | |||||
if (engine->control->real_time) { | if (engine->control->real_time) { | ||||
jack_become_real_time (pthread_self(), engine->rtpriority); | jack_become_real_time (pthread_self(), engine->rtpriority); | ||||
@@ -1207,11 +1211,11 @@ jack_audio_thread (void *arg) | |||||
} | } | ||||
while (1) { | while (1) { | ||||
start = end; | |||||
// start = end; | |||||
if (driver->wait (driver)) { | if (driver->wait (driver)) { | ||||
break; | break; | ||||
} | } | ||||
rdtscl (end); | |||||
// rdtscl (end); | |||||
// printf ("driver cycle time: %.6f usecs\n", ((float) (end - start)) / 450.00f); | // printf ("driver cycle time: %.6f usecs\n", ((float) (end - start)) / 450.00f); | ||||
} | } | ||||
@@ -1298,6 +1302,7 @@ jack_client_internal_new (jack_engine_t *engine, int fd, jack_client_connect_req | |||||
client->request_fd = fd; | client->request_fd = fd; | ||||
client->event_fd = -1; | client->event_fd = -1; | ||||
client->ports = 0; | client->ports = 0; | ||||
client->fed_by = 0; | |||||
client->rank = UINT_MAX; | client->rank = UINT_MAX; | ||||
client->next_client = NULL; | client->next_client = NULL; | ||||
client->handle = NULL; | client->handle = NULL; | ||||
@@ -1378,7 +1383,6 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client) | |||||
} | } | ||||
jack_client_disconnect (engine, client); | jack_client_disconnect (engine, client); | ||||
jack_client_do_deactivate (engine, client); | |||||
for (node = engine->clients; node; node = g_slist_next (node)) { | for (node = engine->clients; node; node = g_slist_next (node)) { | ||||
if (((jack_client_internal_t *) node->data)->control->id == client->control->id) { | if (((jack_client_internal_t *) node->data)->control->id == client->control->id) { | ||||
@@ -1388,6 +1392,8 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client) | |||||
} | } | ||||
} | } | ||||
jack_client_do_deactivate (engine, client); | |||||
/* rearrange the pollfd array so that things work right the | /* rearrange the pollfd array so that things work right the | ||||
next time we go into poll(2). | next time we go into poll(2). | ||||
*/ | */ | ||||
@@ -1395,7 +1401,7 @@ jack_remove_client (jack_engine_t *engine, jack_client_internal_t *client) | |||||
for (i = 0; i < engine->pfd_max; i++) { | for (i = 0; i < engine->pfd_max; i++) { | ||||
if (engine->pfd[i].fd == client->request_fd) { | if (engine->pfd[i].fd == client->request_fd) { | ||||
if (i+1 < engine->pfd_max) { | if (i+1 < engine->pfd_max) { | ||||
memcpy (&engine->pfd[i], &engine->pfd[i+1], sizeof (struct pollfd) * (engine->pfd_max - i)); | |||||
memmove (&engine->pfd[i], &engine->pfd[i+1], sizeof (struct pollfd) * (engine->pfd_max - i)); | |||||
} | } | ||||
engine->pfd_max--; | engine->pfd_max--; | ||||
} | } | ||||
@@ -1584,6 +1590,12 @@ jack_rechain_graph (jack_engine_t *engine, int take_lock) | |||||
} | } | ||||
if (jack_client_is_inprocess (client)) { | if (jack_client_is_inprocess (client)) { | ||||
/* break the chain for the current subgraph. the server | |||||
will wait for chain on the nth FIFO, and will | |||||
then execute this in-process client. | |||||
*/ | |||||
if (subgraph_client) { | if (subgraph_client) { | ||||
subgraph_client->subgraph_wait_fd = jack_get_fifo_fd (engine, n); | subgraph_client->subgraph_wait_fd = jack_get_fifo_fd (engine, n); | ||||
} | } | ||||
@@ -1593,10 +1605,15 @@ jack_rechain_graph (jack_engine_t *engine, int take_lock) | |||||
} else { | } else { | ||||
if (subgraph_client == 0) { | if (subgraph_client == 0) { | ||||
/* start a new subgraph. the engine will start the chain | |||||
by writing to the nth FIFO. | |||||
*/ | |||||
subgraph_client = client; | subgraph_client = client; | ||||
subgraph_client->subgraph_start_fd = jack_get_fifo_fd (engine, n); | subgraph_client->subgraph_start_fd = jack_get_fifo_fd (engine, n); | ||||
} | |||||
} | |||||
if (set) { | if (set) { | ||||
jack_client_set_order (engine, client); | jack_client_set_order (engine, client); | ||||
} | } | ||||
@@ -1631,73 +1648,138 @@ jack_rechain_graph (jack_engine_t *engine, int take_lock) | |||||
return err; | return err; | ||||
} | } | ||||
int | |||||
jack_client_connected (jack_client_internal_t *a, jack_client_internal_t *b) | |||||
static void | |||||
jack_trace_terminal (jack_client_internal_t *c1, jack_client_internal_t *rbase) | |||||
{ | |||||
jack_client_internal_t *c2; | |||||
/* make a copy of the existing list of routes that feed c1 */ | |||||
GSList *existing; | |||||
GSList *node; | |||||
if (c1->fed_by == 0) { | |||||
return; | |||||
} | |||||
existing = g_slist_copy (c1->fed_by); | |||||
/* for each route that feeds c1, recurse, marking it as feeding | |||||
rbase as well. | |||||
*/ | |||||
for (node = existing; node; node = g_slist_next (node)) { | |||||
c2 = (jack_client_internal_t *) node->data; | |||||
/* c2 is a route that feeds c1 which somehow feeds base. mark | |||||
base as being fed by c2 | |||||
*/ | |||||
rbase->fed_by = g_slist_prepend (rbase->fed_by, c2); | |||||
if (c2 != rbase && c2 != c1) { | |||||
/* now recurse, so that we can mark base as being fed by | |||||
all routes that feed c2 | |||||
*/ | |||||
jack_trace_terminal (c2, rbase); | |||||
} | |||||
} | |||||
} | |||||
static int | |||||
jack_client_sort (jack_client_internal_t *a, jack_client_internal_t *b) | |||||
{ | { | ||||
GSList *pnode, *cnode; | |||||
int ret = 0; | |||||
/* the driver client always comes after everything else */ | |||||
if (a->control->type == ClientDriver) { | |||||
return 1; | |||||
} | |||||
/* Check every port on the first client for a connection to | |||||
the second client. | |||||
if (b->control->type == ClientDriver) { | |||||
return -1; | |||||
} | |||||
Return -1 if a should execute before b | |||||
Return 1 if a should execute after b | |||||
Return 0 if they are not connected (ie. it doesn't matter) | |||||
if (g_slist_find (a->fed_by, b)) { | |||||
/* a comes after b */ | |||||
return 1; | |||||
} else if (g_slist_find (b->fed_by, a)) { | |||||
/* b comes after a */ | |||||
return -1; | |||||
} else { | |||||
/* we don't care */ | |||||
return 0; | |||||
} | |||||
} | |||||
static int | |||||
jack_client_feeds (jack_client_internal_t *might, jack_client_internal_t *target) | |||||
{ | |||||
GSList *pnode, *cnode; | |||||
If there is a feedback loop between a and b, the result | |||||
is undefined (the first connected port will be used to | |||||
determine the result). This is "OK", since there is no | |||||
correct execution order in that case. | |||||
/* Check every port of `might' for an outbound connection to `target' | |||||
*/ | */ | ||||
for (pnode = a->ports; pnode; pnode = g_slist_next (pnode)) { | |||||
for (pnode = might->ports; pnode; pnode = g_slist_next (pnode)) { | |||||
jack_port_internal_t *port; | jack_port_internal_t *port; | ||||
port = (jack_port_internal_t *) pnode->data; | port = (jack_port_internal_t *) pnode->data; | ||||
for (cnode = port->connections; cnode; cnode = g_slist_next (cnode)) { | for (cnode = port->connections; cnode; cnode = g_slist_next (cnode)) { | ||||
jack_connection_internal_t *c; | jack_connection_internal_t *c; | ||||
c = (jack_connection_internal_t *) cnode->data; | c = (jack_connection_internal_t *) cnode->data; | ||||
if (c->source->shared->client_id == b->control->id) { | |||||
/* b is the source, so a should be | |||||
executed *after* b. | |||||
*/ | |||||
ret = 1; | |||||
break; | |||||
} else if (c->source->shared->client_id == a->control->id) { | |||||
/* a is the source, so a should be | |||||
executed *before* b | |||||
*/ | |||||
ret = -1; | |||||
break; | |||||
if (c->source->shared->client_id == might->control->id && | |||||
c->destination->shared->client_id == target->control->id) { | |||||
return 1; | |||||
} | } | ||||
} | } | ||||
if (ret) { | |||||
break; | |||||
} | |||||
} | } | ||||
return ret; | |||||
return 0; | |||||
} | } | ||||
static void | static void | ||||
jack_sort_graph (jack_engine_t *engine, int take_lock) | jack_sort_graph (jack_engine_t *engine, int take_lock) | ||||
{ | { | ||||
GSList *node, *onode; | |||||
jack_client_internal_t *client; | |||||
jack_client_internal_t *oclient; | |||||
if (take_lock) { | if (take_lock) { | ||||
pthread_mutex_lock (&engine->graph_lock); | pthread_mutex_lock (&engine->graph_lock); | ||||
} | } | ||||
engine->clients = g_slist_sort (engine->clients, (GCompareFunc) jack_client_connected); | |||||
for (node = engine->clients; node; node = g_slist_next (node)) { | |||||
client = (jack_client_internal_t *) node->data; | |||||
g_slist_free (client->fed_by); | |||||
client->fed_by = 0; | |||||
for (onode = engine->clients; onode; onode = g_slist_next (onode)) { | |||||
oclient = (jack_client_internal_t *) onode->data; | |||||
if (jack_client_feeds (oclient, client)) { | |||||
client->fed_by = g_slist_prepend (client->fed_by, oclient); | |||||
} | |||||
} | |||||
} | |||||
for (node = engine->clients; node; node = g_slist_next (node)) { | |||||
jack_trace_terminal ((jack_client_internal_t *) node->data, | |||||
(jack_client_internal_t *) node->data); | |||||
} | |||||
engine->clients = g_slist_sort (engine->clients, (GCompareFunc) jack_client_sort); | |||||
jack_rechain_graph (engine, FALSE); | jack_rechain_graph (engine, FALSE); | ||||
if (take_lock) { | if (take_lock) { | ||||
@@ -1755,6 +1837,7 @@ jack_port_do_connect (jack_engine_t *engine, | |||||
if (dstport->shared->type_info.mixdown == NULL && dstport->connections) { | if (dstport->shared->type_info.mixdown == NULL && dstport->connections) { | ||||
jack_error ("cannot make multiple connections to a port of type [%s]", dstport->shared->type_info.type_name); | jack_error ("cannot make multiple connections to a port of type [%s]", dstport->shared->type_info.type_name); | ||||
free (connection); | free (connection); | ||||
return -1; | |||||
} else { | } else { | ||||
dstport->connections = g_slist_prepend (dstport->connections, connection); | dstport->connections = g_slist_prepend (dstport->connections, connection); | ||||
srcport->connections = g_slist_prepend (srcport->connections, connection); | srcport->connections = g_slist_prepend (srcport->connections, connection); | ||||
@@ -1811,6 +1894,10 @@ jack_port_disconnect_internal (jack_engine_t *engine, | |||||
jack_sort_graph (engine, FALSE); | jack_sort_graph (engine, FALSE); | ||||
} | } | ||||
if (ret == -1) { | |||||
printf ("disconnect failed\n"); | |||||
} | |||||
return ret; | return ret; | ||||
} | } | ||||
@@ -1887,7 +1974,7 @@ jack_get_fifo_fd (jack_engine_t *engine, int which_fifo) | |||||
return -1; | return -1; | ||||
} | } | ||||
} | } | ||||
return engine->fifo[which_fifo]; | return engine->fifo[which_fifo]; | ||||
} | } | ||||
@@ -2191,8 +2278,6 @@ jack_send_connection_notification (jack_engine_t *engine, jack_client_id_t clien | |||||
return -1; | return -1; | ||||
} | } | ||||
fprintf (stderr, "sending connection to client %s\n", client->control->name); | |||||
event.type = (connected ? PortConnected : PortDisconnected); | event.type = (connected ? PortConnected : PortDisconnected); | ||||
event.x.self_id = self_id; | event.x.self_id = self_id; | ||||
event.y.other_id = other_id; | event.y.other_id = other_id; | ||||
@@ -11,8 +11,8 @@ extern "C" | |||||
#include <FL/Fl_Window.H> | #include <FL/Fl_Window.H> | ||||
#include <FL/Fl_Slider.H> | #include <FL/Fl_Slider.H> | ||||
jack_port_t *my_input_port; | |||||
jack_port_t *my_output_port; | |||||
jack_port_t *input_port; | |||||
jack_port_t *output_port; | |||||
float gain = 0.0; /* slider starts out with zero gain */ | float gain = 0.0; /* slider starts out with zero gain */ | ||||
@@ -20,8 +20,8 @@ int | |||||
process (nframes_t nframes, void *arg) | process (nframes_t nframes, void *arg) | ||||
{ | { | ||||
sample_t *out = (sample_t *) jack_port_get_buffer (my_output_port, nframes); | |||||
sample_t *in = (sample_t *) jack_port_get_buffer (my_input_port, nframes); | |||||
sample_t *out = (sample_t *) jack_port_get_buffer (output_port, nframes); | |||||
sample_t *in = (sample_t *) jack_port_get_buffer (input_port, nframes); | |||||
while (nframes--) | while (nframes--) | ||||
out[nframes] = in[nframes] * gain; | out[nframes] = in[nframes] * gain; | ||||
@@ -72,8 +72,8 @@ main (int argc, char *argv[]) | |||||
printf ("engine sample rate: %lu\n", jack_get_sample_rate (client)); | printf ("engine sample rate: %lu\n", jack_get_sample_rate (client)); | ||||
my_input_port = jack_port_register (client, "myinput", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); | |||||
my_output_port = jack_port_register (client, "myoutput", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |||||
input_port = jack_port_register (client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); | |||||
output_port = jack_port_register (client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |||||
if (jack_activate (client)) { | if (jack_activate (client)) { | ||||
fprintf (stderr, "cannot activate client"); | fprintf (stderr, "cannot activate client"); | ||||
@@ -81,12 +81,11 @@ main (int argc, char *argv[]) | |||||
printf ("client activated\n"); | printf ("client activated\n"); | ||||
if (jack_port_connect (client, "ALSA I/O:Input 1", "myinput")) { | |||||
if (jack_port_connect (client, "ALSA I/O:Input 1", jack_port_name (input_port))) { | |||||
fprintf (stderr, "cannot connect input ports\n"); | fprintf (stderr, "cannot connect input ports\n"); | ||||
} | } | ||||
if (jack_port_connect (client, "myoutput", "ALSA I/O:Output 1")) { | |||||
if (jack_port_connect (client, jack_port_name (output_port), "ALSA I/O:Output 1")) { | |||||
fprintf (stderr, "cannot connect output ports\n"); | fprintf (stderr, "cannot connect output ports\n"); | ||||
} | } | ||||
@@ -49,7 +49,7 @@ typedef struct { | |||||
char **capture_addr; | char **capture_addr; | ||||
const snd_pcm_channel_area_t *capture_areas; | const snd_pcm_channel_area_t *capture_areas; | ||||
const snd_pcm_channel_area_t *playback_areas; | const snd_pcm_channel_area_t *playback_areas; | ||||
unsigned long time_at_interrupt; | |||||
unsigned long long time_at_interrupt; | |||||
struct pollfd pfd; | struct pollfd pfd; | ||||
unsigned long interleave_unit; | unsigned long interleave_unit; | ||||
unsigned long capture_interleave_skip; | unsigned long capture_interleave_skip; | ||||
@@ -29,7 +29,23 @@ extern "C" { | |||||
#include <jack/error.h> | #include <jack/error.h> | ||||
jack_client_t *jack_client_new (const char *client_name); | jack_client_t *jack_client_new (const char *client_name); | ||||
int jack_client_close (jack_client_t *client); | |||||
int jack_client_close (jack_client_t *client); | |||||
/* register a function (and argument) to be called if and when the | |||||
JACK server shuts down the client thread. the function must | |||||
be written as if it were an asynchonrous POSIX signal | |||||
handler - use only async functions, and remember that it | |||||
is executed from another thread. a typical function might | |||||
set a flag or write to a pipe so that the rest of the | |||||
application knows that the JACK client thread has shut | |||||
down. | |||||
NOTE: clients do not need to call this. it exists only | |||||
to help more complex clients understand what is going | |||||
on. if called, it must be called before jack_client_activate(). | |||||
*/ | |||||
void jack_on_shutdown (jack_client_t *, void (*function)(void *arg), void *arg); | |||||
int jack_set_process_callback (jack_client_t *, JackProcessCallback, void *arg); | int jack_set_process_callback (jack_client_t *, JackProcessCallback, void *arg); | ||||
int jack_set_buffer_size_callback (jack_client_t *, JackBufferSizeCallback, void *arg); | int jack_set_buffer_size_callback (jack_client_t *, JackBufferSizeCallback, void *arg); | ||||
@@ -107,6 +123,10 @@ jack_port_register (jack_client_t *, | |||||
int jack_port_unregister (jack_client_t *, jack_port_t *); | int jack_port_unregister (jack_client_t *, jack_port_t *); | ||||
/* a port is an opaque type, and its name is not inferable */ | |||||
const char * jack_port_name (const jack_port_t *port); | |||||
/* This returns a pointer to the memory area associated with the | /* This returns a pointer to the memory area associated with the | ||||
specified port. It can only be called from within the client's | specified port. It can only be called from within the client's | ||||
"process" callback. For an output port, it will be a memory area | "process" callback. For an output port, it will be a memory area | ||||
@@ -217,6 +237,7 @@ jack_port_t **jack_get_ports (jack_client_t *, | |||||
int jack_engine_takeover_timebase (jack_client_t *); | int jack_engine_takeover_timebase (jack_client_t *); | ||||
void jack_update_time (jack_client_t *, nframes_t); | void jack_update_time (jack_client_t *, nframes_t); | ||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
} | } | ||||
#endif | #endif | ||||
@@ -4,15 +4,15 @@ | |||||
#include <jack/jack.h> | #include <jack/jack.h> | ||||
jack_port_t *my_input_port; | |||||
jack_port_t *my_output_port; | |||||
jack_port_t *input_port; | |||||
jack_port_t *output_port; | |||||
int | int | ||||
process (nframes_t nframes, void *arg) | process (nframes_t nframes, void *arg) | ||||
{ | { | ||||
sample_t *out = (sample_t *) jack_port_get_buffer (my_output_port, nframes); | |||||
sample_t *in = (sample_t *) jack_port_get_buffer (my_input_port, nframes); | |||||
sample_t *out = (sample_t *) jack_port_get_buffer (output_port, nframes); | |||||
sample_t *in = (sample_t *) jack_port_get_buffer (input_port, nframes); | |||||
memcpy (out, in, sizeof (sample_t) * nframes); | memcpy (out, in, sizeof (sample_t) * nframes); | ||||
@@ -57,8 +57,8 @@ main (int argc, char *argv[]) | |||||
printf ("engine sample rate: %lu\n", jack_get_sample_rate (client)); | printf ("engine sample rate: %lu\n", jack_get_sample_rate (client)); | ||||
my_input_port = jack_port_register (client, "myinput", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); | |||||
my_output_port = jack_port_register (client, "myoutput", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |||||
input_port = jack_port_register (client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); | |||||
output_port = jack_port_register (client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |||||
if (jack_activate (client)) { | if (jack_activate (client)) { | ||||
fprintf (stderr, "cannot activate client"); | fprintf (stderr, "cannot activate client"); | ||||
@@ -66,11 +66,11 @@ main (int argc, char *argv[]) | |||||
printf ("client activated\n"); | printf ("client activated\n"); | ||||
if (jack_port_connect (client, "ALSA I/O:Input 1", "myinput")) { | |||||
if (jack_port_connect (client, "ALSA I/O:Input 1", jack_port_name (input_port))) { | |||||
fprintf (stderr, "cannot connect input ports\n"); | fprintf (stderr, "cannot connect input ports\n"); | ||||
} | } | ||||
if (jack_port_connect (client, "myoutput", "ALSA I/O:Output 1")) { | |||||
if (jack_port_connect (client, jack_port_name (output_port), "ALSA I/O:Output 1")) { | |||||
fprintf (stderr, "cannot connect output ports\n"); | fprintf (stderr, "cannot connect output ports\n"); | ||||
} | } | ||||