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