git-svn-id: svn+ssh://jackaudio.org/trunk/jack@46 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.109.0
@@ -7,6 +7,7 @@ libtool | |||
jackd | |||
jack_simple_client | |||
jack_monitor_client | |||
jackrec | |||
*.la | |||
*.pc | |||
Makefile | |||
@@ -4,9 +4,9 @@ MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure config.h.in \ | |||
missing install-sh config.sub ltconfig \ | |||
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@ | |||
@@ -26,6 +26,9 @@ jack_monitor_client_LDFLAGS = -L. -ljack -lltdl -lpthread | |||
jack_fltk_client_SOURCES = fltk_client.cc | |||
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 | |||
libjack_la_SOURCES = client.c pool.c driver.c | |||
@@ -38,9 +38,9 @@ | |||
static int config_max_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; | |||
} | |||
@@ -704,6 +704,7 @@ alsa_driver_wait (alsa_driver_t *driver) | |||
channel_t chn; | |||
GSList *node; | |||
sample_t *buffer; | |||
// unsigned long long end; | |||
again: | |||
if (poll (&driver->pfd, 1, 1000) < 0) { | |||
@@ -844,6 +845,9 @@ alsa_driver_wait (alsa_driver_t *driver) | |||
avail -= contiguous; | |||
} | |||
// end = current_usecs(); | |||
// printf ("entire cycle took %f usecs\n", ((float)(end - driver->time_at_interrupt))/450.0f); | |||
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; | |||
pthread_t thread; | |||
char fifo_prefix[FIFO_NAME_SIZE+1]; | |||
void (*on_shutdown)(void *arg); | |||
void *on_shutdown_arg; | |||
char thread_ok : 1; | |||
char first_active : 1; | |||
}; | |||
@@ -100,7 +102,7 @@ jack_client_alloc () | |||
client->control = 0; | |||
client->thread_ok = FALSE; | |||
client->first_active = TRUE; | |||
client->on_shutdown = NULL; | |||
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)); | |||
return -1; | |||
} | |||
return 0; | |||
} | |||
@@ -478,6 +480,9 @@ jack_client_thread (void *arg) | |||
if (client->pollfd[0].revents & ~POLLIN) { | |||
jack_error ("engine has shut down socket; thread exiting"); | |||
if (client->on_shutdown) { | |||
client->on_shutdown (client->on_shutdown_arg); | |||
} | |||
pthread_exit (0); | |||
} | |||
@@ -575,11 +580,11 @@ jack_client_thread (void *arg) | |||
} | |||
/* 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 | |||
effort. | |||
*/ | |||
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"); | |||
return -1; | |||
} | |||
return req.status; | |||
} | |||
@@ -1220,3 +1225,15 @@ jack_port_request_monitor (jack_client_t *client, const char *port_name, int ono | |||
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_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_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 | |||
AC_CHECK_LIB(fltk,main, | |||
[ 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 ]) | |||
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_PROG_CXX | |||
@@ -57,12 +57,13 @@ typedef struct _jack_client_internal { | |||
int subgraph_start_fd; | |||
int subgraph_wait_fd; | |||
GSList *ports; /* protected by engine->graph_lock */ | |||
GSList *fed_by; /* protected by engine->graph_lock */ | |||
int shm_id; | |||
int shm_key; | |||
unsigned long rank; | |||
struct _jack_client_internal *next_client; /* not a linked list! */ | |||
dlhandle handle; | |||
} jack_client_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++; | |||
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; | |||
ctl = client->control; | |||
printf ("client %s state = %d\n", ctl->name, ctl->state); | |||
if (ctl->state > JACK_CLIENT_STATE_NOT_TRIGGERED) { | |||
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); | |||
@@ -370,7 +374,7 @@ jack_process (jack_engine_t *engine, nframes_t nframes) | |||
struct pollfd pollfd[1]; | |||
char c; | |||
// unsigned long then, now; | |||
unsigned long then, now; | |||
// rdtscl (then); | |||
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].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; | |||
rdtscl (then); | |||
if (poll (pollfd, 1, engine->driver->period_interval) < 0) { | |||
jack_error ("engine cannot poll for graph completion (%s)", strerror (errno)); | |||
err++; | |||
break; | |||
} | |||
rdtscl (now); | |||
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++; | |||
break; | |||
} 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); | |||
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->fed_by); | |||
client->fed_by = 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); | |||
printf ("trying ... deactivating client id %d\n", id); | |||
for (node = engine->clients; node; node = g_slist_next (node)) { | |||
jack_client_internal_t *client = (jack_client_internal_t *) node->data; | |||
if (client->control->id == id) { | |||
printf ("deactivating client id %d\n", id); | |||
if (client == engine->timebase_client) { | |||
engine->timebase_client = 0; | |||
engine->control->frame_time = 0; | |||
@@ -1192,7 +1196,7 @@ jack_audio_thread (void *arg) | |||
{ | |||
jack_engine_t *engine = (jack_engine_t *) arg; | |||
jack_driver_t *driver = engine->driver; | |||
unsigned long start, end; | |||
// unsigned long start, end; | |||
if (engine->control->real_time) { | |||
jack_become_real_time (pthread_self(), engine->rtpriority); | |||
@@ -1207,11 +1211,11 @@ jack_audio_thread (void *arg) | |||
} | |||
while (1) { | |||
start = end; | |||
// start = end; | |||
if (driver->wait (driver)) { | |||
break; | |||
} | |||
rdtscl (end); | |||
// rdtscl (end); | |||
// 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->event_fd = -1; | |||
client->ports = 0; | |||
client->fed_by = 0; | |||
client->rank = UINT_MAX; | |||
client->next_client = 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_do_deactivate (engine, client); | |||
for (node = engine->clients; node; node = g_slist_next (node)) { | |||
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 | |||
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++) { | |||
if (engine->pfd[i].fd == client->request_fd) { | |||
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--; | |||
} | |||
@@ -1584,6 +1590,12 @@ jack_rechain_graph (jack_engine_t *engine, int take_lock) | |||
} | |||
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) { | |||
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 { | |||
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->subgraph_start_fd = jack_get_fifo_fd (engine, n); | |||
} | |||
} | |||
if (set) { | |||
jack_client_set_order (engine, client); | |||
} | |||
@@ -1631,73 +1648,138 @@ jack_rechain_graph (jack_engine_t *engine, int take_lock) | |||
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; | |||
port = (jack_port_internal_t *) pnode->data; | |||
for (cnode = port->connections; cnode; cnode = g_slist_next (cnode)) { | |||
jack_connection_internal_t *c; | |||
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 | |||
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) { | |||
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); | |||
if (take_lock) { | |||
@@ -1755,6 +1837,7 @@ jack_port_do_connect (jack_engine_t *engine, | |||
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); | |||
free (connection); | |||
return -1; | |||
} else { | |||
dstport->connections = g_slist_prepend (dstport->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); | |||
} | |||
if (ret == -1) { | |||
printf ("disconnect failed\n"); | |||
} | |||
return ret; | |||
} | |||
@@ -1887,7 +1974,7 @@ jack_get_fifo_fd (jack_engine_t *engine, int which_fifo) | |||
return -1; | |||
} | |||
} | |||
return engine->fifo[which_fifo]; | |||
} | |||
@@ -2191,8 +2278,6 @@ jack_send_connection_notification (jack_engine_t *engine, jack_client_id_t clien | |||
return -1; | |||
} | |||
fprintf (stderr, "sending connection to client %s\n", client->control->name); | |||
event.type = (connected ? PortConnected : PortDisconnected); | |||
event.x.self_id = self_id; | |||
event.y.other_id = other_id; | |||
@@ -11,8 +11,8 @@ extern "C" | |||
#include <FL/Fl_Window.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 */ | |||
@@ -20,8 +20,8 @@ int | |||
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--) | |||
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)); | |||
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)) { | |||
fprintf (stderr, "cannot activate client"); | |||
@@ -81,12 +81,11 @@ main (int argc, char *argv[]) | |||
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"); | |||
} | |||
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"); | |||
} | |||
@@ -49,7 +49,7 @@ typedef struct { | |||
char **capture_addr; | |||
const snd_pcm_channel_area_t *capture_areas; | |||
const snd_pcm_channel_area_t *playback_areas; | |||
unsigned long time_at_interrupt; | |||
unsigned long long time_at_interrupt; | |||
struct pollfd pfd; | |||
unsigned long interleave_unit; | |||
unsigned long capture_interleave_skip; | |||
@@ -29,7 +29,23 @@ extern "C" { | |||
#include <jack/error.h> | |||
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_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 *); | |||
/* 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 | |||
specified port. It can only be called from within the client's | |||
"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 *); | |||
void jack_update_time (jack_client_t *, nframes_t); | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
@@ -4,15 +4,15 @@ | |||
#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 | |||
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); | |||
@@ -57,8 +57,8 @@ main (int argc, char *argv[]) | |||
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)) { | |||
fprintf (stderr, "cannot activate client"); | |||
@@ -66,11 +66,11 @@ main (int argc, char *argv[]) | |||
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"); | |||
} | |||
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"); | |||
} | |||