diff --git a/.cvsignore b/.cvsignore index 40d5a88..57c98c4 100644 --- a/.cvsignore +++ b/.cvsignore @@ -7,6 +7,7 @@ libtool jackd jack_simple_client jack_monitor_client +jackrec *.la *.pc Makefile diff --git a/Makefile.am b/Makefile.am index 24e0996..3077764 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/alsa_driver.c b/alsa_driver.c index 28d19a7..3f82d82 100644 --- a/alsa_driver.c +++ b/alsa_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; } diff --git a/capture_client.c b/capture_client.c new file mode 100644 index 0000000..aa652cd --- /dev/null +++ b/capture_client.c @@ -0,0 +1,319 @@ +#include +#include +#include +#include +#include +#include + +#include + +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); +} diff --git a/client.c b/client.c index 1b9185c..067ea59 100644 --- a/client.c +++ b/client.c @@ -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; +} diff --git a/configure.in b/configure.in index d5f4a51..17de9ee 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/engine.c b/engine.c index 3fb379f..5b96abd 100644 --- a/engine.c +++ b/engine.c @@ -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; diff --git a/fltk_client.cc b/fltk_client.cc index e6e1402..ae84f3c 100644 --- a/fltk_client.cc +++ b/fltk_client.cc @@ -11,8 +11,8 @@ extern "C" #include #include -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"); } diff --git a/jack/alsa_driver.h b/jack/alsa_driver.h index a8bb759..06228f0 100644 --- a/jack/alsa_driver.h +++ b/jack/alsa_driver.h @@ -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; diff --git a/jack/jack.h b/jack/jack.h index 86f0183..aa0e593 100644 --- a/jack/jack.h +++ b/jack/jack.h @@ -29,7 +29,23 @@ extern "C" { #include 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 diff --git a/simple_client.c b/simple_client.c index 223fdb0..8a5cca1 100644 --- a/simple_client.c +++ b/simple_client.c @@ -4,15 +4,15 @@ #include -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"); }