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