From b0a3ae949c09414b054e51a7c6aeb804af57b4f5 Mon Sep 17 00:00:00 2001 From: paul Date: Thu, 9 Jul 2009 18:34:47 +0000 Subject: [PATCH] move jack capture into example clients (its not a finished tool) ; update embedded docs for some jack frame time functions, and use direct form in jack_last_frame_tim() ; use fprintf() and not jack_info when printing DEBUG_WAKEUP output git-svn-id: svn+ssh://jackaudio.org/trunk/jack@3591 0c269be4-1314-0410-8aa9-9f06e86f4224 --- drivers/alsa/alsa_driver.c | 18 +- example-clients/Makefile.am | 32 +++- jack/jack.h | 50 +++--- jackd/engine.c | 2 +- libjack/transclient.c | 15 +- tools/Makefile.am | 19 -- tools/capture_client.c | 344 ------------------------------------ 7 files changed, 66 insertions(+), 414 deletions(-) delete mode 100644 tools/capture_client.c diff --git a/drivers/alsa/alsa_driver.c b/drivers/alsa/alsa_driver.c index c56051a..51009fa 100644 --- a/drivers/alsa/alsa_driver.c +++ b/drivers/alsa/alsa_driver.c @@ -1290,6 +1290,7 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float strerror (errno)); *status = -3; return 0; + } @@ -1306,9 +1307,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float } #ifdef DEBUG_WAKEUP - jack_info ("%" PRIu64 ": checked %d fds, %" PRIu64 - " usecs since poll entered", poll_ret, nfds, - poll_ret - poll_enter); + fprintf (stderr, "%" PRIu64 ": checked %d fds, started at %" PRIu64 " %" PRIu64 " usecs since poll entered\n", + poll_ret, nfds, poll_enter, poll_ret - poll_enter); #endif /* check to see if it was the extra FD that caused us @@ -1345,8 +1345,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float if (revents & POLLOUT) { need_playback = 0; #ifdef DEBUG_WAKEUP - jack_info ("%" PRIu64 - " playback stream ready", + fprintf (stderr, "%" PRIu64 + " playback stream ready\n", poll_ret); #endif } @@ -1368,8 +1368,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float if (revents & POLLIN) { need_capture = 0; #ifdef DEBUG_WAKEUP - jack_info ("%" PRIu64 - " capture stream ready", + fprintf (stderr, "%" PRIu64 + " capture stream ready\n", poll_ret); #endif } @@ -1426,8 +1426,8 @@ alsa_driver_wait (alsa_driver_t *driver, int extra_fd, int *status, float avail = capture_avail < playback_avail ? capture_avail : playback_avail; #ifdef DEBUG_WAKEUP - jack_info ("wakeup complete, avail = %lu, pavail = %lu " - "cavail = %lu", + fprintf (stderr, "wakeup complete, avail = %lu, pavail = %lu " + "cavail = %lu\n", avail, playback_avail, capture_avail); #endif diff --git a/example-clients/Makefile.am b/example-clients/Makefile.am index 8eddc4c..03fda4b 100644 --- a/example-clients/Makefile.am +++ b/example-clients/Makefile.am @@ -1,20 +1,28 @@ MAINTAINERCLEANFILES = Makefile.in +if HAVE_SNDFILE +JACKREC = jackrec +dist-check-sndfile: +else +JACKREC = +dist-check-sndfile: + @echo + @echo ' ******' You need sndfile installed to make dist.' ******' + @echo + @false +endif + bin_PROGRAMS = jack_simple_client \ jack_transport_client \ jack_impulse_grabber \ jack_metro \ jack_showtime \ - jack_midisine \ - jack_midiseq + jack_midisine \ + jack_midiseq \ + $(JACKREC) -if HAVE_SNDFILE -# note! jackrec_CFLAGS syntax not supported by automake-1.4 -sndfile_cflags = @SNDFILE_CFLAGS@ -endif - -AM_CFLAGS = -I.. $(JACK_CFLAGS) $(sndfile_cflags) -AM_CXXFLAGS = -I.. $(JACK_CFLAGS) $(sndfile_cflags) +AM_CFLAGS = -I.. $(JACK_CFLAGS) +AM_CXXFLAGS = -I.. $(JACK_CFLAGS) jack_simple_client_SOURCES = simple_client.c jack_simple_client_LDFLAGS = @OS_LDFLAGS@ @@ -44,6 +52,12 @@ jack_midisine_SOURCES = midisine.c jack_midisine_LDFLAGS = @OS_LDFLAGS@ jack_midisine_LDADD = $(top_builddir)/libjack/libjack.la +if HAVE_SNDFILE +jackrec_SOURCES = capture_client.c +jackrec_LDFLAGS = @SNDFILE_LIBS@ @OS_LDFLAGS@ +jackrec_LDADD = $(top_builddir)/libjack/libjack.la +endif + # # sample in-process client(s) # diff --git a/jack/jack.h b/jack/jack.h index 64ba8a7..3415905 100644 --- a/jack/jack.h +++ b/jack/jack.h @@ -508,15 +508,7 @@ int jack_port_unregister (jack_client_t *, jack_port_t *); * zero-filled. if there are multiple inbound connections, the data * will be mixed appropriately. * - * FOR OUTPUT PORTS ONLY : WILL BE DEPRECATED in Jack 2.0 !! - * --------------------------------------------------------- - * You may cache the value returned, but only between calls to - * your "blocksize" callback. For this reason alone, you should - * either never cache the return value or ensure you have - * a "blocksize" callback and be sure to invalidate the cached - * address from there. - * - * Caching output ports is DEPRECATED in Jack 2.0, due to some new optimization (like "pipelining"). + * Do not cache the returned address across process() callbacks. * Port buffers have to be retrieved in each callback for proper functionning. */ void *jack_port_get_buffer (jack_port_t *, jack_nframes_t); @@ -865,47 +857,57 @@ jack_port_t *jack_port_by_id (jack_client_t *client, /** * @defgroup TimeFunctions Handling time * @{ + * + * JACK time is in units of 'frames', according to the current sample rate. + * The absolute value of frame times is meaningless, frame times have meaning + * only relative to each other. */ /** - * @return the time in frames that has passed since the JACK server - * began the current process cycle. + * @return the estimated time in frames that has passed since the JACK + * server began the current process cycle. */ jack_nframes_t jack_frames_since_cycle_start (const jack_client_t *); /** - * @return an estimate of the current time in frames. This is a - * running counter, no significance should be attached to its value, - * but it can be compared to a previously returned value. + * @return the estimated current time in frames. + * This function is intended for use in other threads (not the process + * callback). The return value can be compared with the value of + * jack_last_frame_time to relate time in other threads to JACK time. */ jack_nframes_t jack_frame_time (const jack_client_t *); /** - * @return the frame_time after the last processing of the graph - * this is only to be used from the process callback. - * - * This function can be used to put timestamps generated by - * jack_frame_time() in correlation to the current process cycle. + * @return the precise time at the start of the current process cycle. + * This function may only be used from the process callback, and can + * be used to interpret timestamps generated by jack_frame_time() in + * other threads with respect to the current process cycle. + * + * This is the only jack time function that returns exact time: + * when used during the process callback it always returns the same + * value (until the next process callback, where it will return + * that value + nframes, etc). The return value is guaranteed to be + * monotonic and linear in this fashion unless an xrun occurs. + * If an xrun occurs, clients must check this value again, as time + * may have advanced in a non-linear way (e.g. cycles may have been skipped). */ jack_nframes_t jack_last_frame_time (const jack_client_t *client); /** - * @return estimated time in microseconds of the specified frame time + * @return the estimated time in microseconds of the specified frame time */ jack_time_t jack_frames_to_time(const jack_client_t *client, jack_nframes_t); /** - * @return estimated time in frames for the specified system time. + * @return the estimated time in frames for the specified system time. */ jack_nframes_t jack_time_to_frames(const jack_client_t *client, jack_time_t); /** * @return return JACK's current system time in microseconds, - * using JACK clock source. + * using the JACK clock source. * * The value returned is guaranteed to be monotonic, but not linear. - * - * This function is a client version of jack_get_microseconds(). */ jack_time_t jack_get_time(); diff --git a/jackd/engine.c b/jackd/engine.c index d3f7263..70f61a6 100644 --- a/jackd/engine.c +++ b/jackd/engine.c @@ -2270,7 +2270,7 @@ jack_run_cycle (jack_engine_t *engine, jack_nframes_t nframes, timer->current_wakeup = engine->driver->last_wait_ust; timer->next_wakeup = engine->driver->last_wait_ust + engine->driver->period_usecs; - + timer->reset_pending = 0; } else { diff --git a/libjack/transclient.c b/libjack/transclient.c index 267e98b..f449003 100644 --- a/libjack/transclient.c +++ b/libjack/transclient.c @@ -248,13 +248,14 @@ jack_time_to_frames(const jack_client_t *client, jack_time_t now) if (time.initialized) { #if 0 - jack_info ("now = %Lu current wakeup = %Lu next = %Lu frames = %lu + %f => %lu", + jack_info ("now = %Lu current wakeup = %Lu next = %Lu frames = %lu + %f => %lu FC = %f SOI = %f", now, time.current_wakeup, time.next_wakeup, time.frames, - (double) (now - time.current_wakeup)/ - (time.next_wakeup - time.current_wakeup), + (double) (now - time.current_wakeup)/ (time.next_wakeup - time.current_wakeup), time.frames + - (long) rint (((double) (now - time.current_wakeup)/ - (time.next_wakeup - time.current_wakeup)) * ectl->buffer_size)); + (long) rint (((double) ((long long) now - time.current_wakeup)/ + (long long) (time.next_wakeup - time.current_wakeup)) * ectl->buffer_size), + time.filter_coefficient, + time.second_order_integrator); #endif return time.frames + @@ -274,9 +275,7 @@ jack_frame_time (const jack_client_t *client) jack_nframes_t jack_last_frame_time (const jack_client_t *client) { - jack_frame_timer_t current; - jack_read_frame_time (client, ¤t); - return current.frames; + return client->engine->frame_timer.frames; } jack_time_t diff --git a/tools/Makefile.am b/tools/Makefile.am index 250767d..45b2193 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,17 +1,5 @@ MAINTAINERCLEANFILES = Makefile.in -if HAVE_SNDFILE -JACKREC = jackrec -dist-check-sndfile: -else -JACKREC = -dist-check-sndfile: - @echo - @echo ' ******' You need sndfile installed to make dist.' ******' - @echo - @false -endif - if HAVE_READLINE JACK_TRANSPORT = jack_transport dist-check-readline: @@ -48,7 +36,6 @@ bin_PROGRAMS = jack_load \ jack_freewheel \ jack_evmon \ jack_alias \ - $(JACKREC) \ $(JACK_TRANSPORT) \ $(NETJACK_TOOLS) @@ -94,12 +81,6 @@ jack_freewheel_SOURCES = freewheel.c jack_freewheel_LDFLAGS = @OS_LDFLAGS@ jack_freewheel_LDADD = $(top_builddir)/libjack/libjack.la -if HAVE_SNDFILE -jackrec_SOURCES = capture_client.c -jackrec_LDFLAGS = @SNDFILE_LIBS@ @OS_LDFLAGS@ -jackrec_LDADD = $(top_builddir)/libjack/libjack.la -endif - if HAVE_READLINE jack_transport_SOURCES = transport.c jack_transport_LDFLAGS = -lreadline @READLINE_DEPS@ @OS_LDFLAGS@ diff --git a/tools/capture_client.c b/tools/capture_client.c deleted file mode 100644 index bcbc951..0000000 --- a/tools/capture_client.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - Copyright (C) 2001 Paul Davis - Copyright (C) 2003 Jack O'Quin - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - * 2002/08/23 - modify for libsndfile 1.0.0 - * 2003/05/26 - use ringbuffers - joq - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef struct _thread_info { - pthread_t thread_id; - SNDFILE *sf; - jack_nframes_t duration; - jack_nframes_t rb_size; - jack_client_t *client; - unsigned int channels; - int bitdepth; - char *path; - volatile int can_capture; - volatile int can_process; - volatile int status; -} jack_thread_info_t; - -/* JACK data */ -unsigned int nports; -jack_port_t **ports; -jack_default_audio_sample_t **in; -jack_nframes_t nframes; -const size_t sample_size = sizeof(jack_default_audio_sample_t); - -/* Synchronization between process thread and disk thread. */ -#define DEFAULT_RB_SIZE 16384 /* ringbuffer size in frames */ -jack_ringbuffer_t *rb; -pthread_mutex_t disk_thread_lock = PTHREAD_MUTEX_INITIALIZER; -pthread_cond_t data_ready = PTHREAD_COND_INITIALIZER; -long overruns = 0; - - -void * -disk_thread (void *arg) -{ - jack_thread_info_t *info = (jack_thread_info_t *) arg; - static jack_nframes_t total_captured = 0; - jack_nframes_t samples_per_frame = info->channels; - size_t bytes_per_frame = samples_per_frame * sample_size; - void *framebuf = malloc (bytes_per_frame); - - pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); - pthread_mutex_lock (&disk_thread_lock); - - info->status = 0; - - while (1) { - - /* Write the data one frame at a time. This is - * inefficient, but makes things simpler. */ - while (info->can_capture && - (jack_ringbuffer_read_space (rb) >= bytes_per_frame)) { - - jack_ringbuffer_read (rb, framebuf, bytes_per_frame); - - if (sf_writef_float (info->sf, framebuf, 1) != 1) { - char errstr[256]; - sf_error_str (0, errstr, sizeof (errstr) - 1); - fprintf (stderr, - "cannot write sndfile (%s)\n", - errstr); - info->status = EIO; /* write failed */ - goto done; - } - - if (++total_captured >= info->duration) { - printf ("disk thread finished\n"); - goto done; - } - } - - /* wait until process() signals more data */ - pthread_cond_wait (&data_ready, &disk_thread_lock); - } - - done: - pthread_mutex_unlock (&disk_thread_lock); - free (framebuf); - return 0; -} - -int -process (jack_nframes_t nframes, void *arg) -{ - int chn; - size_t i; - jack_thread_info_t *info = (jack_thread_info_t *) arg; - - /* Do nothing until we're ready to begin. */ - if ((!info->can_process) || (!info->can_capture)) - return 0; - - for (chn = 0; chn < nports; chn++) - in[chn] = jack_port_get_buffer (ports[chn], nframes); - - /* Sndfile requires interleaved data. It is simpler here to - * just queue interleaved samples to a single ringbuffer. */ - for (i = 0; i < nframes; i++) { - for (chn = 0; chn < nports; chn++) { - if (jack_ringbuffer_write (rb, (void *) (in[chn]+i), - sample_size) - < sample_size) - overruns++; - } - } - - /* Tell the disk thread there is work to do. If it is already - * running, the lock will not be available. We can't wait - * here in the process() thread, but we don't need to signal - * in that case, because the disk thread will read all the - * data queued before waiting again. */ - if (pthread_mutex_trylock (&disk_thread_lock) == 0) { - pthread_cond_signal (&data_ready); - pthread_mutex_unlock (&disk_thread_lock); - } - - return 0; -} - -void -jack_shutdown (void *arg) -{ - fprintf (stderr, "JACK shutdown\n"); - // exit (0); - abort(); -} - -void -setup_disk_thread (jack_thread_info_t *info) -{ - SF_INFO sf_info; - int short_mask; - - sf_info.samplerate = jack_get_sample_rate (info->client); - sf_info.channels = info->channels; - - switch (info->bitdepth) { - case 8: short_mask = SF_FORMAT_PCM_U8; - break; - case 16: short_mask = SF_FORMAT_PCM_16; - break; - case 24: short_mask = SF_FORMAT_PCM_24; - break; - case 32: short_mask = SF_FORMAT_PCM_32; - break; - default: short_mask = SF_FORMAT_PCM_16; - break; - } - sf_info.format = SF_FORMAT_WAV|short_mask; - - if ((info->sf = sf_open (info->path, SFM_WRITE, &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); - } - - if (info->duration == 0) { - info->duration = JACK_MAX_FRAMES; - } else { - info->duration *= sf_info.samplerate; - } - - info->can_capture = 0; - - pthread_create (&info->thread_id, NULL, disk_thread, info); -} - -void -run_disk_thread (jack_thread_info_t *info) -{ - info->can_capture = 1; - pthread_join (info->thread_id, NULL); - sf_close (info->sf); - if (overruns > 0) { - fprintf (stderr, - "jackrec failed with %ld overruns.\n", overruns); - fprintf (stderr, " try a bigger buffer than -B %" - PRIu32 ".\n", info->rb_size); - info->status = EPIPE; - } -} - -void -setup_ports (int sources, char *source_names[], jack_thread_info_t *info) -{ - unsigned int i; - size_t in_size; - - /* Allocate data structures that depend on the number of ports. */ - nports = sources; - ports = (jack_port_t **) malloc (sizeof (jack_port_t *) * nports); - in_size = nports * sizeof (jack_default_audio_sample_t *); - in = (jack_default_audio_sample_t **) malloc (in_size); - rb = jack_ringbuffer_create (nports * sample_size * info->rb_size); - - /* When JACK is running realtime, jack_activate() will have - * called mlockall() to lock our pages into memory. But, we - * still need to touch any newly allocated pages before - * process() starts using them. Otherwise, a page fault could - * create a delay that would force JACK to shut us down. */ - memset(in, 0, in_size); - memset(rb->buf, 0, rb->size); - - for (i = 0; i < nports; i++) { - char name[64]; - - sprintf (name, "input%d", 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_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->can_process = 1; /* process() can start, now */ -} - -int -main (int argc, char *argv[]) - -{ - jack_client_t *client; - jack_thread_info_t thread_info; - int c; - int longopt_index = 0; - extern int optind, opterr; - int show_usage = 0; - char *optstring = "d:f:b:B:h"; - struct option long_options[] = { - { "help", 0, 0, 'h' }, - { "duration", 1, 0, 'd' }, - { "file", 1, 0, 'f' }, - { "bitdepth", 1, 0, 'b' }, - { "bufsize", 1, 0, 'B' }, - { 0, 0, 0, 0 } - }; - - memset (&thread_info, 0, sizeof (thread_info)); - thread_info.rb_size = DEFAULT_RB_SIZE; - opterr = 0; - - while ((c = getopt_long (argc, argv, optstring, long_options, &longopt_index)) != -1) { - switch (c) { - case 1: - /* getopt signals end of '-' options */ - break; - - case 'h': - show_usage++; - break; - case 'd': - thread_info.duration = atoi (optarg); - break; - case 'f': - thread_info.path = optarg; - break; - case 'b': - thread_info.bitdepth = atoi (optarg); - break; - case 'B': - thread_info.rb_size = atoi (optarg); - break; - default: - fprintf (stderr, "error\n"); - show_usage++; - break; - } - } - - if (show_usage || thread_info.path == NULL || optind == argc) { - fprintf (stderr, "usage: jackrec -f filename [ -d second ] [ -b bitdepth ] [ -B bufsize ] port1 [ port2 ... ]\n"); - exit (1); - } - - if ((client = jack_client_new ("jackrec")) == 0) { - fprintf (stderr, "jack server not running?\n"); - exit (1); - } - - thread_info.client = client; - thread_info.channels = argc - optind; - thread_info.can_process = 0; - - setup_disk_thread (&thread_info); - - jack_set_process_callback (client, process, &thread_info); - jack_on_shutdown (client, jack_shutdown, &thread_info); - - if (jack_activate (client)) { - fprintf (stderr, "cannot activate client"); - } - - setup_ports (argc - optind, &argv[optind], &thread_info); - - run_disk_thread (&thread_info); - - jack_client_close (client); - - jack_ringbuffer_free (rb); - - exit (0); -}