| @@ -0,0 +1,158 @@ | |||
| MAINTAINERCLEANFILES = Makefile.in | |||
| if HAVE_READLINE | |||
| JACK_TRANSPORT = jack_transport | |||
| dist-check-readline: | |||
| else | |||
| JACK_TRANSPORT = | |||
| dist-check-readline: | |||
| @echo | |||
| @echo ' ******' You need readline installed to make dist.' ******' | |||
| @echo | |||
| @false | |||
| endif | |||
| NETJACK_TOOLS = jack_netsource | |||
| if HAVE_SAMPLERATE | |||
| if HAVE_ALSA | |||
| NETJACK_TOOLS += alsa_in alsa_out | |||
| endif | |||
| dist-check-samplerate: | |||
| else | |||
| dist-check-samplerate: | |||
| @echo | |||
| @echo ' ******' You need libsamplerate installed to make dist.' ******' | |||
| @echo | |||
| @false | |||
| endif | |||
| bin_PROGRAMS = jack_load \ | |||
| jack_unload \ | |||
| jack_monitor_client \ | |||
| jack_connect \ | |||
| jack_disconnect \ | |||
| jack_lsp \ | |||
| jack_freewheel \ | |||
| jack_evmon \ | |||
| jack_alias \ | |||
| jack_bufsize \ | |||
| jack_samplerate \ | |||
| jack_session_notify \ | |||
| jack_wait \ | |||
| jack_midi_dump \ | |||
| jack_iodelay \ | |||
| jack_load_test \ | |||
| $(JACK_TRANSPORT) \ | |||
| $(NETJACK_TOOLS) | |||
| noinst_PROGRAMS = jack_thread_wait | |||
| 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) | |||
| jack_connect_SOURCES = connect.c | |||
| jack_connect_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_connect_LDADD = $(top_builddir)/libjack/libjack.la | |||
| jack_disconnect_SOURCES = connect.c | |||
| jack_disconnect_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_disconnect_LDADD = $(top_builddir)/libjack/libjack.la | |||
| jack_monitor_client_SOURCES = monitor_client.c | |||
| jack_monitor_client_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_monitor_client_LDADD = $(top_builddir)/libjack/libjack.la | |||
| jack_thread_wait_SOURCES = tw.c | |||
| jack_thread_wait_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_thread_wait_LDADD = $(top_builddir)/libjack/libjack.la | |||
| jack_wait_SOURCES = wait.c | |||
| jack_wait_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_wait_LDADD = $(top_builddir)/libjack/libjack.la | |||
| jack_evmon_SOURCES = evmon.c | |||
| jack_evmon_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_evmon_LDADD = $(top_builddir)/libjack/libjack.la | |||
| jack_alias_SOURCES = alias.c | |||
| jack_alias_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_alias_LDADD = $(top_builddir)/libjack/libjack.la | |||
| jack_lsp_SOURCES = lsp.c | |||
| jack_lsp_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_lsp_LDADD = $(top_builddir)/libjack/libjack.la | |||
| jack_freewheel_SOURCES = freewheel.c | |||
| jack_freewheel_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_freewheel_LDADD = $(top_builddir)/libjack/libjack.la | |||
| jack_bufsize_SOURCES = bufsize.c | |||
| jack_bufsize_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_bufsize_LDADD = $(top_builddir)/libjack/libjack.la | |||
| jack_samplerate_SOURCES = samplerate.c | |||
| jack_samplerate_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_samplerate_LDADD = $(top_builddir)/libjack/libjack.la | |||
| jack_session_notify_SOURCES = session_notify.c | |||
| jack_session_notify_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_session_notify_LDADD = $(top_builddir)/libjack/libjack.la | |||
| jack_midi_dump_SOURCES = midi_dump.c | |||
| jack_midi_dump_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_midi_dump_LDADD = $(top_builddir)/libjack/libjack.la | |||
| jack_iodelay_SOURCES = iodelay.c | |||
| jack_iodelay_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_iodelay_LDADD = $(top_builddir)/libjack/libjack.la | |||
| if HAVE_READLINE | |||
| jack_transport_SOURCES = transport.c | |||
| jack_transport_LDFLAGS = -lreadline @READLINE_DEPS@ @OS_LDFLAGS@ | |||
| jack_transport_LDADD = $(top_builddir)/libjack/libjack.la | |||
| endif | |||
| jack_load_test_SOURCES = load_test.c | |||
| jack_load_test_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_load_test_LDADD = $(top_builddir)/libjack/libjack.la | |||
| # | |||
| # General purpose in-process loader/unloader | |||
| # | |||
| jack_load_SOURCES = ipload.c | |||
| jack_load_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_load_LDADD = $(top_builddir)/libjack/libjack.la | |||
| jack_unload_SOURCES = ipunload.c | |||
| jack_unload_LDFLAGS = @OS_LDFLAGS@ | |||
| jack_unload_LDADD = $(top_builddir)/libjack/libjack.la | |||
| # | |||
| # Netjack slave tools | |||
| # | |||
| jack_netsource_SOURCES = netsource.c $(top_builddir)/drivers/netjack/netjack_packet.c | |||
| jack_netsource_CFLAGS = @NETJACK_CFLAGS@ -I$(top_srcdir)/drivers/netjack | |||
| jack_netsource_LDFLAGS = @NETJACK_LIBS@ @OS_LDFLAGS@ | |||
| jack_netsource_LDADD = $(top_builddir)/libjack/libjack.la | |||
| if HAVE_SAMPLERATE | |||
| if HAVE_ALSA | |||
| alsa_in_SOURCES = alsa_in.c $(top_builddir)/drivers/alsa/memops.c | |||
| alsa_in_CFLAGS = @NETJACK_CFLAGS@ -I$(top_builddir)/drivers/alsa | |||
| alsa_in_LDFLAGS = -lasound -lsamplerate @OS_LDFLAGS@ | |||
| alsa_in_LDADD = $(top_builddir)/libjack/libjack.la | |||
| alsa_out_SOURCES = alsa_out.c $(top_builddir)/drivers/alsa/memops.c | |||
| alsa_out_CFLAGS = @NETJACK_CFLAGS@ -I$(top_builddir)/drivers/alsa | |||
| alsa_out_LDFLAGS = -lasound -lsamplerate @OS_LDFLAGS@ | |||
| alsa_out_LDADD = $(top_builddir)/libjack/libjack.la | |||
| endif #HAVE_ALSA | |||
| endif #HAVE_SAMPLERATE | |||
| # XXX ? dist-hook: dist-check-sndfile dist-check-samplerate | |||
| @@ -0,0 +1,121 @@ | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <unistd.h> | |||
| #include <string.h> | |||
| #include <getopt.h> | |||
| #include <config.h> | |||
| #include <jack/jack.h> | |||
| char * my_name; | |||
| void | |||
| show_version (void) | |||
| { | |||
| fprintf (stderr, "%s: JACK Audio Connection Kit version " VERSION "\n", | |||
| my_name); | |||
| } | |||
| void | |||
| show_usage (void) | |||
| { | |||
| show_version (); | |||
| fprintf (stderr, "\nUsage: %s [options] portname alias\n", my_name); | |||
| fprintf (stderr, "List active Jack ports, and optionally display extra information.\n\n"); | |||
| fprintf (stderr, "Display options:\n"); | |||
| fprintf (stderr, " -u, --unalias remove `alias' as an alias for `port'\n"); | |||
| fprintf (stderr, " -h, --help Display this help message\n"); | |||
| fprintf (stderr, " --version Output version information and exit\n\n"); | |||
| fprintf (stderr, "For more information see http://jackaudio.org/\n"); | |||
| } | |||
| int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| jack_client_t *client; | |||
| jack_status_t status; | |||
| char* portname; | |||
| char* alias; | |||
| int unset = 0; | |||
| int ret; | |||
| int c; | |||
| int option_index; | |||
| extern int optind; | |||
| jack_port_t* port; | |||
| struct option long_options[] = { | |||
| { "unalias", 0, 0, 'u' }, | |||
| { "help", 0, 0, 'h' }, | |||
| { "version", 0, 0, 'v' }, | |||
| { 0, 0, 0, 0 } | |||
| }; | |||
| if (argc < 3) { | |||
| show_usage (); | |||
| return 1; | |||
| } | |||
| my_name = strrchr(argv[0], '/'); | |||
| if (my_name == 0) { | |||
| my_name = argv[0]; | |||
| } else { | |||
| my_name ++; | |||
| } | |||
| while ((c = getopt_long (argc, argv, "uhv", long_options, &option_index)) >= 0) { | |||
| switch (c) { | |||
| case 'u': | |||
| unset = 1; | |||
| break; | |||
| case 'h': | |||
| show_usage (); | |||
| return 1; | |||
| break; | |||
| case 'v': | |||
| show_version (); | |||
| return 1; | |||
| break; | |||
| default: | |||
| show_usage (); | |||
| return 1; | |||
| break; | |||
| } | |||
| } | |||
| portname = argv[optind++]; | |||
| alias = argv[optind]; | |||
| /* Open a client connection to the JACK server. Starting a | |||
| * new server only to list its ports seems pointless, so we | |||
| * specify JackNoStartServer. */ | |||
| //JOQ: need a new server name option | |||
| client = jack_client_open ("lsp", JackNoStartServer, &status); | |||
| if (client == NULL) { | |||
| if (status & JackServerFailed) { | |||
| fprintf (stderr, "JACK server not running\n"); | |||
| } else { | |||
| fprintf (stderr, "jack_client_open() failed, " | |||
| "status = 0x%2.0x\n", status); | |||
| } | |||
| return 1; | |||
| } | |||
| if ((port = jack_port_by_name (client, portname)) == 0) { | |||
| fprintf (stderr, "No port named \"%s\"\n", portname); | |||
| return 1; | |||
| } | |||
| if (!unset) { | |||
| ret = jack_port_set_alias (port, alias); | |||
| } else { | |||
| ret = jack_port_unset_alias (port, alias); | |||
| } | |||
| jack_client_close (client); | |||
| return ret; | |||
| } | |||
| @@ -0,0 +1,801 @@ | |||
| /** @file simple_client.c | |||
| * | |||
| * @brief This simple client demonstrates the basic features of JACK | |||
| * as they would be used by many applications. | |||
| */ | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <unistd.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <signal.h> | |||
| #include <math.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/jslist.h> | |||
| #include <jack/memops.h> | |||
| #include "alsa/asoundlib.h" | |||
| #include <samplerate.h> | |||
| // Here are the lists of the jack ports... | |||
| JSList *capture_ports = NULL; | |||
| JSList *capture_srcs = NULL; | |||
| JSList *playback_ports = NULL; | |||
| JSList *playback_srcs = NULL; | |||
| jack_client_t *client; | |||
| snd_pcm_t *alsa_handle; | |||
| int jack_sample_rate; | |||
| int jack_buffer_size; | |||
| int quit = 0; | |||
| double resample_mean = 1.0; | |||
| double static_resample_factor = 1.0; | |||
| double resample_lower_limit = 0.25; | |||
| double resample_upper_limit = 4.0; | |||
| double *offset_array; | |||
| double *window_array; | |||
| int offset_differential_index = 0; | |||
| double offset_integral = 0; | |||
| // ------------------------------------------------------ commandline parameters | |||
| int sample_rate = 0; /* stream rate */ | |||
| int num_channels = 2; /* count of channels */ | |||
| int period_size = 1024; | |||
| int num_periods = 2; | |||
| int target_delay = 0; /* the delay which the program should try to approach. */ | |||
| int max_diff = 0; /* the diff value, when a hard readpointer skip should occur */ | |||
| int catch_factor = 100000; | |||
| int catch_factor2 = 10000; | |||
| double pclamp = 15.0; | |||
| double controlquant = 10000.0; | |||
| int smooth_size = 256; | |||
| int good_window=0; | |||
| int verbose = 0; | |||
| int instrument = 0; | |||
| int samplerate_quality = 2; | |||
| // Debug stuff: | |||
| volatile float output_resampling_factor = 1.0; | |||
| volatile int output_new_delay = 0; | |||
| volatile float output_offset = 0.0; | |||
| volatile float output_integral = 0.0; | |||
| volatile float output_diff = 0.0; | |||
| snd_pcm_uframes_t real_buffer_size; | |||
| snd_pcm_uframes_t real_period_size; | |||
| // buffers | |||
| char *tmpbuf; | |||
| char *outbuf; | |||
| float *resampbuf; | |||
| // format selection, and corresponding functions from memops in a nice set of structs. | |||
| typedef struct alsa_format { | |||
| snd_pcm_format_t format_id; | |||
| size_t sample_size; | |||
| void (*jack_to_soundcard) (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); | |||
| void (*soundcard_to_jack) (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); | |||
| const char *name; | |||
| } alsa_format_t; | |||
| alsa_format_t formats[] = { | |||
| { SND_PCM_FORMAT_FLOAT_LE, 4, sample_move_dS_floatLE, sample_move_floatLE_sSs, "float" }, | |||
| { SND_PCM_FORMAT_S32, 4, sample_move_d32u24_sS, sample_move_dS_s32u24, "32bit" }, | |||
| { SND_PCM_FORMAT_S24_3LE, 3, sample_move_d24_sS, sample_move_dS_s24, "24bit - real" }, | |||
| { SND_PCM_FORMAT_S24, 4, sample_move_d24_sS, sample_move_dS_s24, "24bit" }, | |||
| { SND_PCM_FORMAT_S16, 2, sample_move_d16_sS, sample_move_dS_s16, "16bit" } | |||
| }; | |||
| #define NUMFORMATS (sizeof(formats)/sizeof(formats[0])) | |||
| int format=0; | |||
| // Alsa stuff... i dont want to touch this bullshit in the next years.... please... | |||
| static int xrun_recovery(snd_pcm_t *handle, int err) { | |||
| // printf( "xrun !!!.... %d\n", err ); | |||
| if (err == -EPIPE) { /* under-run */ | |||
| err = snd_pcm_prepare(handle); | |||
| if (err < 0) | |||
| printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); | |||
| return 0; | |||
| } else if (err == -EAGAIN) { | |||
| while ((err = snd_pcm_resume(handle)) == -EAGAIN) | |||
| usleep(100); /* wait until the suspend flag is released */ | |||
| if (err < 0) { | |||
| err = snd_pcm_prepare(handle); | |||
| if (err < 0) | |||
| printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); | |||
| } | |||
| return 0; | |||
| } | |||
| return err; | |||
| } | |||
| static int set_hwformat( snd_pcm_t *handle, snd_pcm_hw_params_t *params ) | |||
| { | |||
| int i; | |||
| int err; | |||
| for( i=0; i<NUMFORMATS; i++ ) { | |||
| /* set the sample format */ | |||
| err = snd_pcm_hw_params_set_format(handle, params, formats[i].format_id); | |||
| if (err == 0) { | |||
| format = i; | |||
| return 0; | |||
| } | |||
| } | |||
| return err; | |||
| } | |||
| static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access, int rate, int channels, int period, int nperiods ) { | |||
| int err, dir=0; | |||
| unsigned int buffer_time; | |||
| unsigned int period_time; | |||
| unsigned int rrate; | |||
| unsigned int rchannels; | |||
| /* choose all parameters */ | |||
| err = snd_pcm_hw_params_any(handle, params); | |||
| if (err < 0) { | |||
| printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* set the interleaved read/write format */ | |||
| err = snd_pcm_hw_params_set_access(handle, params, access); | |||
| if (err < 0) { | |||
| printf("Access type not available for playback: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* set the sample format */ | |||
| err = set_hwformat(handle, params); | |||
| if (err < 0) { | |||
| printf("Sample format not available for playback: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* set the count of channels */ | |||
| rchannels = channels; | |||
| err = snd_pcm_hw_params_set_channels_near(handle, params, &rchannels); | |||
| if (err < 0) { | |||
| printf("Channels count (%i) not available for record: %s\n", channels, snd_strerror(err)); | |||
| return err; | |||
| } | |||
| if (rchannels != channels) { | |||
| printf("WARNING: chennel count does not match (requested %d got %d)\n", channels, rchannels); | |||
| num_channels = rchannels; | |||
| } | |||
| /* set the stream rate */ | |||
| rrate = rate; | |||
| err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0); | |||
| if (err < 0) { | |||
| printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err)); | |||
| return err; | |||
| } | |||
| if (rrate != rate) { | |||
| printf("WARNING: Rate doesn't match (requested %iHz, get %iHz)\n", rate, rrate); | |||
| sample_rate = rrate; | |||
| } | |||
| /* set the buffer time */ | |||
| buffer_time = 1000000*(uint64_t)period*nperiods/rate; | |||
| err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir); | |||
| if (err < 0) { | |||
| printf("Unable to set buffer time %i for playback: %s\n", 1000000*period*nperiods/rate, snd_strerror(err)); | |||
| return err; | |||
| } | |||
| err = snd_pcm_hw_params_get_buffer_size( params, &real_buffer_size ); | |||
| if (err < 0) { | |||
| printf("Unable to get buffer size back: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| if( real_buffer_size != nperiods * period ) { | |||
| printf( "WARNING: buffer size does not match: (requested %d, got %d)\n", nperiods * period, (int) real_buffer_size ); | |||
| } | |||
| /* set the period time */ | |||
| period_time = 1000000*(uint64_t)period/rate; | |||
| err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir); | |||
| if (err < 0) { | |||
| printf("Unable to set period time %i for playback: %s\n", 1000000*period/rate, snd_strerror(err)); | |||
| return err; | |||
| } | |||
| err = snd_pcm_hw_params_get_period_size(params, &real_period_size, NULL ); | |||
| if (err < 0) { | |||
| printf("Unable to get period size back: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| if( real_period_size != period ) { | |||
| printf( "WARNING: period size does not match: (requested %i, got %i)\n", period, (int)real_period_size ); | |||
| } | |||
| /* write the parameters to device */ | |||
| err = snd_pcm_hw_params(handle, params); | |||
| if (err < 0) { | |||
| printf("Unable to set hw params for playback: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| return 0; | |||
| } | |||
| static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int period) { | |||
| int err; | |||
| /* get the current swparams */ | |||
| err = snd_pcm_sw_params_current(handle, swparams); | |||
| if (err < 0) { | |||
| printf("Unable to determine current swparams for capture: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* start the transfer when the buffer is full */ | |||
| err = snd_pcm_sw_params_set_start_threshold(handle, swparams, period ); | |||
| if (err < 0) { | |||
| printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, -1 ); | |||
| if (err < 0) { | |||
| printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* allow the transfer when at least period_size samples can be processed */ | |||
| err = snd_pcm_sw_params_set_avail_min(handle, swparams, 2*period ); | |||
| if (err < 0) { | |||
| printf("Unable to set avail min for capture: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* align all transfers to 1 sample */ | |||
| err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1); | |||
| if (err < 0) { | |||
| printf("Unable to set transfer align for capture: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* write the parameters to the playback device */ | |||
| err = snd_pcm_sw_params(handle, swparams); | |||
| if (err < 0) { | |||
| printf("Unable to set sw params for capture: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| return 0; | |||
| } | |||
| // ok... i only need this function to communicate with the alsa bloat api... | |||
| static snd_pcm_t *open_audiofd( char *device_name, int capture, int rate, int channels, int period, int nperiods ) { | |||
| int err; | |||
| snd_pcm_t *handle; | |||
| snd_pcm_hw_params_t *hwparams; | |||
| snd_pcm_sw_params_t *swparams; | |||
| snd_pcm_hw_params_alloca(&hwparams); | |||
| snd_pcm_sw_params_alloca(&swparams); | |||
| if ((err = snd_pcm_open(&(handle), device_name, capture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK )) < 0) { | |||
| printf("Capture open error: %s\n", snd_strerror(err)); | |||
| return NULL; | |||
| } | |||
| if ((err = set_hwparams(handle, hwparams,SND_PCM_ACCESS_RW_INTERLEAVED, rate, channels, period, nperiods )) < 0) { | |||
| printf("Setting of hwparams failed: %s\n", snd_strerror(err)); | |||
| return NULL; | |||
| } | |||
| if ((err = set_swparams(handle, swparams, period)) < 0) { | |||
| printf("Setting of swparams failed: %s\n", snd_strerror(err)); | |||
| return NULL; | |||
| } | |||
| snd_pcm_start( handle ); | |||
| snd_pcm_wait( handle, 200 ); | |||
| return handle; | |||
| } | |||
| double hann( double x ) | |||
| { | |||
| return 0.5 * (1.0 - cos( 2*M_PI * x ) ); | |||
| } | |||
| /** | |||
| * The process callback for this JACK application. | |||
| * It is called by JACK at the appropriate times. | |||
| */ | |||
| int process (jack_nframes_t nframes, void *arg) { | |||
| int rlen; | |||
| int err; | |||
| snd_pcm_sframes_t delay = target_delay; | |||
| int put_back_samples=0; | |||
| int i; | |||
| delay = snd_pcm_avail( alsa_handle ); | |||
| delay -= jack_frames_since_cycle_start( client ); | |||
| // Do it the hard way. | |||
| // this is for compensating xruns etc... | |||
| if( delay > (target_delay+max_diff) ) { | |||
| output_new_delay = (int) delay; | |||
| while ((delay-target_delay) > 0) { | |||
| snd_pcm_uframes_t to_read = ((delay-target_delay) > 512) ? 512 : (delay-target_delay); | |||
| snd_pcm_readi( alsa_handle, tmpbuf, to_read ); | |||
| delay -= to_read; | |||
| } | |||
| delay = target_delay; | |||
| // Set the resample_rate... we need to adjust the offset integral, to do this. | |||
| // first look at the PI controller, this code is just a special case, which should never execute once | |||
| // everything is swung in. | |||
| offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; | |||
| // Also clear the array. we are beginning a new control cycle. | |||
| for( i=0; i<smooth_size; i++ ) | |||
| offset_array[i] = 0.0; | |||
| } | |||
| if( delay < (target_delay-max_diff) ) { | |||
| snd_pcm_rewind( alsa_handle, target_delay - delay ); | |||
| output_new_delay = (int) delay; | |||
| delay = target_delay; | |||
| // Set the resample_rate... we need to adjust the offset integral, to do this. | |||
| offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; | |||
| // Also clear the array. we are beginning a new control cycle. | |||
| for( i=0; i<smooth_size; i++ ) | |||
| offset_array[i] = 0.0; | |||
| } | |||
| /* ok... now we should have target_delay +- max_diff on the alsa side. | |||
| * | |||
| * calculate the number of frames, we want to get. | |||
| */ | |||
| double offset = delay - target_delay; | |||
| // Save offset. | |||
| offset_array[(offset_differential_index++)% smooth_size ] = offset; | |||
| // Build the mean of the windowed offset array | |||
| // basically fir lowpassing. | |||
| double smooth_offset = 0.0; | |||
| for( i=0; i<smooth_size; i++ ) | |||
| smooth_offset += | |||
| offset_array[ (i + offset_differential_index-1) % smooth_size] * window_array[i]; | |||
| smooth_offset /= (double) smooth_size; | |||
| // this is the integral of the smoothed_offset | |||
| offset_integral += smooth_offset; | |||
| // Clamp offset. | |||
| // the smooth offset still contains unwanted noise | |||
| // which would go straigth onto the resample coeff. | |||
| // it only used in the P component and the I component is used for the fine tuning anyways. | |||
| if( fabs( smooth_offset ) < pclamp ) | |||
| smooth_offset = 0.0; | |||
| // ok. now this is the PI controller. | |||
| // u(t) = K * ( e(t) + 1/T \int e(t') dt' ) | |||
| // K = 1/catch_factor and T = catch_factor2 | |||
| double current_resample_factor = static_resample_factor - smooth_offset / (double) catch_factor - offset_integral / (double) catch_factor / (double)catch_factor2; | |||
| // now quantize this value around resample_mean, so that the noise which is in the integral component doesnt hurt. | |||
| current_resample_factor = floor( (current_resample_factor - resample_mean) * controlquant + 0.5 ) / controlquant + resample_mean; | |||
| // Output "instrumentatio" gonna change that to real instrumentation in a few. | |||
| output_resampling_factor = (float) current_resample_factor; | |||
| output_diff = (float) smooth_offset; | |||
| output_integral = (float) offset_integral; | |||
| output_offset = (float) offset; | |||
| // Clamp a bit. | |||
| if( current_resample_factor < resample_lower_limit ) current_resample_factor = resample_lower_limit; | |||
| if( current_resample_factor > resample_upper_limit ) current_resample_factor = resample_upper_limit; | |||
| // Now Calculate how many samples we need. | |||
| rlen = ceil( ((double)nframes) / current_resample_factor )+2; | |||
| assert( rlen > 2 ); | |||
| // Calculate resample_mean so we can init ourselves to saner values. | |||
| resample_mean = 0.9999 * resample_mean + 0.0001 * current_resample_factor; | |||
| // get the data... | |||
| again: | |||
| err = snd_pcm_readi(alsa_handle, outbuf, rlen); | |||
| if( err < 0 ) { | |||
| printf( "err = %d\n", err ); | |||
| if (xrun_recovery(alsa_handle, err) < 0) { | |||
| //printf("Write error: %s\n", snd_strerror(err)); | |||
| //exit(EXIT_FAILURE); | |||
| } | |||
| goto again; | |||
| } | |||
| if( err != rlen ) { | |||
| //printf( "read = %d\n", rlen ); | |||
| } | |||
| /* | |||
| * render jack ports to the outbuf... | |||
| */ | |||
| int chn = 0; | |||
| JSList *node = capture_ports; | |||
| JSList *src_node = capture_srcs; | |||
| SRC_DATA src; | |||
| while ( node != NULL) | |||
| { | |||
| jack_port_t *port = (jack_port_t *) node->data; | |||
| float *buf = jack_port_get_buffer (port, nframes); | |||
| SRC_STATE *src_state = src_node->data; | |||
| formats[format].soundcard_to_jack( resampbuf, outbuf + format[formats].sample_size * chn, rlen, num_channels*format[formats].sample_size ); | |||
| src.data_in = resampbuf; | |||
| src.input_frames = rlen; | |||
| src.data_out = buf; | |||
| src.output_frames = nframes; | |||
| src.end_of_input = 0; | |||
| src.src_ratio = current_resample_factor; | |||
| src_process( src_state, &src ); | |||
| put_back_samples = rlen-src.input_frames_used; | |||
| src_node = jack_slist_next (src_node); | |||
| node = jack_slist_next (node); | |||
| chn++; | |||
| } | |||
| // Put back the samples libsamplerate did not consume. | |||
| //printf( "putback = %d\n", put_back_samples ); | |||
| snd_pcm_rewind( alsa_handle, put_back_samples ); | |||
| return 0; | |||
| } | |||
| /** | |||
| * the latency callback. | |||
| * sets up the latencies on the ports. | |||
| */ | |||
| void | |||
| latency_cb (jack_latency_callback_mode_t mode, void *arg) | |||
| { | |||
| jack_latency_range_t range; | |||
| JSList *node; | |||
| range.min = range.max = target_delay; | |||
| if (mode == JackCaptureLatency) { | |||
| for (node = capture_ports; node; node = jack_slist_next (node)) { | |||
| jack_port_t *port = node->data; | |||
| jack_port_set_latency_range (port, mode, &range); | |||
| } | |||
| } else { | |||
| for (node = playback_ports; node; node = jack_slist_next (node)) { | |||
| jack_port_t *port = node->data; | |||
| jack_port_set_latency_range (port, mode, &range); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * Allocate the necessary jack ports... | |||
| */ | |||
| void alloc_ports( int n_capture, int n_playback ) { | |||
| int port_flags = JackPortIsOutput; | |||
| int chn; | |||
| jack_port_t *port; | |||
| char buf[32]; | |||
| capture_ports = NULL; | |||
| for (chn = 0; chn < n_capture; chn++) | |||
| { | |||
| snprintf (buf, sizeof(buf) - 1, "capture_%u", chn+1); | |||
| port = jack_port_register (client, buf, | |||
| JACK_DEFAULT_AUDIO_TYPE, | |||
| port_flags, 0); | |||
| if (!port) | |||
| { | |||
| printf( "jacknet_client: cannot register port for %s", buf); | |||
| break; | |||
| } | |||
| capture_srcs = jack_slist_append( capture_srcs, src_new( 4-samplerate_quality, 1, NULL ) ); | |||
| capture_ports = jack_slist_append (capture_ports, port); | |||
| } | |||
| port_flags = JackPortIsInput; | |||
| playback_ports = NULL; | |||
| for (chn = 0; chn < n_playback; chn++) | |||
| { | |||
| snprintf (buf, sizeof(buf) - 1, "playback_%u", chn+1); | |||
| port = jack_port_register (client, buf, | |||
| JACK_DEFAULT_AUDIO_TYPE, | |||
| port_flags, 0); | |||
| if (!port) | |||
| { | |||
| printf( "jacknet_client: cannot register port for %s", buf); | |||
| break; | |||
| } | |||
| playback_srcs = jack_slist_append( playback_srcs, src_new( 4-samplerate_quality, 1, NULL ) ); | |||
| playback_ports = jack_slist_append (playback_ports, port); | |||
| } | |||
| } | |||
| /** | |||
| * This is the shutdown callback for this JACK application. | |||
| * It is called by JACK if the server ever shuts down or | |||
| * decides to disconnect the client. | |||
| */ | |||
| void jack_shutdown (void *arg) { | |||
| exit (1); | |||
| } | |||
| /** | |||
| * be user friendly. | |||
| * be user friendly. | |||
| * be user friendly. | |||
| */ | |||
| void printUsage() { | |||
| fprintf(stderr, "usage: alsa_out [options]\n" | |||
| "\n" | |||
| " -j <jack name> - client name\n" | |||
| " -d <alsa_device> \n" | |||
| " -c <channels> \n" | |||
| " -p <period_size> \n" | |||
| " -n <num_period> \n" | |||
| " -r <sample_rate> \n" | |||
| " -q <sample_rate quality [0..4]\n" | |||
| " -m <max_diff> \n" | |||
| " -t <target_delay> \n" | |||
| " -i turns on instrumentation\n" | |||
| " -v turns on printouts\n" | |||
| "\n"); | |||
| } | |||
| /** | |||
| * the main function.... | |||
| */ | |||
| void | |||
| sigterm_handler( int signal ) | |||
| { | |||
| quit = 1; | |||
| } | |||
| int main (int argc, char *argv[]) { | |||
| char jack_name[30] = "alsa_in"; | |||
| char alsa_device[30] = "hw:0"; | |||
| extern char *optarg; | |||
| extern int optind, optopt; | |||
| int errflg=0; | |||
| int c; | |||
| while ((c = getopt(argc, argv, "ivj:r:c:p:n:d:q:m:t:f:F:C:Q:s:")) != -1) { | |||
| switch(c) { | |||
| case 'j': | |||
| strcpy(jack_name,optarg); | |||
| break; | |||
| case 'r': | |||
| sample_rate = atoi(optarg); | |||
| break; | |||
| case 'c': | |||
| num_channels = atoi(optarg); | |||
| break; | |||
| case 'p': | |||
| period_size = atoi(optarg); | |||
| break; | |||
| case 'n': | |||
| num_periods = atoi(optarg); | |||
| break; | |||
| case 'd': | |||
| strcpy(alsa_device,optarg); | |||
| break; | |||
| case 't': | |||
| target_delay = atoi(optarg); | |||
| break; | |||
| case 'q': | |||
| samplerate_quality = atoi(optarg); | |||
| break; | |||
| case 'm': | |||
| max_diff = atoi(optarg); | |||
| break; | |||
| case 'f': | |||
| catch_factor = atoi(optarg); | |||
| break; | |||
| case 'F': | |||
| catch_factor2 = atoi(optarg); | |||
| break; | |||
| case 'C': | |||
| pclamp = (double) atoi(optarg); | |||
| break; | |||
| case 'Q': | |||
| controlquant = (double) atoi(optarg); | |||
| break; | |||
| case 'v': | |||
| verbose = 1; | |||
| break; | |||
| case 'i': | |||
| instrument = 1; | |||
| break; | |||
| case 's': | |||
| smooth_size = atoi(optarg); | |||
| break; | |||
| case ':': | |||
| fprintf(stderr, | |||
| "Option -%c requires an operand\n", optopt); | |||
| errflg++; | |||
| break; | |||
| case '?': | |||
| fprintf(stderr, | |||
| "Unrecognized option: -%c\n", optopt); | |||
| errflg++; | |||
| } | |||
| } | |||
| if (errflg) { | |||
| printUsage(); | |||
| exit(2); | |||
| } | |||
| if( (samplerate_quality < 0) || (samplerate_quality > 4) ) { | |||
| fprintf (stderr, "invalid samplerate quality\n"); | |||
| return 1; | |||
| } | |||
| if ((client = jack_client_open (jack_name, 0, NULL)) == 0) { | |||
| fprintf (stderr, "jack server not running?\n"); | |||
| return 1; | |||
| } | |||
| /* tell the JACK server to call `process()' whenever | |||
| there is work to be done. | |||
| */ | |||
| jack_set_process_callback (client, process, 0); | |||
| /* tell the JACK server to call `jack_shutdown()' if | |||
| it ever shuts down, either entirely, or if it | |||
| just decides to stop calling us. | |||
| */ | |||
| jack_on_shutdown (client, jack_shutdown, 0); | |||
| if (jack_set_latency_callback) | |||
| jack_set_latency_callback (client, latency_cb, 0); | |||
| // get jack sample_rate | |||
| jack_sample_rate = jack_get_sample_rate( client ); | |||
| if( !sample_rate ) | |||
| sample_rate = jack_sample_rate; | |||
| // now open the alsa fd... | |||
| alsa_handle = open_audiofd( alsa_device, 1, sample_rate, num_channels, period_size, num_periods); | |||
| if( alsa_handle == 0 ) | |||
| exit(20); | |||
| printf( "selected sample format: %s\n", formats[format].name ); | |||
| static_resample_factor = (double) jack_sample_rate / (double) sample_rate; | |||
| resample_lower_limit = static_resample_factor * 0.25; | |||
| resample_upper_limit = static_resample_factor * 4.0; | |||
| resample_mean = static_resample_factor; | |||
| offset_array = malloc( sizeof(double) * smooth_size ); | |||
| if( offset_array == NULL ) { | |||
| fprintf( stderr, "no memory for offset_array !!!\n" ); | |||
| exit(20); | |||
| } | |||
| window_array = malloc( sizeof(double) * smooth_size ); | |||
| if( window_array == NULL ) { | |||
| fprintf( stderr, "no memory for window_array !!!\n" ); | |||
| exit(20); | |||
| } | |||
| int i; | |||
| for( i=0; i<smooth_size; i++ ) { | |||
| offset_array[i] = 0.0; | |||
| window_array[i] = hann( (double) i / ((double) smooth_size - 1.0) ); | |||
| } | |||
| jack_buffer_size = jack_get_buffer_size( client ); | |||
| // Setup target delay and max_diff for the normal user, who does not play with them... | |||
| if( !target_delay ) | |||
| target_delay = (num_periods*period_size / 2) + jack_buffer_size/2; | |||
| if( !max_diff ) | |||
| max_diff = num_periods*period_size - target_delay ; | |||
| if( max_diff > target_delay ) { | |||
| fprintf( stderr, "target_delay (%d) cant be smaller than max_diff(%d)\n", target_delay, max_diff ); | |||
| exit(20); | |||
| } | |||
| if( (target_delay+max_diff) > (num_periods*period_size) ) { | |||
| fprintf( stderr, "target_delay+max_diff (%d) cant be bigger than buffersize(%d)\n", target_delay+max_diff, num_periods*period_size ); | |||
| exit(20); | |||
| } | |||
| // alloc input ports, which are blasted out to alsa... | |||
| alloc_ports( num_channels, 0 ); | |||
| outbuf = malloc( num_periods * period_size * formats[format].sample_size * num_channels ); | |||
| resampbuf = malloc( num_periods * period_size * sizeof( float ) ); | |||
| tmpbuf = malloc( 512 * formats[format].sample_size * num_channels ); | |||
| if ((outbuf == NULL) || (resampbuf == NULL) || (tmpbuf == NULL)) | |||
| { | |||
| fprintf( stderr, "no memory for buffers.\n" ); | |||
| exit(20); | |||
| } | |||
| memset( tmpbuf, 0, 512 * formats[format].sample_size * num_channels); | |||
| /* tell the JACK server that we are ready to roll */ | |||
| if (jack_activate (client)) { | |||
| fprintf (stderr, "cannot activate client"); | |||
| return 1; | |||
| } | |||
| signal( SIGTERM, sigterm_handler ); | |||
| signal( SIGINT, sigterm_handler ); | |||
| if( verbose ) { | |||
| while(!quit) { | |||
| usleep(500000); | |||
| if( output_new_delay ) { | |||
| printf( "delay = %d\n", output_new_delay ); | |||
| output_new_delay = 0; | |||
| } | |||
| printf( "res: %f, \tdiff = %f, \toffset = %f \n", output_resampling_factor, output_diff, output_offset ); | |||
| } | |||
| } else if( instrument ) { | |||
| printf( "# n\tresamp\tdiff\toffseti\tintegral\n"); | |||
| int n=0; | |||
| while(!quit) { | |||
| usleep(1000); | |||
| printf( "%d\t%f\t%f\t%f\t%f\n", n++, output_resampling_factor, output_diff, output_offset, output_integral ); | |||
| } | |||
| } else { | |||
| while(!quit) | |||
| { | |||
| usleep(500000); | |||
| if( output_new_delay ) { | |||
| printf( "delay = %d\n", output_new_delay ); | |||
| output_new_delay = 0; | |||
| } | |||
| } | |||
| } | |||
| jack_deactivate( client ); | |||
| jack_client_close (client); | |||
| exit (0); | |||
| } | |||
| @@ -0,0 +1,803 @@ | |||
| /** @file simple_client.c | |||
| * | |||
| * @brief This simple client demonstrates the basic features of JACK | |||
| * as they would be used by many applications. | |||
| */ | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <unistd.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <signal.h> | |||
| #include <math.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/jslist.h> | |||
| #include <jack/memops.h> | |||
| #include "alsa/asoundlib.h" | |||
| #include <samplerate.h> | |||
| // Here are the lists of the jack ports... | |||
| JSList *capture_ports = NULL; | |||
| JSList *capture_srcs = NULL; | |||
| JSList *playback_ports = NULL; | |||
| JSList *playback_srcs = NULL; | |||
| jack_client_t *client; | |||
| snd_pcm_t *alsa_handle; | |||
| int jack_sample_rate; | |||
| int jack_buffer_size; | |||
| int quit = 0; | |||
| double resample_mean = 1.0; | |||
| double static_resample_factor = 1.0; | |||
| double resample_lower_limit = 0.25; | |||
| double resample_upper_limit = 4.0; | |||
| double *offset_array; | |||
| double *window_array; | |||
| int offset_differential_index = 0; | |||
| double offset_integral = 0; | |||
| // ------------------------------------------------------ commandline parameters | |||
| int sample_rate = 0; /* stream rate */ | |||
| int num_channels = 2; /* count of channels */ | |||
| int period_size = 1024; | |||
| int num_periods = 2; | |||
| int target_delay = 0; /* the delay which the program should try to approach. */ | |||
| int max_diff = 0; /* the diff value, when a hard readpointer skip should occur */ | |||
| int catch_factor = 100000; | |||
| int catch_factor2 = 10000; | |||
| double pclamp = 15.0; | |||
| double controlquant = 10000.0; | |||
| int smooth_size = 256; | |||
| int good_window=0; | |||
| int verbose = 0; | |||
| int instrument = 0; | |||
| int samplerate_quality = 2; | |||
| // Debug stuff: | |||
| volatile float output_resampling_factor = 1.0; | |||
| volatile int output_new_delay = 0; | |||
| volatile float output_offset = 0.0; | |||
| volatile float output_integral = 0.0; | |||
| volatile float output_diff = 0.0; | |||
| snd_pcm_uframes_t real_buffer_size; | |||
| snd_pcm_uframes_t real_period_size; | |||
| // buffers | |||
| char *tmpbuf; | |||
| char *outbuf; | |||
| float *resampbuf; | |||
| // format selection, and corresponding functions from memops in a nice set of structs. | |||
| typedef struct alsa_format { | |||
| snd_pcm_format_t format_id; | |||
| size_t sample_size; | |||
| void (*jack_to_soundcard) (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); | |||
| void (*soundcard_to_jack) (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); | |||
| const char *name; | |||
| } alsa_format_t; | |||
| alsa_format_t formats[] = { | |||
| { SND_PCM_FORMAT_FLOAT_LE, 4, sample_move_dS_floatLE, sample_move_floatLE_sSs, "float" }, | |||
| { SND_PCM_FORMAT_S32, 4, sample_move_d32u24_sS, sample_move_dS_s32u24, "32bit" }, | |||
| { SND_PCM_FORMAT_S24_3LE, 3, sample_move_d24_sS, sample_move_dS_s24, "24bit - real" }, | |||
| { SND_PCM_FORMAT_S24, 4, sample_move_d24_sS, sample_move_dS_s24, "24bit" }, | |||
| { SND_PCM_FORMAT_S16, 2, sample_move_d16_sS, sample_move_dS_s16, "16bit" } | |||
| }; | |||
| #define NUMFORMATS (sizeof(formats)/sizeof(formats[0])) | |||
| int format=0; | |||
| // Alsa stuff... i dont want to touch this bullshit in the next years.... please... | |||
| static int xrun_recovery(snd_pcm_t *handle, int err) { | |||
| // printf( "xrun !!!.... %d\n", err ); | |||
| if (err == -EPIPE) { /* under-run */ | |||
| err = snd_pcm_prepare(handle); | |||
| if (err < 0) | |||
| printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); | |||
| return 0; | |||
| } else if (err == -EAGAIN) { | |||
| while ((err = snd_pcm_resume(handle)) == -EAGAIN) | |||
| usleep(100); /* wait until the suspend flag is released */ | |||
| if (err < 0) { | |||
| err = snd_pcm_prepare(handle); | |||
| if (err < 0) | |||
| printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); | |||
| } | |||
| return 0; | |||
| } | |||
| return err; | |||
| } | |||
| static int set_hwformat( snd_pcm_t *handle, snd_pcm_hw_params_t *params ) | |||
| { | |||
| int i; | |||
| int err; | |||
| for( i=0; i<NUMFORMATS; i++ ) { | |||
| /* set the sample format */ | |||
| err = snd_pcm_hw_params_set_format(handle, params, formats[i].format_id); | |||
| if (err == 0) { | |||
| format = i; | |||
| return 0; | |||
| } | |||
| } | |||
| return err; | |||
| } | |||
| static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access, int rate, int channels, int period, int nperiods ) { | |||
| int err, dir=0; | |||
| unsigned int buffer_time; | |||
| unsigned int period_time; | |||
| unsigned int rrate; | |||
| unsigned int rchannels; | |||
| /* choose all parameters */ | |||
| err = snd_pcm_hw_params_any(handle, params); | |||
| if (err < 0) { | |||
| printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* set the interleaved read/write format */ | |||
| err = snd_pcm_hw_params_set_access(handle, params, access); | |||
| if (err < 0) { | |||
| printf("Access type not available for playback: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* set the sample format */ | |||
| err = set_hwformat(handle, params); | |||
| if (err < 0) { | |||
| printf("Sample format not available for playback: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* set the count of channels */ | |||
| rchannels = channels; | |||
| err = snd_pcm_hw_params_set_channels_near(handle, params, &rchannels); | |||
| if (err < 0) { | |||
| printf("Channels count (%i) not available for record: %s\n", channels, snd_strerror(err)); | |||
| return err; | |||
| } | |||
| if (rchannels != channels) { | |||
| printf("WARNING: chennel count does not match (requested %d got %d)\n", channels, rchannels); | |||
| num_channels = rchannels; | |||
| } | |||
| /* set the stream rate */ | |||
| rrate = rate; | |||
| err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0); | |||
| if (err < 0) { | |||
| printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err)); | |||
| return err; | |||
| } | |||
| if (rrate != rate) { | |||
| printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, rrate); | |||
| return -EINVAL; | |||
| } | |||
| /* set the buffer time */ | |||
| buffer_time = 1000000*(uint64_t)period*nperiods/rate; | |||
| err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir); | |||
| if (err < 0) { | |||
| printf("Unable to set buffer time %i for playback: %s\n", 1000000*period*nperiods/rate, snd_strerror(err)); | |||
| return err; | |||
| } | |||
| err = snd_pcm_hw_params_get_buffer_size( params, &real_buffer_size ); | |||
| if (err < 0) { | |||
| printf("Unable to get buffer size back: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| if( real_buffer_size != nperiods * period ) { | |||
| printf( "WARNING: buffer size does not match: (requested %d, got %d)\n", nperiods * period, (int) real_buffer_size ); | |||
| } | |||
| /* set the period time */ | |||
| period_time = 1000000*(uint64_t)period/rate; | |||
| err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir); | |||
| if (err < 0) { | |||
| printf("Unable to set period time %i for playback: %s\n", 1000000*period/rate, snd_strerror(err)); | |||
| return err; | |||
| } | |||
| err = snd_pcm_hw_params_get_period_size(params, &real_period_size, NULL ); | |||
| if (err < 0) { | |||
| printf("Unable to get period size back: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| if( real_period_size != period ) { | |||
| printf( "WARNING: period size does not match: (requested %i, got %i)\n", period, (int)real_period_size ); | |||
| } | |||
| /* write the parameters to device */ | |||
| err = snd_pcm_hw_params(handle, params); | |||
| if (err < 0) { | |||
| printf("Unable to set hw params for playback: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| return 0; | |||
| } | |||
| static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int period, int nperiods) { | |||
| int err; | |||
| /* get the current swparams */ | |||
| err = snd_pcm_sw_params_current(handle, swparams); | |||
| if (err < 0) { | |||
| printf("Unable to determine current swparams for capture: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* start the transfer when the buffer is full */ | |||
| err = snd_pcm_sw_params_set_start_threshold(handle, swparams, period ); | |||
| if (err < 0) { | |||
| printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, -1 ); | |||
| if (err < 0) { | |||
| printf("Unable to set start threshold mode for capture: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* allow the transfer when at least period_size samples can be processed */ | |||
| err = snd_pcm_sw_params_set_avail_min(handle, swparams, 1 ); | |||
| if (err < 0) { | |||
| printf("Unable to set avail min for capture: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* align all transfers to 1 sample */ | |||
| err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1); | |||
| if (err < 0) { | |||
| printf("Unable to set transfer align for capture: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| /* write the parameters to the playback device */ | |||
| err = snd_pcm_sw_params(handle, swparams); | |||
| if (err < 0) { | |||
| printf("Unable to set sw params for capture: %s\n", snd_strerror(err)); | |||
| return err; | |||
| } | |||
| return 0; | |||
| } | |||
| // ok... i only need this function to communicate with the alsa bloat api... | |||
| static snd_pcm_t *open_audiofd( char *device_name, int capture, int rate, int channels, int period, int nperiods ) { | |||
| int err; | |||
| snd_pcm_t *handle; | |||
| snd_pcm_hw_params_t *hwparams; | |||
| snd_pcm_sw_params_t *swparams; | |||
| snd_pcm_hw_params_alloca(&hwparams); | |||
| snd_pcm_sw_params_alloca(&swparams); | |||
| if ((err = snd_pcm_open(&(handle), device_name, capture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK )) < 0) { | |||
| printf("Capture open error: %s\n", snd_strerror(err)); | |||
| return NULL; | |||
| } | |||
| if ((err = set_hwparams(handle, hwparams,SND_PCM_ACCESS_RW_INTERLEAVED, rate, channels, period, nperiods )) < 0) { | |||
| printf("Setting of hwparams failed: %s\n", snd_strerror(err)); | |||
| return NULL; | |||
| } | |||
| if ((err = set_swparams(handle, swparams, period, nperiods)) < 0) { | |||
| printf("Setting of swparams failed: %s\n", snd_strerror(err)); | |||
| return NULL; | |||
| } | |||
| //snd_pcm_start( handle ); | |||
| //snd_pcm_wait( handle, 200 ); | |||
| int num_null_samples = nperiods * period * channels; | |||
| char *tmp = alloca( num_null_samples * formats[format].sample_size ); | |||
| memset( tmp, 0, num_null_samples * formats[format].sample_size ); | |||
| snd_pcm_writei( handle, tmp, num_null_samples ); | |||
| return handle; | |||
| } | |||
| double hann( double x ) | |||
| { | |||
| return 0.5 * (1.0 - cos( 2*M_PI * x ) ); | |||
| } | |||
| /** | |||
| * The process callback for this JACK application. | |||
| * It is called by JACK at the appropriate times. | |||
| */ | |||
| int process (jack_nframes_t nframes, void *arg) { | |||
| int rlen; | |||
| int err; | |||
| snd_pcm_sframes_t delay = target_delay; | |||
| int i; | |||
| delay = (num_periods*period_size)-snd_pcm_avail( alsa_handle ) ; | |||
| delay -= jack_frames_since_cycle_start( client ); | |||
| // Do it the hard way. | |||
| // this is for compensating xruns etc... | |||
| if( delay > (target_delay+max_diff) ) { | |||
| snd_pcm_rewind( alsa_handle, delay - target_delay ); | |||
| output_new_delay = (int) delay; | |||
| delay = target_delay; | |||
| // Set the resample_rate... we need to adjust the offset integral, to do this. | |||
| // first look at the PI controller, this code is just a special case, which should never execute once | |||
| // everything is swung in. | |||
| offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; | |||
| // Also clear the array. we are beginning a new control cycle. | |||
| for( i=0; i<smooth_size; i++ ) | |||
| offset_array[i] = 0.0; | |||
| } | |||
| if( delay < (target_delay-max_diff) ) { | |||
| output_new_delay = (int) delay; | |||
| while ((target_delay-delay) > 0) { | |||
| snd_pcm_uframes_t to_write = ((target_delay-delay) > 512) ? 512 : (target_delay-delay); | |||
| snd_pcm_writei( alsa_handle, tmpbuf, to_write ); | |||
| delay += to_write; | |||
| } | |||
| delay = target_delay; | |||
| // Set the resample_rate... we need to adjust the offset integral, to do this. | |||
| offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; | |||
| // Also clear the array. we are beginning a new control cycle. | |||
| for( i=0; i<smooth_size; i++ ) | |||
| offset_array[i] = 0.0; | |||
| } | |||
| /* ok... now we should have target_delay +- max_diff on the alsa side. | |||
| * | |||
| * calculate the number of frames, we want to get. | |||
| */ | |||
| double offset = delay - target_delay; | |||
| // Save offset. | |||
| offset_array[(offset_differential_index++)% smooth_size ] = offset; | |||
| // Build the mean of the windowed offset array | |||
| // basically fir lowpassing. | |||
| double smooth_offset = 0.0; | |||
| for( i=0; i<smooth_size; i++ ) | |||
| smooth_offset += | |||
| offset_array[ (i + offset_differential_index-1) % smooth_size] * window_array[i]; | |||
| smooth_offset /= (double) smooth_size; | |||
| // this is the integral of the smoothed_offset | |||
| offset_integral += smooth_offset; | |||
| // Clamp offset. | |||
| // the smooth offset still contains unwanted noise | |||
| // which would go straigth onto the resample coeff. | |||
| // it only used in the P component and the I component is used for the fine tuning anyways. | |||
| if( fabs( smooth_offset ) < pclamp ) | |||
| smooth_offset = 0.0; | |||
| // ok. now this is the PI controller. | |||
| // u(t) = K * ( e(t) + 1/T \int e(t') dt' ) | |||
| // K = 1/catch_factor and T = catch_factor2 | |||
| double current_resample_factor = static_resample_factor - smooth_offset / (double) catch_factor - offset_integral / (double) catch_factor / (double)catch_factor2; | |||
| // now quantize this value around resample_mean, so that the noise which is in the integral component doesnt hurt. | |||
| current_resample_factor = floor( (current_resample_factor - resample_mean) * controlquant + 0.5 ) / controlquant + resample_mean; | |||
| // Output "instrumentatio" gonna change that to real instrumentation in a few. | |||
| output_resampling_factor = (float) current_resample_factor; | |||
| output_diff = (float) smooth_offset; | |||
| output_integral = (float) offset_integral; | |||
| output_offset = (float) offset; | |||
| // Clamp a bit. | |||
| if( current_resample_factor < resample_lower_limit ) current_resample_factor = resample_lower_limit; | |||
| if( current_resample_factor > resample_upper_limit ) current_resample_factor = resample_upper_limit; | |||
| // Now Calculate how many samples we need. | |||
| rlen = ceil( ((double)nframes) * current_resample_factor )+2; | |||
| assert( rlen > 2 ); | |||
| // Calculate resample_mean so we can init ourselves to saner values. | |||
| resample_mean = 0.9999 * resample_mean + 0.0001 * current_resample_factor; | |||
| /* | |||
| * now this should do it... | |||
| */ | |||
| outbuf = alloca( rlen * formats[format].sample_size * num_channels ); | |||
| resampbuf = alloca( rlen * sizeof( float ) ); | |||
| /* | |||
| * render jack ports to the outbuf... | |||
| */ | |||
| int chn = 0; | |||
| JSList *node = playback_ports; | |||
| JSList *src_node = playback_srcs; | |||
| SRC_DATA src; | |||
| while ( node != NULL) | |||
| { | |||
| jack_port_t *port = (jack_port_t *) node->data; | |||
| float *buf = jack_port_get_buffer (port, nframes); | |||
| SRC_STATE *src_state = src_node->data; | |||
| src.data_in = buf; | |||
| src.input_frames = nframes; | |||
| src.data_out = resampbuf; | |||
| src.output_frames = rlen; | |||
| src.end_of_input = 0; | |||
| src.src_ratio = current_resample_factor; | |||
| src_process( src_state, &src ); | |||
| formats[format].jack_to_soundcard( outbuf + format[formats].sample_size * chn, resampbuf, src.output_frames_gen, num_channels*format[formats].sample_size, NULL); | |||
| src_node = jack_slist_next (src_node); | |||
| node = jack_slist_next (node); | |||
| chn++; | |||
| } | |||
| // now write the output... | |||
| again: | |||
| err = snd_pcm_writei(alsa_handle, outbuf, src.output_frames_gen); | |||
| //err = snd_pcm_writei(alsa_handle, outbuf, src.output_frames_gen); | |||
| if( err < 0 ) { | |||
| printf( "err = %d\n", err ); | |||
| if (xrun_recovery(alsa_handle, err) < 0) { | |||
| printf("Write error: %s\n", snd_strerror(err)); | |||
| exit(EXIT_FAILURE); | |||
| } | |||
| goto again; | |||
| } | |||
| return 0; | |||
| } | |||
| /** | |||
| * the latency callback. | |||
| * sets up the latencies on the ports. | |||
| */ | |||
| void | |||
| latency_cb (jack_latency_callback_mode_t mode, void *arg) | |||
| { | |||
| jack_latency_range_t range; | |||
| JSList *node; | |||
| range.min = range.max = target_delay; | |||
| if (mode == JackCaptureLatency) { | |||
| for (node = capture_ports; node; node = jack_slist_next (node)) { | |||
| jack_port_t *port = node->data; | |||
| jack_port_set_latency_range (port, mode, &range); | |||
| } | |||
| } else { | |||
| for (node = playback_ports; node; node = jack_slist_next (node)) { | |||
| jack_port_t *port = node->data; | |||
| jack_port_set_latency_range (port, mode, &range); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * Allocate the necessary jack ports... | |||
| */ | |||
| void alloc_ports( int n_capture, int n_playback ) { | |||
| int port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; | |||
| int chn; | |||
| jack_port_t *port; | |||
| char buf[32]; | |||
| capture_ports = NULL; | |||
| for (chn = 0; chn < n_capture; chn++) | |||
| { | |||
| snprintf (buf, sizeof(buf) - 1, "capture_%u", chn+1); | |||
| port = jack_port_register (client, buf, | |||
| JACK_DEFAULT_AUDIO_TYPE, | |||
| port_flags, 0); | |||
| if (!port) | |||
| { | |||
| printf( "jacknet_client: cannot register port for %s", buf); | |||
| break; | |||
| } | |||
| capture_srcs = jack_slist_append( capture_srcs, src_new( 4-samplerate_quality, 1, NULL ) ); | |||
| capture_ports = jack_slist_append (capture_ports, port); | |||
| } | |||
| port_flags = JackPortIsInput; | |||
| playback_ports = NULL; | |||
| for (chn = 0; chn < n_playback; chn++) | |||
| { | |||
| snprintf (buf, sizeof(buf) - 1, "playback_%u", chn+1); | |||
| port = jack_port_register (client, buf, | |||
| JACK_DEFAULT_AUDIO_TYPE, | |||
| port_flags, 0); | |||
| if (!port) | |||
| { | |||
| printf( "jacknet_client: cannot register port for %s", buf); | |||
| break; | |||
| } | |||
| playback_srcs = jack_slist_append( playback_srcs, src_new( 4-samplerate_quality, 1, NULL ) ); | |||
| playback_ports = jack_slist_append (playback_ports, port); | |||
| } | |||
| } | |||
| /** | |||
| * This is the shutdown callback for this JACK application. | |||
| * It is called by JACK if the server ever shuts down or | |||
| * decides to disconnect the client. | |||
| */ | |||
| void jack_shutdown (void *arg) { | |||
| exit (1); | |||
| } | |||
| /** | |||
| * be user friendly. | |||
| * be user friendly. | |||
| * be user friendly. | |||
| */ | |||
| void printUsage() { | |||
| fprintf(stderr, "usage: alsa_out [options]\n" | |||
| "\n" | |||
| " -j <jack name> - client name\n" | |||
| " -d <alsa_device> \n" | |||
| " -c <channels> \n" | |||
| " -p <period_size> \n" | |||
| " -n <num_period> \n" | |||
| " -r <sample_rate> \n" | |||
| " -q <sample_rate quality [0..4]\n" | |||
| " -m <max_diff> \n" | |||
| " -t <target_delay> \n" | |||
| " -i turns on instrumentation\n" | |||
| " -v turns on printouts\n" | |||
| "\n"); | |||
| } | |||
| /** | |||
| * the main function.... | |||
| */ | |||
| void | |||
| sigterm_handler( int signal ) | |||
| { | |||
| quit = 1; | |||
| } | |||
| int main (int argc, char *argv[]) { | |||
| char jack_name[30] = "alsa_out"; | |||
| char alsa_device[30] = "hw:0"; | |||
| extern char *optarg; | |||
| extern int optind, optopt; | |||
| int errflg=0; | |||
| int c; | |||
| while ((c = getopt(argc, argv, "ivj:r:c:p:n:d:q:m:t:f:F:C:Q:s:")) != -1) { | |||
| switch(c) { | |||
| case 'j': | |||
| strcpy(jack_name,optarg); | |||
| break; | |||
| case 'r': | |||
| sample_rate = atoi(optarg); | |||
| break; | |||
| case 'c': | |||
| num_channels = atoi(optarg); | |||
| break; | |||
| case 'p': | |||
| period_size = atoi(optarg); | |||
| break; | |||
| case 'n': | |||
| num_periods = atoi(optarg); | |||
| break; | |||
| case 'd': | |||
| strcpy(alsa_device,optarg); | |||
| break; | |||
| case 't': | |||
| target_delay = atoi(optarg); | |||
| break; | |||
| case 'q': | |||
| samplerate_quality = atoi(optarg); | |||
| break; | |||
| case 'm': | |||
| max_diff = atoi(optarg); | |||
| break; | |||
| case 'f': | |||
| catch_factor = atoi(optarg); | |||
| break; | |||
| case 'F': | |||
| catch_factor2 = atoi(optarg); | |||
| break; | |||
| case 'C': | |||
| pclamp = (double) atoi(optarg); | |||
| break; | |||
| case 'Q': | |||
| controlquant = (double) atoi(optarg); | |||
| break; | |||
| case 'v': | |||
| verbose = 1; | |||
| break; | |||
| case 'i': | |||
| instrument = 1; | |||
| break; | |||
| case 's': | |||
| smooth_size = atoi(optarg); | |||
| break; | |||
| case ':': | |||
| fprintf(stderr, | |||
| "Option -%c requires an operand\n", optopt); | |||
| errflg++; | |||
| break; | |||
| case '?': | |||
| fprintf(stderr, | |||
| "Unrecognized option: -%c\n", optopt); | |||
| errflg++; | |||
| } | |||
| } | |||
| if (errflg) { | |||
| printUsage(); | |||
| exit(2); | |||
| } | |||
| if( (samplerate_quality < 0) || (samplerate_quality > 4) ) { | |||
| fprintf (stderr, "invalid samplerate quality\n"); | |||
| return 1; | |||
| } | |||
| if ((client = jack_client_open (jack_name, 0, NULL)) == 0) { | |||
| fprintf (stderr, "jack server not running?\n"); | |||
| return 1; | |||
| } | |||
| /* tell the JACK server to call `process()' whenever | |||
| there is work to be done. | |||
| */ | |||
| jack_set_process_callback (client, process, 0); | |||
| /* tell the JACK server to call `jack_shutdown()' if | |||
| it ever shuts down, either entirely, or if it | |||
| just decides to stop calling us. | |||
| */ | |||
| jack_on_shutdown (client, jack_shutdown, 0); | |||
| if (jack_set_latency_callback) | |||
| jack_set_latency_callback (client, latency_cb, 0); | |||
| // get jack sample_rate | |||
| jack_sample_rate = jack_get_sample_rate( client ); | |||
| if( !sample_rate ) | |||
| sample_rate = jack_sample_rate; | |||
| static_resample_factor = (double) sample_rate / (double) jack_sample_rate; | |||
| resample_lower_limit = static_resample_factor * 0.25; | |||
| resample_upper_limit = static_resample_factor * 4.0; | |||
| resample_mean = static_resample_factor; | |||
| offset_array = malloc( sizeof(double) * smooth_size ); | |||
| if( offset_array == NULL ) { | |||
| fprintf( stderr, "no memory for offset_array !!!\n" ); | |||
| exit(20); | |||
| } | |||
| window_array = malloc( sizeof(double) * smooth_size ); | |||
| if( window_array == NULL ) { | |||
| fprintf( stderr, "no memory for window_array !!!\n" ); | |||
| exit(20); | |||
| } | |||
| int i; | |||
| for( i=0; i<smooth_size; i++ ) { | |||
| offset_array[i] = 0.0; | |||
| window_array[i] = hann( (double) i / ((double) smooth_size - 1.0) ); | |||
| } | |||
| jack_buffer_size = jack_get_buffer_size( client ); | |||
| // Setup target delay and max_diff for the normal user, who does not play with them... | |||
| if( !target_delay ) | |||
| target_delay = (num_periods*period_size / 2) - jack_buffer_size/2; | |||
| if( !max_diff ) | |||
| max_diff = target_delay; | |||
| if( max_diff > target_delay ) { | |||
| fprintf( stderr, "target_delay (%d) cant be smaller than max_diff(%d)\n", target_delay, max_diff ); | |||
| exit(20); | |||
| } | |||
| if( (target_delay+max_diff) > (num_periods*period_size) ) { | |||
| fprintf( stderr, "target_delay+max_diff (%d) cant be bigger than buffersize(%d)\n", target_delay+max_diff, num_periods*period_size ); | |||
| exit(20); | |||
| } | |||
| // now open the alsa fd... | |||
| alsa_handle = open_audiofd( alsa_device, 0, sample_rate, num_channels, period_size, num_periods); | |||
| if( alsa_handle == 0 ) | |||
| exit(20); | |||
| printf( "selected sample format: %s\n", formats[format].name ); | |||
| // alloc input ports, which are blasted out to alsa... | |||
| alloc_ports( 0, num_channels ); | |||
| outbuf = malloc( num_periods * period_size * formats[format].sample_size * num_channels ); | |||
| resampbuf = malloc( num_periods * period_size * sizeof( float ) ); | |||
| tmpbuf = malloc( 512 * formats[format].sample_size * num_channels ); | |||
| if ((outbuf == NULL) || (resampbuf == NULL) || (tmpbuf == NULL)) | |||
| { | |||
| fprintf( stderr, "no memory for buffers.\n" ); | |||
| exit(20); | |||
| } | |||
| /* tell the JACK server that we are ready to roll */ | |||
| if (jack_activate (client)) { | |||
| fprintf (stderr, "cannot activate client"); | |||
| return 1; | |||
| } | |||
| signal( SIGTERM, sigterm_handler ); | |||
| signal( SIGINT, sigterm_handler ); | |||
| if( verbose ) { | |||
| while(!quit) { | |||
| usleep(500000); | |||
| if( output_new_delay ) { | |||
| printf( "delay = %d\n", output_new_delay ); | |||
| output_new_delay = 0; | |||
| } | |||
| printf( "res: %f, \tdiff = %f, \toffset = %f \n", output_resampling_factor, output_diff, output_offset ); | |||
| } | |||
| } else if( instrument ) { | |||
| printf( "# n\tresamp\tdiff\toffseti\tintegral\n"); | |||
| int n=0; | |||
| while(!quit) { | |||
| usleep(1000); | |||
| printf( "%d\t%f\t%f\t%f\t%f\n", n++, output_resampling_factor, output_diff, output_offset, output_integral ); | |||
| } | |||
| } else { | |||
| while(!quit) | |||
| { | |||
| usleep(500000); | |||
| if( output_new_delay ) { | |||
| printf( "delay = %d\n", output_new_delay ); | |||
| output_new_delay = 0; | |||
| } | |||
| } | |||
| } | |||
| jack_deactivate( client ); | |||
| jack_client_close (client); | |||
| exit (0); | |||
| } | |||
| @@ -0,0 +1,125 @@ | |||
| /* | |||
| * bufsize.c -- change JACK buffer size. | |||
| * | |||
| * 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. | |||
| */ | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <unistd.h> | |||
| #include <signal.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/transport.h> | |||
| char *package; /* program name */ | |||
| jack_client_t *client; | |||
| jack_nframes_t nframes; | |||
| int just_print_bufsize=0; | |||
| void jack_shutdown(void *arg) | |||
| { | |||
| fprintf(stderr, "JACK shut down, exiting ...\n"); | |||
| exit(1); | |||
| } | |||
| void signal_handler(int sig) | |||
| { | |||
| jack_client_close(client); | |||
| fprintf(stderr, "signal received, exiting ...\n"); | |||
| exit(0); | |||
| } | |||
| void parse_arguments(int argc, char *argv[]) | |||
| { | |||
| /* basename $0 */ | |||
| package = strrchr(argv[0], '/'); | |||
| if (package == 0) | |||
| package = argv[0]; | |||
| else | |||
| package++; | |||
| if (argc==1) { | |||
| just_print_bufsize = 1; | |||
| return; | |||
| } | |||
| if (argc < 2) { | |||
| fprintf(stderr, "usage: %s <bufsize>\n", package); | |||
| exit(9); | |||
| } | |||
| if (strspn (argv[1], "0123456789") != strlen (argv[1])) { | |||
| fprintf(stderr, "usage: %s <bufsize>\n", package); | |||
| exit(8); | |||
| } | |||
| nframes = strtoul(argv[1], NULL, 0); | |||
| if (errno == ERANGE) { | |||
| fprintf(stderr, "%s: invalid buffer size: %s (range is 1-16384)\n", | |||
| package, argv[1]); | |||
| exit(2); | |||
| } | |||
| if (nframes < 1 || nframes > 16384) { | |||
| fprintf(stderr, "%s: invalid buffer size: %s (range is 1-16384)\n", | |||
| package, argv[1]); | |||
| exit(3); | |||
| } | |||
| } | |||
| void silent_function( const char *ignore ) | |||
| { | |||
| } | |||
| int main(int argc, char *argv[]) | |||
| { | |||
| int rc; | |||
| parse_arguments(argc, argv); | |||
| if (just_print_bufsize) | |||
| jack_set_info_function( silent_function ); | |||
| /* become a JACK client */ | |||
| if ((client = jack_client_open(package, JackNullOption, NULL)) == 0) { | |||
| fprintf(stderr, "JACK server not running?\n"); | |||
| exit(1); | |||
| } | |||
| signal(SIGQUIT, signal_handler); | |||
| signal(SIGTERM, signal_handler); | |||
| signal(SIGHUP, signal_handler); | |||
| signal(SIGINT, signal_handler); | |||
| jack_on_shutdown(client, jack_shutdown, 0); | |||
| if (just_print_bufsize) { | |||
| fprintf(stdout, "%d\n", jack_get_buffer_size( client ) ); | |||
| rc=0; | |||
| } | |||
| else | |||
| { | |||
| rc = jack_set_buffer_size(client, nframes); | |||
| if (rc) | |||
| fprintf(stderr, "jack_set_buffer_size(): %s\n", strerror(rc)); | |||
| } | |||
| jack_client_close(client); | |||
| return rc; | |||
| } | |||
| @@ -0,0 +1,222 @@ | |||
| /* | |||
| Copyright (C) 2002 Jeremy Hall | |||
| 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. | |||
| */ | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <unistd.h> | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #include <getopt.h> | |||
| #include <config.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/session.h> | |||
| #define TRUE 1 | |||
| #define FALSE 0 | |||
| void | |||
| show_version (char *my_name) | |||
| { | |||
| fprintf (stderr, "%s: JACK Audio Connection Kit version " VERSION "\n", my_name); | |||
| } | |||
| void | |||
| show_usage (char *my_name) | |||
| { | |||
| show_version (my_name); | |||
| fprintf (stderr, "\nusage: %s [options] port1 port2\n", my_name); | |||
| fprintf (stderr, "Connects two JACK ports together.\n\n"); | |||
| fprintf (stderr, " -s, --server <name> Connect to the jack server named <name>\n"); | |||
| fprintf (stderr, " -v, --version Output version information and exit\n"); | |||
| fprintf (stderr, " -h, --help Display this help message\n\n"); | |||
| fprintf (stderr, "For more information see http://jackaudio.org/\n"); | |||
| } | |||
| int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| jack_client_t *client; | |||
| jack_status_t status; | |||
| char *server_name = NULL; | |||
| int c; | |||
| int option_index; | |||
| jack_options_t options = JackNoStartServer; | |||
| char *my_name = strrchr(argv[0], '/'); | |||
| jack_port_t *src_port = 0; | |||
| jack_port_t *dst_port = 0; | |||
| jack_port_t *port1 = 0; | |||
| jack_port_t *port2 = 0; | |||
| char portA[300]; | |||
| char portB[300]; | |||
| int use_uuid=0; | |||
| int connecting, disconnecting; | |||
| int port1_flags, port2_flags; | |||
| int rc = 1; | |||
| struct option long_options[] = { | |||
| { "server", 1, 0, 's' }, | |||
| { "help", 0, 0, 'h' }, | |||
| { "version", 0, 0, 'v' }, | |||
| { "uuid", 0, 0, 'u' }, | |||
| { 0, 0, 0, 0 } | |||
| }; | |||
| while ((c = getopt_long (argc, argv, "s:hvu", long_options, &option_index)) >= 0) { | |||
| switch (c) { | |||
| case 's': | |||
| server_name = (char *) malloc (sizeof (char) * strlen(optarg)); | |||
| strcpy (server_name, optarg); | |||
| options |= JackServerName; | |||
| break; | |||
| case 'u': | |||
| use_uuid = 1; | |||
| break; | |||
| case 'h': | |||
| show_usage (my_name); | |||
| return 1; | |||
| break; | |||
| case 'v': | |||
| show_version (my_name); | |||
| return 1; | |||
| break; | |||
| default: | |||
| show_usage (my_name); | |||
| return 1; | |||
| break; | |||
| } | |||
| } | |||
| connecting = disconnecting = FALSE; | |||
| if (my_name == 0) { | |||
| my_name = argv[0]; | |||
| } else { | |||
| my_name ++; | |||
| } | |||
| if (strstr(my_name, "disconnect")) { | |||
| disconnecting = 1; | |||
| } else if (strstr(my_name, "connect")) { | |||
| connecting = 1; | |||
| } else { | |||
| fprintf(stderr, "ERROR! client should be called jack_connect or jack_disconnect. client is called %s\n", my_name); | |||
| return 1; | |||
| } | |||
| if (argc < 3) show_usage(my_name); | |||
| /* try to become a client of the JACK server */ | |||
| if ((client = jack_client_open (my_name, options, &status, server_name)) == 0) { | |||
| fprintf (stderr, "jack server not running?\n"); | |||
| return 1; | |||
| } | |||
| /* find the two ports */ | |||
| if( use_uuid ) { | |||
| char *tmpname; | |||
| char *clientname; | |||
| char *portname; | |||
| tmpname = strdup( argv[argc-1] ); | |||
| portname = strchr( tmpname, ':' ); | |||
| portname[0] = '\0'; | |||
| portname+=1; | |||
| clientname = jack_get_client_name_by_uuid( client, tmpname ); | |||
| if( clientname ) { | |||
| snprintf( portA, sizeof(portA), "%s:%s", clientname, portname ); | |||
| jack_free( clientname ); | |||
| } else { | |||
| snprintf( portA, sizeof(portA), "%s", argv[argc-1] ); | |||
| } | |||
| free( tmpname ); | |||
| tmpname = strdup( argv[argc-2] ); | |||
| portname = strchr( tmpname, ':' ); | |||
| portname[0] = '\0'; | |||
| portname+=1; | |||
| clientname = jack_get_client_name_by_uuid( client, tmpname ); | |||
| if( clientname ) { | |||
| snprintf( portB, sizeof(portB), "%s:%s", clientname, portname ); | |||
| jack_free( clientname ); | |||
| } else { | |||
| snprintf( portB, sizeof(portB), "%s", argv[argc-2] ); | |||
| } | |||
| free( tmpname ); | |||
| } else { | |||
| snprintf( portA, sizeof(portA), "%s", argv[argc-1] ); | |||
| snprintf( portB, sizeof(portB), "%s", argv[argc-2] ); | |||
| } | |||
| if ((port1 = jack_port_by_name(client, portA)) == 0) { | |||
| fprintf (stderr, "ERROR %s not a valid port\n", portA); | |||
| goto exit; | |||
| } | |||
| if ((port2 = jack_port_by_name(client, portB)) == 0) { | |||
| fprintf (stderr, "ERROR %s not a valid port\n", portB); | |||
| goto exit; | |||
| } | |||
| port1_flags = jack_port_flags (port1); | |||
| port2_flags = jack_port_flags (port2); | |||
| if (port1_flags & JackPortIsInput) { | |||
| if (port2_flags & JackPortIsOutput) { | |||
| src_port = port2; | |||
| dst_port = port1; | |||
| } | |||
| } else { | |||
| if (port2_flags & JackPortIsInput) { | |||
| src_port = port1; | |||
| dst_port = port2; | |||
| } | |||
| } | |||
| if (!src_port || !dst_port) { | |||
| fprintf (stderr, "arguments must include 1 input port and 1 output port\n"); | |||
| goto exit; | |||
| } | |||
| /* connect the ports. Note: you can't do this before | |||
| the client is activated (this may change in the future). | |||
| */ | |||
| if (connecting) { | |||
| if (jack_connect(client, jack_port_name(src_port), jack_port_name(dst_port))) { | |||
| goto exit; | |||
| } | |||
| } | |||
| if (disconnecting) { | |||
| if (jack_disconnect(client, jack_port_name(src_port), jack_port_name(dst_port))) { | |||
| goto exit; | |||
| } | |||
| } | |||
| /* everything was ok, so setting exitcode to 0 */ | |||
| rc = 0; | |||
| exit: | |||
| jack_client_close (client); | |||
| exit (rc); | |||
| } | |||
| @@ -0,0 +1,92 @@ | |||
| /* | |||
| Copyright (C) 2007 Paul Davis | |||
| 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. | |||
| */ | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <unistd.h> | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #include <jack/jack.h> | |||
| void | |||
| port_callback (jack_port_id_t port, int yn, void* arg) | |||
| { | |||
| printf ("Port %d %s\n", port, (yn ? "registered" : "unregistered")); | |||
| } | |||
| void | |||
| connect_callback (jack_port_id_t a, jack_port_id_t b, int yn, void* arg) | |||
| { | |||
| printf ("Ports %d and %d %s\n", a, b, (yn ? "connected" : "disconnected")); | |||
| } | |||
| void | |||
| client_callback (const char* client, int yn, void* arg) | |||
| { | |||
| printf ("Client %s %s\n", client, (yn ? "registered" : "unregistered")); | |||
| } | |||
| int | |||
| graph_callback (void* arg) | |||
| { | |||
| printf ("Graph reordered\n"); | |||
| return 0; | |||
| } | |||
| int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| jack_client_t *client; | |||
| jack_options_t options = JackNullOption; | |||
| jack_status_t status; | |||
| if ((client = jack_client_open ("event-monitor", options, &status, NULL)) == 0) { | |||
| fprintf (stderr, "jack_client_open() failed, " | |||
| "status = 0x%2.0x\n", status); | |||
| if (status & JackServerFailed) { | |||
| fprintf (stderr, "Unable to connect to JACK server\n"); | |||
| } | |||
| return 1; | |||
| } | |||
| if (jack_set_port_registration_callback (client, port_callback, NULL)) { | |||
| fprintf (stderr, "cannot set port registration callback\n"); | |||
| return 1; | |||
| } | |||
| if (jack_set_port_connect_callback (client, connect_callback, NULL)) { | |||
| fprintf (stderr, "cannot set port connect callback\n"); | |||
| return 1; | |||
| } | |||
| if (jack_set_client_registration_callback (client, client_callback, NULL)) { | |||
| fprintf (stderr, "cannot set client registration callback\n"); | |||
| return 1; | |||
| } | |||
| if (jack_set_graph_order_callback (client, graph_callback, NULL)) { | |||
| fprintf (stderr, "cannot set graph order registration callback\n"); | |||
| return 1; | |||
| } | |||
| if (jack_activate (client)) { | |||
| fprintf (stderr, "cannot activate client"); | |||
| return 1; | |||
| } | |||
| sleep (-1); | |||
| exit (0); | |||
| } | |||
| @@ -0,0 +1,86 @@ | |||
| /* | |||
| * freewheel - start/stop JACK "freewheeling" mode | |||
| * | |||
| * Copyright (C) 2003 Paul Davis. | |||
| * | |||
| * 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. | |||
| */ | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <unistd.h> | |||
| #include <signal.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/transport.h> | |||
| char *package; /* program name */ | |||
| jack_client_t *client; | |||
| int onoff; | |||
| void jack_shutdown(void *arg) | |||
| { | |||
| fprintf(stderr, "JACK shut down, exiting ...\n"); | |||
| exit(1); | |||
| } | |||
| void signal_handler(int sig) | |||
| { | |||
| jack_client_close(client); | |||
| fprintf(stderr, "signal received, exiting ...\n"); | |||
| exit(0); | |||
| } | |||
| void parse_arguments(int argc, char *argv[]) | |||
| { | |||
| if (argc < 2) { | |||
| fprintf(stderr, "usage: %s y|n\n", package); | |||
| exit(9); | |||
| } | |||
| if (argv[1][0] == 'y' || argv[1][0] == 'Y' || argv[1][0] == '1') { | |||
| onoff = 1; | |||
| } else { | |||
| onoff = 0; | |||
| } | |||
| } | |||
| int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| parse_arguments (argc, argv); | |||
| /* become a JACK client */ | |||
| if ((client = jack_client_open ("freewheel", JackNullOption, NULL)) == 0) { | |||
| fprintf (stderr, "JACK server not running?\n"); | |||
| exit(1); | |||
| } | |||
| signal (SIGQUIT, signal_handler); | |||
| signal (SIGTERM, signal_handler); | |||
| signal (SIGHUP, signal_handler); | |||
| signal (SIGINT, signal_handler); | |||
| jack_on_shutdown (client, jack_shutdown, 0); | |||
| if (jack_set_freewheel (client, onoff)) { | |||
| fprintf (stderr, "failed to reset freewheel mode\n"); | |||
| } | |||
| jack_client_close(client); | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,260 @@ | |||
| /* | |||
| Copyright (C) 2003-2008 Fons Adriaensen <fons@kokkinizita.net> | |||
| 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. | |||
| */ | |||
| // -------------------------------------------------------------------------------- | |||
| #include <stdlib.h> | |||
| #include <stdio.h> | |||
| #include <math.h> | |||
| #include <unistd.h> | |||
| #include <jack/jack.h> | |||
| struct Freq | |||
| { | |||
| int p; | |||
| int f; | |||
| float a; | |||
| float xa; | |||
| float ya; | |||
| float xf; | |||
| float yf; | |||
| }; | |||
| struct MTDM | |||
| { | |||
| double _del; | |||
| double _err; | |||
| int _cnt; | |||
| int _inv; | |||
| struct Freq _freq [5]; | |||
| }; | |||
| struct MTDM * mtdm_new (void) | |||
| { | |||
| int i; | |||
| struct Freq *F; | |||
| struct MTDM *retval = malloc( sizeof(struct MTDM) ); | |||
| if (retval==NULL) | |||
| return NULL; | |||
| retval->_cnt = 0; | |||
| retval->_inv = 0; | |||
| retval->_freq [0].f = 4096; | |||
| retval->_freq [1].f = 512; | |||
| retval->_freq [2].f = 1088; | |||
| retval->_freq [3].f = 1544; | |||
| retval->_freq [4].f = 2049; | |||
| retval->_freq [0].a = 0.2f; | |||
| retval->_freq [1].a = 0.1f; | |||
| retval->_freq [2].a = 0.1f; | |||
| retval->_freq [3].a = 0.1f; | |||
| retval->_freq [4].a = 0.1f; | |||
| for (i = 0, F = retval->_freq; i < 5; i++, F++) | |||
| { | |||
| F->p = 128; | |||
| F->xa = F->ya = 0.0f; | |||
| F->xf = F->yf = 0.0f; | |||
| } | |||
| return retval; | |||
| } | |||
| int mtdm_process (struct MTDM *self, size_t len, float *ip, float *op) | |||
| { | |||
| int i; | |||
| float vip, vop, a, c, s; | |||
| struct Freq *F; | |||
| while (len--) | |||
| { | |||
| vop = 0.0f; | |||
| vip = *ip++; | |||
| for (i = 0, F = self->_freq; i < 5; i++, F++) | |||
| { | |||
| a = 2 * (float) M_PI * (F->p & 65535) / 65536.0; | |||
| F->p += F->f; | |||
| c = cosf (a); | |||
| s = -sinf (a); | |||
| vop += F->a * s; | |||
| F->xa += s * vip; | |||
| F->ya += c * vip; | |||
| } | |||
| *op++ = vop; | |||
| if (++(self->_cnt) == 16) | |||
| { | |||
| for (i = 0, F = self->_freq; i < 5; i++, F++) | |||
| { | |||
| F->xf += 1e-3f * (F->xa - F->xf + 1e-20); | |||
| F->yf += 1e-3f * (F->ya - F->yf + 1e-20); | |||
| F->xa = F->ya = 0.0f; | |||
| } | |||
| self->_cnt = 0; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int mtdm_resolve (struct MTDM *self) | |||
| { | |||
| int i, k, m; | |||
| double d, e, f0, p; | |||
| struct Freq *F = self->_freq; | |||
| if (hypot (F->xf, F->yf) < 0.01) return -1; | |||
| d = atan2 (F->yf, F->xf) / (2 * M_PI); | |||
| if (self->_inv) d += 0.5f; | |||
| if (d > 0.5f) d -= 1.0f; | |||
| f0 = self->_freq [0].f; | |||
| m = 1; | |||
| self->_err = 0.0; | |||
| for (i = 0; i < 4; i++) | |||
| { | |||
| F++; | |||
| p = atan2 (F->yf, F->xf) / (2 * M_PI) - d * F->f / f0; | |||
| if (self->_inv) p += 0.5f; | |||
| p -= floor (p); | |||
| p *= 8; | |||
| k = (int)(floor (p + 0.5)); | |||
| e = fabs (p - k); | |||
| if (e > self->_err) self->_err = e; | |||
| if (e > 0.4) return 1; | |||
| d += m * (k & 7); | |||
| m *= 8; | |||
| } | |||
| self->_del = 16 * d; | |||
| return 0; | |||
| } | |||
| void mtdm_invert (struct MTDM *self) | |||
| { | |||
| self->_inv ^= 1; | |||
| } | |||
| // -------------------------------------------------------------------------------- | |||
| static struct MTDM *mtdm; | |||
| static jack_client_t *jack_handle; | |||
| static jack_port_t *jack_capt; | |||
| static jack_port_t *jack_play; | |||
| jack_latency_range_t capture_latency = {-1, -1}; | |||
| jack_latency_range_t playback_latency = {-1, -1}; | |||
| void | |||
| latency_cb (jack_latency_callback_mode_t mode, void *arg) | |||
| { | |||
| jack_latency_range_t range; | |||
| range.min = range.max = 0; | |||
| if (mode == JackCaptureLatency) { | |||
| jack_port_set_latency_range (jack_play, mode, &range); | |||
| jack_port_get_latency_range (jack_capt, mode, &range); | |||
| if ((range.min != capture_latency.min) || (range.max != capture_latency.max)) { | |||
| capture_latency = range; | |||
| printf ("new capture latency: [%d, %d]\n", range.min, range.max); | |||
| } | |||
| } else { | |||
| jack_port_set_latency_range (jack_capt, mode, &range); | |||
| jack_port_get_latency_range (jack_play, mode, &range); | |||
| if ((range.min != playback_latency.min) || (range.max != playback_latency.max)) { | |||
| playback_latency = range; | |||
| printf ("new playback latency: [%d, %d]\n", range.min, range.max); | |||
| } | |||
| } | |||
| } | |||
| int jack_callback (jack_nframes_t nframes, void *arg) | |||
| { | |||
| float *ip, *op; | |||
| ip = (float *)(jack_port_get_buffer (jack_capt, nframes)); | |||
| op = (float *)(jack_port_get_buffer (jack_play, nframes)); | |||
| mtdm_process (mtdm, nframes, ip, op); | |||
| return 0; | |||
| } | |||
| int main (int ac, char *av []) | |||
| { | |||
| float t; | |||
| jack_status_t s; | |||
| mtdm = mtdm_new(); | |||
| jack_handle = jack_client_open ("jack_delay", JackNoStartServer, &s); | |||
| if (jack_handle == 0) | |||
| { | |||
| fprintf (stderr, "Can't connect to Jack, is the server running ?\n"); | |||
| exit (1); | |||
| } | |||
| jack_set_process_callback (jack_handle, jack_callback, 0); | |||
| if (jack_set_latency_callback) | |||
| jack_set_latency_callback (jack_handle, latency_cb, 0); | |||
| jack_capt = jack_port_register (jack_handle, "in", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); | |||
| jack_play = jack_port_register (jack_handle, "out", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); | |||
| t = 1000.0f / jack_get_sample_rate (jack_handle); | |||
| if (jack_activate (jack_handle)) | |||
| { | |||
| fprintf(stderr, "Can't activate Jack"); | |||
| return 1; | |||
| } | |||
| while (1) | |||
| { | |||
| #ifdef WIN32 | |||
| Sleep (250); | |||
| #else | |||
| usleep (250000); | |||
| #endif | |||
| if (mtdm_resolve (mtdm) < 0) printf ("Signal below threshold...\n"); | |||
| else | |||
| { | |||
| jack_nframes_t systemic_latency; | |||
| if (mtdm->_err > 0.3) | |||
| { | |||
| mtdm_invert ( mtdm ); | |||
| mtdm_resolve ( mtdm ); | |||
| } | |||
| systemic_latency = (jack_nframes_t) floor (mtdm->_del - (capture_latency.max + playback_latency.max)); | |||
| printf ("%10.3lf frames %10.3lf ms total roundtrip latency\n\textra loopback latency: %u frames\n\tuse %u for the backend arguments -I and -O", mtdm->_del, mtdm->_del * t, | |||
| systemic_latency, systemic_latency/2); | |||
| if (mtdm->_err > 0.2) printf (" ??"); | |||
| if (mtdm->_inv) printf (" Inv"); | |||
| printf ("\n"); | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| // -------------------------------------------------------------------------------- | |||
| @@ -0,0 +1,170 @@ | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <signal.h> | |||
| #include <unistd.h> | |||
| #include <getopt.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/intclient.h> | |||
| jack_client_t *client; | |||
| jack_intclient_t intclient; | |||
| char *client_name; | |||
| char *intclient_name; | |||
| char *load_name; | |||
| char *load_init = NULL; | |||
| char *server_name = NULL; | |||
| int wait_opt = 0; | |||
| void | |||
| signal_handler (int sig) | |||
| { | |||
| jack_status_t status; | |||
| fprintf (stderr, "signal received, unloading..."); | |||
| status = jack_internal_client_unload (client, intclient); | |||
| if (status & JackFailure) | |||
| fprintf (stderr, "(failed), status = 0x%2.0x\n", status); | |||
| else | |||
| fprintf (stderr, "(succeeded)\n"); | |||
| jack_client_close (client); | |||
| exit (0); | |||
| } | |||
| void | |||
| show_usage () | |||
| { | |||
| fprintf (stderr, "usage: %s [ options ] client-name [ load-name " | |||
| "[ init-string]]\n\noptions:\n", client_name); | |||
| fprintf (stderr, | |||
| "\t-h, --help \t\t print help message\n" | |||
| "\t-i, --init string\t initialize string\n" | |||
| "\t-s, --server name\t select JACK server\n" | |||
| "\t-w, --wait \t\t wait for signal, then unload\n" | |||
| "\n" | |||
| ); | |||
| } | |||
| int | |||
| parse_args (int argc, char *argv[]) | |||
| { | |||
| int c; | |||
| int option_index = 0; | |||
| char *short_options = "hi:s:w"; | |||
| struct option long_options[] = { | |||
| { "help", 0, 0, 'h' }, | |||
| { "init", required_argument, 0, 'i' }, | |||
| { "server", required_argument, 0, 's' }, | |||
| { "wait", 0, 0, 'w' }, | |||
| { 0, 0, 0, 0 } | |||
| }; | |||
| client_name = strrchr(argv[0], '/'); | |||
| if (client_name == NULL) { | |||
| client_name = argv[0]; | |||
| } else { | |||
| client_name++; | |||
| } | |||
| while ((c = getopt_long (argc, argv, short_options, long_options, | |||
| &option_index)) >= 0) { | |||
| switch (c) { | |||
| case 'i': | |||
| load_init = optarg; | |||
| break; | |||
| case 's': | |||
| server_name = optarg; | |||
| break; | |||
| case 'w': | |||
| wait_opt = 1; | |||
| break; | |||
| case 'h': | |||
| default: | |||
| show_usage (); | |||
| return 1; | |||
| } | |||
| } | |||
| if (optind == argc) { /* no positional args? */ | |||
| show_usage (); | |||
| return 1; | |||
| } | |||
| if (optind < argc) | |||
| load_name = intclient_name = argv[optind++]; | |||
| if (optind < argc) | |||
| load_name = argv[optind++]; | |||
| if (optind < argc) | |||
| load_init = argv[optind++]; | |||
| //fprintf (stderr, "client-name = `%s', load-name = `%s', " | |||
| // "load-init = `%s', wait = %d\n", | |||
| // intclient_name, load_name, load_init, wait_opt); | |||
| return 0; /* args OK */ | |||
| } | |||
| int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| jack_status_t status; | |||
| /* parse and validate command arguments */ | |||
| if (parse_args (argc, argv)) | |||
| exit (1); /* invalid command line */ | |||
| /* first, become a JACK client */ | |||
| client = jack_client_open (client_name, JackServerName, | |||
| &status, server_name); | |||
| if (client == NULL) { | |||
| fprintf (stderr, "jack_client_open() failed, " | |||
| "status = 0x%2.0x\n", status); | |||
| if (status & JackServerFailed) { | |||
| fprintf (stderr, "Unable to connect to JACK server\n"); | |||
| } | |||
| exit (1); | |||
| } | |||
| if (status & JackServerStarted) { | |||
| fprintf (stderr, "JACK server started\n"); | |||
| } | |||
| if (status & JackNameNotUnique) { | |||
| client_name = jack_get_client_name(client); | |||
| fprintf (stderr, "unique name `%s' assigned\n", client_name); | |||
| } | |||
| /* then, load the internal client */ | |||
| intclient = jack_internal_client_load (client, intclient_name, | |||
| (JackLoadName|JackLoadInit), | |||
| &status, load_name, load_init); | |||
| if (status & JackFailure) { | |||
| fprintf (stderr, "could not load %s, status = 0x%2.0x\n", | |||
| load_name, status); | |||
| return 2; | |||
| } | |||
| if (status & JackNameNotUnique) { | |||
| intclient_name = | |||
| jack_get_internal_client_name (client, intclient); | |||
| fprintf (stderr, "unique internal client name `%s' assigned\n", | |||
| intclient_name); | |||
| } | |||
| fprintf (stdout, "%s is running.\n", load_name); | |||
| if (wait_opt) { | |||
| /* define a signal handler to unload the client, then | |||
| * wait for it to exit */ | |||
| signal (SIGQUIT, signal_handler); | |||
| signal (SIGTERM, signal_handler); | |||
| signal (SIGHUP, signal_handler); | |||
| signal (SIGINT, signal_handler); | |||
| while (1) { | |||
| sleep (1); | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,76 @@ | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #include <stdio.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/intclient.h> | |||
| int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| char *my_name; | |||
| char *client_name; | |||
| jack_client_t *client; | |||
| jack_status_t status; | |||
| jack_intclient_t intclient; | |||
| /* validate args */ | |||
| if ((argc < 2) || (argc > 3)) { | |||
| fprintf (stderr, "usage: %s client-name [ server-name ]]\n", | |||
| argv[0]); | |||
| return 1; | |||
| } | |||
| /* use `basename $0` for my own client name */ | |||
| my_name = strrchr(argv[0], '/'); | |||
| if (my_name == 0) { | |||
| my_name = argv[0]; | |||
| } else { | |||
| my_name++; | |||
| } | |||
| /* first, become a JACK client */ | |||
| if (argc > 2) { | |||
| client = jack_client_open (my_name, | |||
| (JackServerName|JackNoStartServer), | |||
| &status, argv[2]); | |||
| } else { | |||
| client = jack_client_open (my_name, JackNoStartServer, &status); | |||
| } | |||
| if (client == NULL) { | |||
| if (status & JackServerFailed) { | |||
| fprintf (stderr, "JACK server not running.\n"); | |||
| } else { | |||
| fprintf (stderr, "JACK open failed, " | |||
| "status = 0x%2.0x\n", status); | |||
| } | |||
| exit (1); | |||
| } | |||
| /* then, get the internal client handle */ | |||
| client_name = argv[1]; | |||
| intclient = jack_internal_client_handle (client, client_name, &status); | |||
| if (status & JackFailure) { | |||
| fprintf (stderr, "client %s not found.\n", client_name); | |||
| exit (2); | |||
| } | |||
| /* now, unload the internal client */ | |||
| status = jack_internal_client_unload (client, intclient); | |||
| if (status & JackFailure) { | |||
| if (status & JackNoSuchClient) { | |||
| fprintf (stderr, "client %s is gone.\n", | |||
| client_name); | |||
| } else { | |||
| fprintf (stderr, "could not unload %s, " | |||
| "returns 0x%2.0x\n", client_name, status); | |||
| } | |||
| exit (3); | |||
| } else { | |||
| fprintf (stdout, "%s unloaded.\n", client_name); | |||
| } | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,120 @@ | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <unistd.h> | |||
| #include <string.h> | |||
| #include <getopt.h> | |||
| #include <signal.h> | |||
| #include <time.h> | |||
| #include <config.h> | |||
| #include <jack/jack.h> | |||
| char * my_name; | |||
| jack_client_t *client; | |||
| unsigned int wait_timeout = 1000; | |||
| void | |||
| show_version (void) | |||
| { | |||
| fprintf (stderr, "%s: JACK Audio Connection Kit version " VERSION "\n", | |||
| my_name); | |||
| } | |||
| void | |||
| show_usage (void) | |||
| { | |||
| show_version (); | |||
| fprintf (stderr, "\nUsage: %s [options]\n", my_name); | |||
| fprintf (stderr, "this is a test client, which just sleeps in its process_cb to simulate cpu load\n"); | |||
| fprintf (stderr, "options:\n"); | |||
| fprintf (stderr, " -t, --timeout Wait timeout in seconds\n"); | |||
| fprintf (stderr, " -h, --help Display this help message\n"); | |||
| fprintf (stderr, " --version Output version information and exit\n\n"); | |||
| fprintf (stderr, "For more information see http://jackaudio.org/\n"); | |||
| } | |||
| void jack_shutdown(void *arg) | |||
| { | |||
| fprintf(stderr, "JACK shut down, exiting ...\n"); | |||
| exit(1); | |||
| } | |||
| void signal_handler(int sig) | |||
| { | |||
| jack_client_close(client); | |||
| fprintf(stderr, "signal received, exiting ...\n"); | |||
| exit(0); | |||
| } | |||
| int | |||
| process_cb (jack_nframes_t nframes, void *arg) | |||
| { | |||
| jack_time_t now = jack_get_time(); | |||
| jack_time_t wait = now + wait_timeout; | |||
| while (jack_get_time() < wait) ; | |||
| return 0; | |||
| } | |||
| int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| int c; | |||
| int option_index; | |||
| struct option long_options[] = { | |||
| { "timeout", 1, 0, 't' }, | |||
| { "help", 0, 0, 'h' }, | |||
| { "version", 0, 0, 'v' }, | |||
| { 0, 0, 0, 0 } | |||
| }; | |||
| my_name = strrchr(argv[0], '/'); | |||
| if (my_name == 0) { | |||
| my_name = argv[0]; | |||
| } else { | |||
| my_name ++; | |||
| } | |||
| while ((c = getopt_long (argc, argv, "t:hv", long_options, &option_index)) >= 0) { | |||
| switch (c) { | |||
| case 't': | |||
| wait_timeout = atoi(optarg); | |||
| break; | |||
| case 'h': | |||
| show_usage (); | |||
| return 1; | |||
| break; | |||
| case 'v': | |||
| show_version (); | |||
| return 1; | |||
| break; | |||
| default: | |||
| show_usage (); | |||
| return 1; | |||
| break; | |||
| } | |||
| } | |||
| /* try to open server in a loop. breaking under certein conditions */ | |||
| client = jack_client_open( "load_test", JackNullOption, NULL ); | |||
| signal(SIGQUIT, signal_handler); | |||
| signal(SIGTERM, signal_handler); | |||
| signal(SIGHUP, signal_handler); | |||
| signal(SIGINT, signal_handler); | |||
| jack_on_shutdown(client, jack_shutdown, 0); | |||
| jack_set_process_callback( client, process_cb, NULL ); | |||
| jack_activate (client); | |||
| sleep( -1 ); | |||
| exit (0); | |||
| } | |||
| @@ -0,0 +1,229 @@ | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <unistd.h> | |||
| #include <string.h> | |||
| #include <getopt.h> | |||
| #include <config.h> | |||
| #include <jack/jack.h> | |||
| char * my_name; | |||
| void | |||
| show_version (void) | |||
| { | |||
| fprintf (stderr, "%s: JACK Audio Connection Kit version " VERSION "\n", | |||
| my_name); | |||
| } | |||
| void | |||
| show_usage (void) | |||
| { | |||
| show_version (); | |||
| fprintf (stderr, "\nUsage: %s [options] [filter string]\n", my_name); | |||
| fprintf (stderr, "List active Jack ports, and optionally display extra information.\n"); | |||
| fprintf (stderr, "Optionally filter ports which match ALL strings provided after any options.\n\n"); | |||
| fprintf (stderr, "Display options:\n"); | |||
| fprintf (stderr, " -s, --server <name> Connect to the jack server named <name>\n"); | |||
| fprintf (stderr, " -A, --aliases List aliases for each port\n"); | |||
| fprintf (stderr, " -c, --connections List connections to/from each port\n"); | |||
| fprintf (stderr, " -l, --latency Display per-port latency in frames at each port\n"); | |||
| fprintf (stderr, " -L, --latency Display total latency in frames at each port\n"); | |||
| fprintf (stderr, " -p, --properties Display port properties. Output may include:\n" | |||
| " input|output, can-monitor, physical, terminal\n\n"); | |||
| fprintf (stderr, " -t, --type Display port type\n"); | |||
| fprintf (stderr, " -h, --help Display this help message\n"); | |||
| fprintf (stderr, " --version Output version information and exit\n\n"); | |||
| fprintf (stderr, "For more information see http://jackaudio.org/\n"); | |||
| } | |||
| int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| jack_client_t *client; | |||
| jack_status_t status; | |||
| jack_options_t options = JackNoStartServer; | |||
| const char **ports, **connections; | |||
| unsigned int i, j, k; | |||
| int skip_port; | |||
| int show_aliases = 0; | |||
| int show_con = 0; | |||
| int show_port_latency = 0; | |||
| int show_total_latency = 0; | |||
| int show_properties = 0; | |||
| int show_type = 0; | |||
| int c; | |||
| int option_index; | |||
| char* aliases[2]; | |||
| char *server_name = NULL; | |||
| struct option long_options[] = { | |||
| { "server", 1, 0, 's' }, | |||
| { "aliases", 0, 0, 'A' }, | |||
| { "connections", 0, 0, 'c' }, | |||
| { "port-latency", 0, 0, 'l' }, | |||
| { "total-latency", 0, 0, 'L' }, | |||
| { "properties", 0, 0, 'p' }, | |||
| { "type", 0, 0, 't' }, | |||
| { "help", 0, 0, 'h' }, | |||
| { "version", 0, 0, 'v' }, | |||
| { 0, 0, 0, 0 } | |||
| }; | |||
| my_name = strrchr(argv[0], '/'); | |||
| if (my_name == 0) { | |||
| my_name = argv[0]; | |||
| } else { | |||
| my_name ++; | |||
| } | |||
| while ((c = getopt_long (argc, argv, "s:AclLphvt", long_options, &option_index)) >= 0) { | |||
| switch (c) { | |||
| case 's': | |||
| server_name = (char *) malloc (sizeof (char) * strlen(optarg)); | |||
| strcpy (server_name, optarg); | |||
| options |= JackServerName; | |||
| break; | |||
| case 'A': | |||
| aliases[0] = (char *) malloc (jack_port_name_size()); | |||
| aliases[1] = (char *) malloc (jack_port_name_size()); | |||
| show_aliases = 1; | |||
| break; | |||
| case 'c': | |||
| show_con = 1; | |||
| break; | |||
| case 'l': | |||
| show_port_latency = 1; | |||
| break; | |||
| case 'L': | |||
| show_total_latency = 1; | |||
| break; | |||
| case 'p': | |||
| show_properties = 1; | |||
| break; | |||
| case 't': | |||
| show_type = 1; | |||
| break; | |||
| case 'h': | |||
| show_usage (); | |||
| return 1; | |||
| break; | |||
| case 'v': | |||
| show_version (); | |||
| return 1; | |||
| break; | |||
| default: | |||
| show_usage (); | |||
| return 1; | |||
| break; | |||
| } | |||
| } | |||
| /* Open a client connection to the JACK server. Starting a | |||
| * new server only to list its ports seems pointless, so we | |||
| * specify JackNoStartServer. */ | |||
| client = jack_client_open ("lsp", options, &status, server_name); | |||
| if (client == NULL) { | |||
| if (status & JackServerFailed) { | |||
| fprintf (stderr, "JACK server not running\n"); | |||
| } else { | |||
| fprintf (stderr, "jack_client_open() failed, " | |||
| "status = 0x%2.0x\n", status); | |||
| } | |||
| return 1; | |||
| } | |||
| ports = jack_get_ports (client, NULL, NULL, 0); | |||
| for (i = 0; ports && ports[i]; ++i) { | |||
| // skip over any that don't match ALL of the strings presented at command line | |||
| skip_port = 0; | |||
| for(k=optind; k < argc; k++){ | |||
| if(strstr(ports[i], argv[k]) == NULL ){ | |||
| skip_port = 1; | |||
| } | |||
| } | |||
| if(skip_port) continue; | |||
| printf ("%s\n", ports[i]); | |||
| jack_port_t *port = jack_port_by_name (client, ports[i]); | |||
| if (show_aliases) { | |||
| int cnt; | |||
| int i; | |||
| cnt = jack_port_get_aliases (port, aliases); | |||
| for (i = 0; i < cnt; ++i) { | |||
| printf (" %s\n", aliases[i]); | |||
| } | |||
| } | |||
| if (show_con) { | |||
| if ((connections = jack_port_get_all_connections (client, jack_port_by_name(client, ports[i]))) != 0) { | |||
| for (j = 0; connections[j]; j++) { | |||
| printf (" %s\n", connections[j]); | |||
| } | |||
| free (connections); | |||
| } | |||
| } | |||
| if (show_port_latency) { | |||
| if (port) { | |||
| jack_latency_range_t range; | |||
| printf (" port latency = %" PRIu32 " frames\n", | |||
| jack_port_get_latency (port)); | |||
| jack_port_get_latency_range (port, JackPlaybackLatency, &range); | |||
| printf (" port playback latency = [ %" PRIu32 " %" PRIu32 " ] frames\n", | |||
| range.min, range.max); | |||
| jack_port_get_latency_range (port, JackCaptureLatency, &range); | |||
| printf (" port capture latency = [ %" PRIu32 " %" PRIu32 " ] frames\n", | |||
| range.min, range.max); | |||
| } | |||
| } | |||
| if (show_total_latency) { | |||
| if (port) { | |||
| printf (" total latency = %" PRIu32 " frames\n", | |||
| jack_port_get_total_latency (client, port)); | |||
| } | |||
| } | |||
| if (show_properties) { | |||
| if (port) { | |||
| int flags = jack_port_flags (port); | |||
| printf (" properties: "); | |||
| if (flags & JackPortIsInput) { | |||
| fputs ("input,", stdout); | |||
| } | |||
| if (flags & JackPortIsOutput) { | |||
| fputs ("output,", stdout); | |||
| } | |||
| if (flags & JackPortCanMonitor) { | |||
| fputs ("can-monitor,", stdout); | |||
| } | |||
| if (flags & JackPortIsPhysical) { | |||
| fputs ("physical,", stdout); | |||
| } | |||
| if (flags & JackPortIsTerminal) { | |||
| fputs ("terminal,", stdout); | |||
| } | |||
| putc ('\n', stdout); | |||
| } | |||
| } | |||
| if (show_type) { | |||
| if (port) { | |||
| putc ('\t', stdout); | |||
| fputs (jack_port_type (port), stdout); | |||
| putc ('\n', stdout); | |||
| } | |||
| } | |||
| } | |||
| if (ports) | |||
| jack_free (ports); | |||
| jack_client_close (client); | |||
| exit (0); | |||
| } | |||
| @@ -0,0 +1,114 @@ | |||
| #include <stdio.h> | |||
| #include <unistd.h> | |||
| #include <assert.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/midiport.h> | |||
| static jack_port_t* port; | |||
| static void | |||
| describe (jack_midi_event_t* event, char* buffer, size_t buflen) | |||
| { | |||
| assert (buflen > 0); | |||
| buffer[0] = '\0'; | |||
| if (event->size == 0) { | |||
| return; | |||
| } | |||
| int type = event->buffer[0] & 0xf0; | |||
| int channel = event->buffer[0] & 0xf; | |||
| switch (type) { | |||
| case 0x90: | |||
| assert (event->size == 3); | |||
| snprintf (buffer, buflen, "note on (channel %d): pitch %d, velocity %d", channel, event->buffer[1], event->buffer[2]); | |||
| break; | |||
| case 0x80: | |||
| assert (event->size == 3); | |||
| snprintf (buffer, buflen, "note off (channel %d): pitch %d, velocity %d", channel, event->buffer[1], event->buffer[2]); | |||
| break; | |||
| case 0xb0: | |||
| assert (event->size == 3); | |||
| snprintf (buffer, buflen, "control change (channel %d): controller %d, value %d", channel, event->buffer[1], event->buffer[2]); | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| } | |||
| int | |||
| process (jack_nframes_t frames, void* arg) | |||
| { | |||
| void* buffer; | |||
| jack_nframes_t N; | |||
| jack_nframes_t i; | |||
| char description[256]; | |||
| buffer = jack_port_get_buffer (port, frames); | |||
| assert (buffer); | |||
| N = jack_midi_get_event_count (buffer); | |||
| for (i = 0; i < N; ++i) { | |||
| jack_midi_event_t event; | |||
| int r; | |||
| r = jack_midi_event_get (&event, buffer, i); | |||
| if (r == 0) { | |||
| size_t j; | |||
| printf ("%d:", event.time); | |||
| for (j = 0; j < event.size; ++j) { | |||
| printf (" %x", event.buffer[j]); | |||
| } | |||
| describe (&event, description, sizeof (description)); | |||
| printf (" %s", description); | |||
| printf ("\n"); | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| int | |||
| main (int argc, char* argv[]) | |||
| { | |||
| jack_client_t* client; | |||
| char const default_name[] = "midi-monitor"; | |||
| char const * client_name; | |||
| int r; | |||
| if (argc == 2) { | |||
| client_name = argv[1]; | |||
| } else { | |||
| client_name = default_name; | |||
| } | |||
| client = jack_client_open (client_name, JackNullOption, NULL); | |||
| if (client == NULL) { | |||
| fprintf (stderr, "Could not create JACK client.\n"); | |||
| exit (EXIT_FAILURE); | |||
| } | |||
| jack_set_process_callback (client, process, 0); | |||
| port = jack_port_register (client, "input", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); | |||
| if (port == NULL) { | |||
| fprintf (stderr, "Could not register port.\n"); | |||
| exit (EXIT_FAILURE); | |||
| } | |||
| r = jack_activate (client); | |||
| if (r != 0) { | |||
| fprintf (stderr, "Could not activate client.\n"); | |||
| exit (EXIT_FAILURE); | |||
| } | |||
| sleep (-1); | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,46 @@ | |||
| #include <stdio.h> | |||
| #include <unistd.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <jack/jack.h> | |||
| #define TRUE 1 | |||
| #define FALSE 0 | |||
| int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| jack_client_t *client; | |||
| char *my_name = strrchr(argv[0], '/'); | |||
| if (my_name == 0) { | |||
| my_name = argv[0]; | |||
| } else { | |||
| my_name ++; | |||
| } | |||
| if (argc != 2) { | |||
| fprintf (stderr, "Usage: %s client\n", my_name); | |||
| return 1; | |||
| } | |||
| if ((client = jack_client_open ("input monitoring", JackNullOption, NULL)) == 0) { | |||
| fprintf (stderr, "jack server not running?\n"); | |||
| return 1; | |||
| } | |||
| if (jack_port_request_monitor_by_name (client, argv[1], TRUE)) { | |||
| fprintf (stderr, "could not enable monitoring for %s\n", argv[1]); | |||
| jack_client_close (client); | |||
| return 1; | |||
| } | |||
| sleep (30); | |||
| if (jack_port_request_monitor_by_name (client, argv[1], FALSE)) { | |||
| fprintf (stderr, "could not disable monitoring for %s\n", argv[1]); | |||
| } | |||
| jack_client_close (client); | |||
| exit (0); | |||
| } | |||
| @@ -0,0 +1,783 @@ | |||
| /* | |||
| NetJack Client | |||
| Copyright (C) 2008 Marc-Olivier Barre <marco@marcochapeau.org> | |||
| Copyright (C) 2008 Pieter Palmers <pieterpalmers@users.sourceforge.net> | |||
| Copyright (C) 2006 Torben Hohn <torbenh@gmx.de> | |||
| 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. | |||
| */ | |||
| /** @file netsource.c | |||
| * | |||
| * @brief This client connects a remote slave JACK to a local JACK server assumed to be the master | |||
| */ | |||
| #include "config.h" | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <unistd.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <signal.h> | |||
| #ifdef WIN32 | |||
| #include <winsock2.h> | |||
| #include <malloc.h> | |||
| #else | |||
| #include <netinet/in.h> | |||
| #include <netdb.h> | |||
| #include <sys/socket.h> | |||
| #endif | |||
| /* These two required by FreeBSD. */ | |||
| #include <sys/types.h> | |||
| #include <jack/jack.h> | |||
| //#include <net_driver.h> | |||
| #include <netjack_packet.h> | |||
| #if HAVE_SAMPLERATE | |||
| #include <samplerate.h> | |||
| #endif | |||
| #if HAVE_CELT | |||
| #include <celt/celt.h> | |||
| #endif | |||
| #include <math.h> | |||
| JSList *capture_ports = NULL; | |||
| JSList *capture_srcs = NULL; | |||
| int capture_channels = 0; | |||
| int capture_channels_audio = 2; | |||
| int capture_channels_midi = 1; | |||
| JSList *playback_ports = NULL; | |||
| JSList *playback_srcs = NULL; | |||
| int playback_channels = 0; | |||
| int playback_channels_audio = 2; | |||
| int playback_channels_midi = 1; | |||
| int dont_htonl_floats = 0; | |||
| int latency = 5; | |||
| jack_nframes_t factor = 1; | |||
| int bitdepth = 0; | |||
| int mtu = 1400; | |||
| int reply_port = 0; | |||
| int bind_port = 0; | |||
| int redundancy = 1; | |||
| jack_client_t *client; | |||
| packet_cache * packcache = 0; | |||
| int state_connected = 0; | |||
| int state_latency = 0; | |||
| int state_netxruns = 0; | |||
| int state_currentframe = 0; | |||
| int state_recv_packet_queue_time = 0; | |||
| int quit=0; | |||
| int outsockfd; | |||
| int insockfd; | |||
| #ifdef WIN32 | |||
| struct sockaddr_in destaddr; | |||
| struct sockaddr_in bindaddr; | |||
| #else | |||
| struct sockaddr destaddr; | |||
| struct sockaddr bindaddr; | |||
| #endif | |||
| int sync_state; | |||
| jack_transport_state_t last_transport_state; | |||
| int framecnt = 0; | |||
| int cont_miss = 0; | |||
| int freewheeling = 0; | |||
| /** | |||
| * This Function allocates all the I/O Ports which are added the lists. | |||
| */ | |||
| void | |||
| alloc_ports (int n_capture_audio, int n_playback_audio, int n_capture_midi, int n_playback_midi) | |||
| { | |||
| int port_flags = JackPortIsOutput; | |||
| int chn; | |||
| jack_port_t *port; | |||
| char buf[32]; | |||
| capture_ports = NULL; | |||
| /* Allocate audio capture channels */ | |||
| for (chn = 0; chn < n_capture_audio; chn++) | |||
| { | |||
| snprintf (buf, sizeof (buf) - 1, "capture_%u", chn + 1); | |||
| port = jack_port_register (client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0); | |||
| if (!port) | |||
| { | |||
| printf( "jack_netsource: cannot register %s port\n", buf); | |||
| break; | |||
| } | |||
| if( bitdepth == 1000 ) { | |||
| #if HAVE_CELT | |||
| #if HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 | |||
| CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate( client ), jack_get_buffer_size(client), NULL ); | |||
| capture_srcs = jack_slist_append(capture_srcs, celt_decoder_create( celt_mode, 1, NULL ) ); | |||
| #else | |||
| CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate( client ), 1, jack_get_buffer_size(client), NULL ); | |||
| capture_srcs = jack_slist_append(capture_srcs, celt_decoder_create( celt_mode ) ); | |||
| #endif | |||
| #endif | |||
| } else { | |||
| #if HAVE_SAMPLERATE | |||
| capture_srcs = jack_slist_append (capture_srcs, src_new (SRC_LINEAR, 1, NULL)); | |||
| #endif | |||
| } | |||
| capture_ports = jack_slist_append (capture_ports, port); | |||
| } | |||
| /* Allocate midi capture channels */ | |||
| for (chn = n_capture_audio; chn < n_capture_midi + n_capture_audio; chn++) | |||
| { | |||
| snprintf (buf, sizeof (buf) - 1, "capture_%u", chn + 1); | |||
| port = jack_port_register (client, buf, JACK_DEFAULT_MIDI_TYPE, port_flags, 0); | |||
| if (!port) | |||
| { | |||
| printf ("jack_netsource: cannot register %s port\n", buf); | |||
| break; | |||
| } | |||
| capture_ports = jack_slist_append(capture_ports, port); | |||
| } | |||
| /* Allocate audio playback channels */ | |||
| port_flags = JackPortIsInput; | |||
| playback_ports = NULL; | |||
| for (chn = 0; chn < n_playback_audio; chn++) | |||
| { | |||
| snprintf (buf, sizeof (buf) - 1, "playback_%u", chn + 1); | |||
| port = jack_port_register (client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0); | |||
| if (!port) | |||
| { | |||
| printf ("jack_netsource: cannot register %s port\n", buf); | |||
| break; | |||
| } | |||
| if( bitdepth == 1000 ) { | |||
| #if HAVE_CELT | |||
| #if HAVE_CELT_API_0_7 || HAVE_CELT_API_0_8 | |||
| CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate (client), jack_get_buffer_size(client), NULL ); | |||
| playback_srcs = jack_slist_append(playback_srcs, celt_encoder_create( celt_mode, 1, NULL ) ); | |||
| #else | |||
| CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate (client), 1, jack_get_buffer_size(client), NULL ); | |||
| playback_srcs = jack_slist_append(playback_srcs, celt_encoder_create( celt_mode ) ); | |||
| #endif | |||
| #endif | |||
| } else { | |||
| #if HAVE_SAMPLERATE | |||
| playback_srcs = jack_slist_append (playback_srcs, src_new (SRC_LINEAR, 1, NULL)); | |||
| #endif | |||
| } | |||
| playback_ports = jack_slist_append (playback_ports, port); | |||
| } | |||
| /* Allocate midi playback channels */ | |||
| for (chn = n_playback_audio; chn < n_playback_midi + n_playback_audio; chn++) | |||
| { | |||
| snprintf (buf, sizeof (buf) - 1, "playback_%u", chn + 1); | |||
| port = jack_port_register (client, buf, JACK_DEFAULT_MIDI_TYPE, port_flags, 0); | |||
| if (!port) | |||
| { | |||
| printf ("jack_netsource: cannot register %s port\n", buf); | |||
| break; | |||
| } | |||
| playback_ports = jack_slist_append (playback_ports, port); | |||
| } | |||
| } | |||
| /** | |||
| * The Sync callback... sync state is set elsewhere... | |||
| * we will see if this is working correctly. | |||
| * i dont really believe in it yet. | |||
| */ | |||
| int | |||
| sync_cb (jack_transport_state_t state, jack_position_t *pos, void *arg) | |||
| { | |||
| static int latency_count = 0; | |||
| int retval = sync_state; | |||
| if (! state_connected) { | |||
| return 1; | |||
| } | |||
| if (latency_count) { | |||
| latency_count--; | |||
| retval = 0; | |||
| } | |||
| else if (state == JackTransportStarting && last_transport_state != JackTransportStarting) | |||
| { | |||
| retval = 0; | |||
| latency_count = latency - 1; | |||
| } | |||
| last_transport_state = state; | |||
| return retval; | |||
| } | |||
| void | |||
| freewheel_cb (int starting, void *arg) | |||
| { | |||
| freewheeling = starting; | |||
| } | |||
| int deadline_goodness=0; | |||
| /** | |||
| * The process callback for this JACK application. | |||
| * It is called by JACK at the appropriate times. | |||
| */ | |||
| int | |||
| process (jack_nframes_t nframes, void *arg) | |||
| { | |||
| jack_nframes_t net_period; | |||
| int rx_bufsize, tx_bufsize; | |||
| jack_default_audio_sample_t *buf; | |||
| jack_port_t *port; | |||
| JSList *node; | |||
| int chn; | |||
| int size, i; | |||
| const char *porttype; | |||
| int input_fd; | |||
| jack_position_t local_trans_pos; | |||
| uint32_t *packet_buf_tx, *packet_bufX; | |||
| uint32_t *rx_packet_ptr; | |||
| jack_time_t packet_recv_timestamp; | |||
| if( bitdepth == 1000 ) | |||
| net_period = (factor * jack_get_buffer_size(client) * 1024 / jack_get_sample_rate(client) / 8)&(~1) ; | |||
| else | |||
| net_period = (float) nframes / (float) factor; | |||
| rx_bufsize = get_sample_size (bitdepth) * capture_channels * net_period + sizeof (jacknet_packet_header); | |||
| tx_bufsize = get_sample_size (bitdepth) * playback_channels * net_period + sizeof (jacknet_packet_header); | |||
| /* Allocate a buffer where both In and Out Buffer will fit */ | |||
| packet_buf_tx = alloca (tx_bufsize); | |||
| jacknet_packet_header *pkthdr_tx = (jacknet_packet_header *) packet_buf_tx; | |||
| /* | |||
| * for latency==0 we need to send out the packet before we wait on the reply. | |||
| * but this introduces a cycle of latency, when netsource is connected to itself. | |||
| * so we send out before read only in zero latency mode. | |||
| * | |||
| */ | |||
| if( latency == 0 ) { | |||
| /* reset packet_bufX... */ | |||
| packet_bufX = packet_buf_tx + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t); | |||
| /* ---------- Send ---------- */ | |||
| render_jack_ports_to_payload (bitdepth, playback_ports, playback_srcs, nframes, | |||
| packet_bufX, net_period, dont_htonl_floats); | |||
| /* fill in packet hdr */ | |||
| pkthdr_tx->transport_state = jack_transport_query (client, &local_trans_pos); | |||
| pkthdr_tx->transport_frame = local_trans_pos.frame; | |||
| pkthdr_tx->framecnt = framecnt; | |||
| pkthdr_tx->latency = latency; | |||
| pkthdr_tx->reply_port = reply_port; | |||
| pkthdr_tx->sample_rate = jack_get_sample_rate (client); | |||
| pkthdr_tx->period_size = nframes; | |||
| /* playback for us is capture on the other side */ | |||
| pkthdr_tx->capture_channels_audio = playback_channels_audio; | |||
| pkthdr_tx->playback_channels_audio = capture_channels_audio; | |||
| pkthdr_tx->capture_channels_midi = playback_channels_midi; | |||
| pkthdr_tx->playback_channels_midi = capture_channels_midi; | |||
| pkthdr_tx->mtu = mtu; | |||
| if( freewheeling!= 0 ) | |||
| pkthdr_tx->sync_state = (jack_nframes_t)MASTER_FREEWHEELS; | |||
| else | |||
| pkthdr_tx->sync_state = (jack_nframes_t)deadline_goodness; | |||
| //printf("goodness=%d\n", deadline_goodness ); | |||
| packet_header_hton (pkthdr_tx); | |||
| if (cont_miss < 3*latency+5) { | |||
| int r; | |||
| for( r=0; r<redundancy; r++ ) | |||
| netjack_sendto (outsockfd, (char *) packet_buf_tx, tx_bufsize, 0, &destaddr, sizeof (destaddr), mtu); | |||
| } | |||
| else if (cont_miss > 50+5*latency) | |||
| { | |||
| state_connected = 0; | |||
| packet_cache_reset_master_address( packcache ); | |||
| //printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss); | |||
| cont_miss = 0; | |||
| } | |||
| } | |||
| /* | |||
| * ok... now the RECEIVE code. | |||
| * | |||
| */ | |||
| if( reply_port ) | |||
| input_fd = insockfd; | |||
| else | |||
| input_fd = outsockfd; | |||
| // for latency == 0 we can poll. | |||
| if( (latency == 0) || (freewheeling!=0) ) { | |||
| jack_time_t deadline = jack_get_time() + 1000000 * jack_get_buffer_size(client)/jack_get_sample_rate(client); | |||
| // Now loop until we get the right packet. | |||
| while(1) { | |||
| jack_nframes_t got_frame; | |||
| if ( ! netjack_poll_deadline( input_fd, deadline, jack_get_time ) ) | |||
| break; | |||
| packet_cache_drain_socket(packcache, input_fd, jack_get_time); | |||
| if (packet_cache_get_next_available_framecnt( packcache, framecnt - latency, &got_frame )) | |||
| if( got_frame == (framecnt - latency) ) | |||
| break; | |||
| } | |||
| } else { | |||
| // normally: | |||
| // only drain socket. | |||
| packet_cache_drain_socket(packcache, input_fd, jack_get_time); | |||
| } | |||
| size = packet_cache_retreive_packet_pointer( packcache, framecnt - latency, (char**)&rx_packet_ptr, rx_bufsize, &packet_recv_timestamp ); | |||
| /* First alternative : we received what we expected. Render the data | |||
| * to the JACK ports so it can be played. */ | |||
| if (size == rx_bufsize) | |||
| { | |||
| uint32_t *packet_buf_rx = rx_packet_ptr; | |||
| jacknet_packet_header *pkthdr_rx = (jacknet_packet_header *) packet_buf_rx; | |||
| packet_bufX = packet_buf_rx + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t); | |||
| // calculate how much time there would have been, if this packet was sent at the deadline. | |||
| int recv_time_offset = (int) (jack_get_time() - packet_recv_timestamp); | |||
| packet_header_ntoh (pkthdr_rx); | |||
| deadline_goodness = recv_time_offset - (int)pkthdr_rx->latency; | |||
| //printf( "deadline goodness = %d ---> off: %d\n", deadline_goodness, recv_time_offset ); | |||
| if (cont_miss) | |||
| { | |||
| //printf("Frame %d \tRecovered from dropouts\n", framecnt); | |||
| cont_miss = 0; | |||
| } | |||
| render_payload_to_jack_ports (bitdepth, packet_bufX, net_period, | |||
| capture_ports, capture_srcs, nframes, dont_htonl_floats); | |||
| state_currentframe = framecnt; | |||
| state_recv_packet_queue_time = recv_time_offset; | |||
| state_connected = 1; | |||
| sync_state = pkthdr_rx->sync_state; | |||
| packet_cache_release_packet( packcache, framecnt - latency ); | |||
| } | |||
| /* Second alternative : we've received something that's not | |||
| * as big as expected or we missed a packet. We render silence | |||
| * to the ouput ports */ | |||
| else | |||
| { | |||
| jack_nframes_t latency_estimate; | |||
| if( packet_cache_find_latency( packcache, framecnt, &latency_estimate ) ) | |||
| //if( (state_latency == 0) || (latency_estimate < state_latency) ) | |||
| state_latency = latency_estimate; | |||
| // Set the counters up. | |||
| state_currentframe = framecnt; | |||
| //state_latency = framecnt - pkthdr->framecnt; | |||
| state_netxruns += 1; | |||
| //printf ("Frame %d \tPacket missed or incomplete (expected: %d bytes, got: %d bytes)\n", framecnt, rx_bufsize, size); | |||
| //printf ("Frame %d \tPacket missed or incomplete\n", framecnt); | |||
| cont_miss += 1; | |||
| chn = 0; | |||
| node = capture_ports; | |||
| while (node != NULL) | |||
| { | |||
| port = (jack_port_t *) node->data; | |||
| buf = jack_port_get_buffer (port, nframes); | |||
| porttype = jack_port_type (port); | |||
| if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size ()) == 0) | |||
| for (i = 0; i < nframes; i++) | |||
| buf[i] = 0.0; | |||
| else if (strncmp (porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size ()) == 0) | |||
| jack_midi_clear_buffer (buf); | |||
| node = jack_slist_next (node); | |||
| chn++; | |||
| } | |||
| } | |||
| if( latency != 0 ) { | |||
| /* reset packet_bufX... */ | |||
| packet_bufX = packet_buf_tx + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t); | |||
| /* ---------- Send ---------- */ | |||
| render_jack_ports_to_payload (bitdepth, playback_ports, playback_srcs, nframes, | |||
| packet_bufX, net_period, dont_htonl_floats); | |||
| /* fill in packet hdr */ | |||
| pkthdr_tx->transport_state = jack_transport_query (client, &local_trans_pos); | |||
| pkthdr_tx->transport_frame = local_trans_pos.frame; | |||
| pkthdr_tx->framecnt = framecnt; | |||
| pkthdr_tx->latency = latency; | |||
| pkthdr_tx->reply_port = reply_port; | |||
| pkthdr_tx->sample_rate = jack_get_sample_rate (client); | |||
| pkthdr_tx->period_size = nframes; | |||
| /* playback for us is capture on the other side */ | |||
| pkthdr_tx->capture_channels_audio = playback_channels_audio; | |||
| pkthdr_tx->playback_channels_audio = capture_channels_audio; | |||
| pkthdr_tx->capture_channels_midi = playback_channels_midi; | |||
| pkthdr_tx->playback_channels_midi = capture_channels_midi; | |||
| pkthdr_tx->mtu = mtu; | |||
| if( freewheeling!= 0 ) | |||
| pkthdr_tx->sync_state = (jack_nframes_t)MASTER_FREEWHEELS; | |||
| else | |||
| pkthdr_tx->sync_state = (jack_nframes_t)deadline_goodness; | |||
| //printf("goodness=%d\n", deadline_goodness ); | |||
| packet_header_hton (pkthdr_tx); | |||
| if (cont_miss < 3*latency+5) { | |||
| int r; | |||
| for( r=0; r<redundancy; r++ ) | |||
| netjack_sendto (outsockfd, (char *) packet_buf_tx, tx_bufsize, 0, &destaddr, sizeof (destaddr), mtu); | |||
| } | |||
| else if (cont_miss > 50+5*latency) | |||
| { | |||
| state_connected = 0; | |||
| packet_cache_reset_master_address( packcache ); | |||
| //printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss); | |||
| cont_miss = 0; | |||
| } | |||
| } | |||
| framecnt++; | |||
| return 0; | |||
| } | |||
| /** | |||
| * This is the shutdown callback for this JACK application. | |||
| * It is called by JACK if the server ever shuts down or | |||
| * decides to disconnect the client. | |||
| */ | |||
| void | |||
| jack_shutdown (void *arg) | |||
| { | |||
| exit (1); | |||
| } | |||
| void | |||
| init_sockaddr_in (struct sockaddr_in *name , const char *hostname , uint16_t port) | |||
| { | |||
| name->sin_family = AF_INET ; | |||
| name->sin_port = htons (port); | |||
| if (hostname) | |||
| { | |||
| struct hostent *hostinfo = gethostbyname (hostname); | |||
| if (hostinfo == NULL) { | |||
| fprintf (stderr, "init_sockaddr_in: unknown host: %s.\n", hostname); | |||
| fflush( stderr ); | |||
| } | |||
| #ifdef WIN32 | |||
| name->sin_addr.s_addr = inet_addr( hostname ); | |||
| #else | |||
| name->sin_addr = *(struct in_addr *) hostinfo->h_addr ; | |||
| #endif | |||
| } | |||
| else | |||
| name->sin_addr.s_addr = htonl (INADDR_ANY) ; | |||
| } | |||
| void | |||
| printUsage () | |||
| { | |||
| fprintf (stderr, "usage: jack_netsource [options]\n" | |||
| "\n" | |||
| " -h this help text\n" | |||
| " -H <slave host> - Host name of the slave JACK\n" | |||
| " -o <num channels> - Number of audio playback channels\n" | |||
| " -i <num channels> - Number of audio capture channels\n" | |||
| " -O <num channels> - Number of midi playback channels\n" | |||
| " -I <num channels> - Number of midi capture channels\n" | |||
| " -n <periods> - Network latency in JACK periods\n" | |||
| " -p <port> - UDP port that the slave is listening on\n" | |||
| " -r <reply port> - UDP port that we are listening on\n" | |||
| " -B <bind port> - reply port, for use in NAT environments\n" | |||
| " -b <bitdepth> - Set transport to use 16bit or 8bit\n" | |||
| " -c <kbits> - Use CELT encoding with <kbits> kbits per channel\n" | |||
| " -m <mtu> - Assume this mtu for the link\n" | |||
| " -R <N> - Redundancy: send out packets N times.\n" | |||
| " -e - skip host-to-network endianness conversion\n" | |||
| " -N <jack name> - Reports a different name to jack\n" | |||
| " -s <server name> - The name of the local jack server\n" | |||
| "\n"); | |||
| } | |||
| void | |||
| sigterm_handler( int signal ) | |||
| { | |||
| quit = 1; | |||
| } | |||
| int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| /* Some startup related basics */ | |||
| char *client_name, *server_name = NULL, *peer_ip; | |||
| int peer_port = 3000; | |||
| jack_options_t options = JackNullOption; | |||
| jack_status_t status; | |||
| #ifdef WIN32 | |||
| WSADATA wsa; | |||
| int rc = WSAStartup(MAKEWORD(2,0),&wsa); | |||
| #endif | |||
| /* Torben's famous state variables, aka "the reporting API" ! */ | |||
| /* heh ? these are only the copies of them ;) */ | |||
| int statecopy_connected, statecopy_latency, statecopy_netxruns; | |||
| jack_nframes_t net_period; | |||
| /* Argument parsing stuff */ | |||
| extern char *optarg; | |||
| extern int optind, optopt; | |||
| int errflg=0, c; | |||
| if (argc < 3) | |||
| { | |||
| printUsage (); | |||
| return 1; | |||
| } | |||
| client_name = (char *) malloc (sizeof (char) * 10); | |||
| peer_ip = (char *) malloc (sizeof (char) * 10); | |||
| sprintf(client_name, "netjack"); | |||
| sprintf(peer_ip, "localhost"); | |||
| while ((c = getopt (argc, argv, ":h:H:o:i:O:I:n:p:r:B:b:c:m:R:e:N:s:")) != -1) | |||
| { | |||
| switch (c) | |||
| { | |||
| case 'h': | |||
| printUsage(); | |||
| exit (0); | |||
| break; | |||
| case 'H': | |||
| free(peer_ip); | |||
| peer_ip = (char *) malloc (sizeof (char) * strlen (optarg)+1); | |||
| strcpy (peer_ip, optarg); | |||
| break; | |||
| case 'o': | |||
| playback_channels_audio = atoi (optarg); | |||
| break; | |||
| case 'i': | |||
| capture_channels_audio = atoi (optarg); | |||
| break; | |||
| case 'O': | |||
| playback_channels_midi = atoi (optarg); | |||
| break; | |||
| case 'I': | |||
| capture_channels_midi = atoi (optarg); | |||
| break; | |||
| case 'n': | |||
| latency = atoi (optarg); | |||
| break; | |||
| case 'p': | |||
| peer_port = atoi (optarg); | |||
| break; | |||
| case 'r': | |||
| reply_port = atoi (optarg); | |||
| break; | |||
| case 'B': | |||
| bind_port = atoi (optarg); | |||
| break; | |||
| case 'f': | |||
| factor = atoi (optarg); | |||
| printf("This feature is deprecated and will be removed in future netjack versions. CELT offers a superiour way to conserve bandwidth"); | |||
| break; | |||
| case 'b': | |||
| bitdepth = atoi (optarg); | |||
| break; | |||
| case 'c': | |||
| #if HAVE_CELT | |||
| bitdepth = 1000; | |||
| factor = atoi (optarg); | |||
| #else | |||
| printf( "not built with celt supprt\n" ); | |||
| exit(10); | |||
| #endif | |||
| break; | |||
| case 'm': | |||
| mtu = atoi (optarg); | |||
| break; | |||
| case 'R': | |||
| redundancy = atoi (optarg); | |||
| break; | |||
| case 'e': | |||
| dont_htonl_floats = 1; | |||
| break; | |||
| case 'N': | |||
| free(client_name); | |||
| client_name = (char *) malloc (sizeof (char) * strlen (optarg)+1); | |||
| strcpy (client_name, optarg); | |||
| break; | |||
| case 's': | |||
| server_name = (char *) malloc (sizeof (char) * strlen (optarg)+1); | |||
| strcpy (server_name, optarg); | |||
| options |= JackServerName; | |||
| break; | |||
| case ':': | |||
| fprintf (stderr, "Option -%c requires an operand\n", optopt); | |||
| errflg++; | |||
| break; | |||
| case '?': | |||
| fprintf (stderr, "Unrecognized option: -%c\n", optopt); | |||
| errflg++; | |||
| } | |||
| } | |||
| if (errflg) | |||
| { | |||
| printUsage (); | |||
| exit (2); | |||
| } | |||
| capture_channels = capture_channels_audio + capture_channels_midi; | |||
| playback_channels = playback_channels_audio + playback_channels_midi; | |||
| outsockfd = socket (AF_INET, SOCK_DGRAM, 0); | |||
| insockfd = socket (AF_INET, SOCK_DGRAM, 0); | |||
| if( (outsockfd == -1) || (insockfd == -1) ) { | |||
| fprintf (stderr, "cant open sockets\n" ); | |||
| return 1; | |||
| } | |||
| init_sockaddr_in ((struct sockaddr_in *) &destaddr, peer_ip, peer_port); | |||
| if(bind_port) { | |||
| init_sockaddr_in ((struct sockaddr_in *) &bindaddr, NULL, bind_port); | |||
| if( bind (outsockfd, &bindaddr, sizeof (bindaddr)) ) { | |||
| fprintf (stderr, "bind failure\n" ); | |||
| } | |||
| } | |||
| if(reply_port) | |||
| { | |||
| init_sockaddr_in ((struct sockaddr_in *) &bindaddr, NULL, reply_port); | |||
| if( bind (insockfd, &bindaddr, sizeof (bindaddr)) ) { | |||
| fprintf (stderr, "bind failure\n" ); | |||
| } | |||
| } | |||
| /* try to become a client of the JACK server */ | |||
| client = jack_client_open (client_name, options, &status, server_name); | |||
| if (client == NULL) | |||
| { | |||
| fprintf (stderr, "jack_client_open() failed, status = 0x%2.0x\n" | |||
| "Is the JACK server running ?\n", status); | |||
| return 1; | |||
| } | |||
| /* Set up jack callbacks */ | |||
| jack_set_process_callback (client, process, 0); | |||
| jack_set_sync_callback (client, sync_cb, 0); | |||
| jack_set_freewheel_callback (client, freewheel_cb, 0); | |||
| jack_on_shutdown (client, jack_shutdown, 0); | |||
| alloc_ports (capture_channels_audio, playback_channels_audio, capture_channels_midi, playback_channels_midi); | |||
| if( bitdepth == 1000 ) | |||
| net_period = (factor * jack_get_buffer_size(client) * 1024 / jack_get_sample_rate(client) / 8)&(~1) ; | |||
| else | |||
| net_period = ceilf((float) jack_get_buffer_size (client) / (float) factor); | |||
| int rx_bufsize = get_sample_size (bitdepth) * capture_channels * net_period + sizeof (jacknet_packet_header); | |||
| packcache = packet_cache_new (latency + 50, rx_bufsize, mtu); | |||
| /* tell the JACK server that we are ready to roll */ | |||
| if (jack_activate (client)) | |||
| { | |||
| fprintf (stderr, "Cannot activate client"); | |||
| return 1; | |||
| } | |||
| /* Now sleep forever... and evaluate the state_ vars */ | |||
| signal( SIGTERM, sigterm_handler ); | |||
| signal( SIGINT, sigterm_handler ); | |||
| statecopy_connected = 2; // make it report unconnected on start. | |||
| statecopy_latency = state_latency; | |||
| statecopy_netxruns = state_netxruns; | |||
| while ( !quit ) | |||
| { | |||
| #ifdef WIN32 | |||
| Sleep (1000); | |||
| #else | |||
| sleep(1); | |||
| #endif | |||
| if (statecopy_connected != state_connected) | |||
| { | |||
| statecopy_connected = state_connected; | |||
| if (statecopy_connected) | |||
| { | |||
| state_netxruns = 1; // We want to reset the netxrun count on each new connection | |||
| printf ("Connected :-)\n"); | |||
| } | |||
| else | |||
| printf ("Not Connected\n"); | |||
| fflush(stdout); | |||
| } | |||
| if (statecopy_connected) | |||
| { | |||
| if (statecopy_netxruns != state_netxruns) { | |||
| statecopy_netxruns = state_netxruns; | |||
| printf ("%s: at frame %06d -> total netxruns %d (%d%%) queue time= %d\n", | |||
| client_name, | |||
| state_currentframe, | |||
| statecopy_netxruns, | |||
| 100*statecopy_netxruns/state_currentframe, | |||
| state_recv_packet_queue_time); | |||
| fflush(stdout); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| if (statecopy_latency != state_latency) | |||
| { | |||
| statecopy_latency = state_latency; | |||
| if (statecopy_latency > 1) | |||
| printf ("current latency %d\n", statecopy_latency); | |||
| fflush(stdout); | |||
| } | |||
| } | |||
| } | |||
| jack_client_close (client); | |||
| packet_cache_free (packcache); | |||
| exit (0); | |||
| } | |||
| @@ -0,0 +1,85 @@ | |||
| /* | |||
| * smaplerate.c -- get current samplerate | |||
| * | |||
| * 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. | |||
| */ | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <unistd.h> | |||
| #include <signal.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/transport.h> | |||
| char *package; /* program name */ | |||
| jack_client_t *client; | |||
| void jack_shutdown(void *arg) | |||
| { | |||
| fprintf(stderr, "JACK shut down, exiting ...\n"); | |||
| exit(1); | |||
| } | |||
| void signal_handler(int sig) | |||
| { | |||
| jack_client_close(client); | |||
| fprintf(stderr, "signal received, exiting ...\n"); | |||
| exit(0); | |||
| } | |||
| void parse_arguments(int argc, char *argv[]) | |||
| { | |||
| /* basename $0 */ | |||
| package = strrchr(argv[0], '/'); | |||
| if (package == 0) | |||
| package = argv[0]; | |||
| else | |||
| package++; | |||
| if (argc==1) { | |||
| return; | |||
| } | |||
| fprintf(stderr, "usage: %s\n", package); | |||
| exit(9); | |||
| } | |||
| int main(int argc, char *argv[]) | |||
| { | |||
| parse_arguments(argc, argv); | |||
| /* become a JACK client */ | |||
| if ((client = jack_client_open(package, JackNullOption, NULL)) == 0) { | |||
| fprintf(stderr, "JACK server not running?\n"); | |||
| exit(1); | |||
| } | |||
| signal(SIGQUIT, signal_handler); | |||
| signal(SIGTERM, signal_handler); | |||
| signal(SIGHUP, signal_handler); | |||
| signal(SIGINT, signal_handler); | |||
| jack_on_shutdown(client, jack_shutdown, 0); | |||
| fprintf(stdout, "%d\n", jack_get_sample_rate( client ) ); | |||
| jack_client_close(client); | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,181 @@ | |||
| /* | |||
| * session_notify.c -- ultra minimal session manager | |||
| * | |||
| * Copyright (C) 2010 Torben Hohn. | |||
| * | |||
| * 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. | |||
| */ | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <unistd.h> | |||
| #include <signal.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/jslist.h> | |||
| #include <jack/transport.h> | |||
| #include <jack/session.h> | |||
| char *package; /* program name */ | |||
| jack_client_t *client; | |||
| jack_session_event_type_t notify_type; | |||
| char *save_path = NULL; | |||
| void jack_shutdown(void *arg) | |||
| { | |||
| fprintf(stderr, "JACK shut down, exiting ...\n"); | |||
| exit(1); | |||
| } | |||
| void signal_handler(int sig) | |||
| { | |||
| jack_client_close(client); | |||
| fprintf(stderr, "signal received, exiting ...\n"); | |||
| exit(0); | |||
| } | |||
| void parse_arguments(int argc, char *argv[]) | |||
| { | |||
| /* basename $0 */ | |||
| package = strrchr(argv[0], '/'); | |||
| if (package == 0) | |||
| package = argv[0]; | |||
| else | |||
| package++; | |||
| if (argc==2) { | |||
| if( !strcmp( argv[1], "quit" ) ) { | |||
| notify_type = JackSessionSaveAndQuit; | |||
| return; | |||
| } | |||
| } | |||
| if (argc==3) { | |||
| if( !strcmp( argv[1], "save" ) ) { | |||
| notify_type = JackSessionSave; | |||
| save_path = argv[2]; | |||
| return; | |||
| } | |||
| } | |||
| fprintf(stderr, "usage: %s quit|save [path]\n", package); | |||
| exit(9); | |||
| } | |||
| typedef struct { | |||
| char name[32]; | |||
| char uuid[16]; | |||
| } uuid_map_t; | |||
| JSList *uuid_map = NULL; | |||
| void add_uuid_mapping( const char *uuid ) { | |||
| char *clientname = jack_get_client_name_by_uuid( client, uuid ); | |||
| if( !clientname ) { | |||
| printf( "error... cant find client for uuid" ); | |||
| return; | |||
| } | |||
| uuid_map_t *mapping = malloc( sizeof(uuid_map_t) ); | |||
| snprintf( mapping->uuid, sizeof(mapping->uuid), "%s", uuid ); | |||
| snprintf( mapping->name, sizeof(mapping->name), "%s", clientname ); | |||
| uuid_map = jack_slist_append( uuid_map, mapping ); | |||
| } | |||
| char *map_port_name_to_uuid_port( const char *port_name ) | |||
| { | |||
| JSList *node; | |||
| char retval[300]; | |||
| char *port_component = strchr( port_name,':' ); | |||
| char *client_component = strdup( port_name ); | |||
| strchr( client_component, ':' )[0] = '\0'; | |||
| sprintf( retval, "%s", port_name ); | |||
| for( node=uuid_map; node; node=jack_slist_next(node) ) { | |||
| uuid_map_t *mapping = node->data; | |||
| if( !strcmp( mapping->name, client_component ) ) { | |||
| sprintf( retval, "%s%s", mapping->uuid, port_component ); | |||
| break; | |||
| } | |||
| } | |||
| return strdup(retval); | |||
| } | |||
| int main(int argc, char *argv[]) | |||
| { | |||
| parse_arguments(argc, argv); | |||
| jack_session_command_t *retval; | |||
| int k,i,j; | |||
| /* become a JACK client */ | |||
| if ((client = jack_client_open(package, JackNullOption, NULL)) == 0) { | |||
| fprintf(stderr, "JACK server not running?\n"); | |||
| exit(1); | |||
| } | |||
| signal(SIGQUIT, signal_handler); | |||
| signal(SIGTERM, signal_handler); | |||
| signal(SIGHUP, signal_handler); | |||
| signal(SIGINT, signal_handler); | |||
| jack_on_shutdown(client, jack_shutdown, 0); | |||
| jack_activate(client); | |||
| retval = jack_session_notify( client, NULL, notify_type, save_path ); | |||
| for(i=0; retval[i].uuid; i++ ) { | |||
| printf( "export SESSION_DIR=\"%s%s/\"\n", save_path, retval[i].client_name ); | |||
| printf( "%s &\n", retval[i].command ); | |||
| add_uuid_mapping(retval[i].uuid); | |||
| } | |||
| printf( "sleep 10\n" ); | |||
| for(k=0; retval[k].uuid; k++ ) { | |||
| char* port_regexp = alloca( jack_client_name_size()+3 ); | |||
| char* client_name = jack_get_client_name_by_uuid( client, retval[k].uuid ); | |||
| snprintf( port_regexp, jack_client_name_size()+3, "%s:.*", client_name ); | |||
| jack_free(client_name); | |||
| const char **ports = jack_get_ports( client, port_regexp, NULL, 0 ); | |||
| if( !ports ) { | |||
| continue; | |||
| } | |||
| for (i = 0; ports[i]; ++i) { | |||
| const char **connections; | |||
| if ((connections = jack_port_get_all_connections (client, jack_port_by_name(client, ports[i]))) != 0) { | |||
| for (j = 0; connections[j]; j++) { | |||
| char *src = map_port_name_to_uuid_port( ports[i] ); | |||
| char *dst = map_port_name_to_uuid_port( connections[j] ); | |||
| printf( "jack_connect -u \"%s\" \"%s\"\n", src, dst ); | |||
| } | |||
| jack_free (connections); | |||
| } | |||
| } | |||
| jack_free(ports); | |||
| } | |||
| jack_session_commands_free(retval); | |||
| jack_client_close(client); | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,481 @@ | |||
| /* | |||
| * transport.c -- JACK transport master example client. | |||
| * | |||
| * 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. | |||
| */ | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <unistd.h> | |||
| #include <signal.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <readline/readline.h> | |||
| #include <readline/history.h> | |||
| #include <jack/jack.h> | |||
| #include <jack/transport.h> | |||
| char *package; /* program name */ | |||
| int done = 0; | |||
| jack_client_t *client; | |||
| /* Time and tempo variables. These are global to the entire, | |||
| * transport timeline. There is no attempt to keep a true tempo map. | |||
| * The default time signature is: "march time", 4/4, 120bpm | |||
| */ | |||
| float time_beats_per_bar = 4.0; | |||
| float time_beat_type = 4.0; | |||
| double time_ticks_per_beat = 1920.0; | |||
| double time_beats_per_minute = 120.0; | |||
| volatile int time_reset = 1; /* true when time values change */ | |||
| volatile int avr_set = 0; | |||
| float audio_frames_per_video_frame; | |||
| /* JACK timebase callback. | |||
| * | |||
| * Runs in the process thread. Realtime, must not wait. | |||
| */ | |||
| void timebase(jack_transport_state_t state, jack_nframes_t nframes, | |||
| jack_position_t *pos, int new_pos, void *arg) | |||
| { | |||
| double min; /* minutes since frame 0 */ | |||
| long abs_tick; /* ticks since frame 0 */ | |||
| long abs_beat; /* beats since frame 0 */ | |||
| if (new_pos || time_reset) { | |||
| pos->valid = JackPositionBBT; | |||
| pos->beats_per_bar = time_beats_per_bar; | |||
| pos->beat_type = time_beat_type; | |||
| pos->ticks_per_beat = time_ticks_per_beat; | |||
| pos->beats_per_minute = time_beats_per_minute; | |||
| time_reset = 0; /* time change complete */ | |||
| /* Compute BBT info from frame number. This is relatively | |||
| * simple here, but would become complex if we supported tempo | |||
| * or time signature changes at specific locations in the | |||
| * transport timeline. | |||
| */ | |||
| min = pos->frame / ((double) pos->frame_rate * 60.0); | |||
| abs_tick = min * pos->beats_per_minute * pos->ticks_per_beat; | |||
| abs_beat = abs_tick / pos->ticks_per_beat; | |||
| pos->bar = abs_beat / pos->beats_per_bar; | |||
| pos->beat = abs_beat - (pos->bar * pos->beats_per_bar) + 1; | |||
| pos->tick = abs_tick - (abs_beat * pos->ticks_per_beat); | |||
| pos->bar_start_tick = pos->bar * pos->beats_per_bar * | |||
| pos->ticks_per_beat; | |||
| pos->bar++; /* adjust start to bar 1 */ | |||
| #if 0 | |||
| /* some debug code... */ | |||
| fprintf(stderr, "\nnew position: %" PRIu32 "\tBBT: %3" | |||
| PRIi32 "|%" PRIi32 "|%04" PRIi32 "\n", | |||
| pos->frame, pos->bar, pos->beat, pos->tick); | |||
| #endif | |||
| } else { | |||
| /* Compute BBT info based on previous period. */ | |||
| pos->tick += | |||
| nframes * pos->ticks_per_beat * pos->beats_per_minute | |||
| / (pos->frame_rate * 60); | |||
| while (pos->tick >= pos->ticks_per_beat) { | |||
| pos->tick -= pos->ticks_per_beat; | |||
| if (++pos->beat > pos->beats_per_bar) { | |||
| pos->beat = 1; | |||
| ++pos->bar; | |||
| pos->bar_start_tick += | |||
| pos->beats_per_bar | |||
| * pos->ticks_per_beat; | |||
| } | |||
| } | |||
| } | |||
| if (avr_set) { | |||
| pos->valid |= JackAudioVideoRatio; | |||
| pos->audio_frames_per_video_frame = audio_frames_per_video_frame; | |||
| } | |||
| } | |||
| void jack_shutdown(void *arg) | |||
| { | |||
| #if defined(RL_READLINE_VERSION) && RL_READLINE_VERSION >= 0x0400 | |||
| rl_cleanup_after_signal(); | |||
| #endif | |||
| fprintf(stderr, "JACK shut down, exiting ...\n"); | |||
| exit(1); | |||
| } | |||
| void signal_handler(int sig) | |||
| { | |||
| jack_client_close(client); | |||
| fprintf(stderr, "signal received, exiting ...\n"); | |||
| exit(0); | |||
| } | |||
| /* Command functions: see commands[] table following. */ | |||
| void com_activate(char *arg) | |||
| { | |||
| if (jack_activate(client)) { | |||
| fprintf(stderr, "cannot activate client"); | |||
| } | |||
| } | |||
| void com_deactivate(char *arg) | |||
| { | |||
| if (jack_deactivate(client)) { | |||
| fprintf(stderr, "cannot deactivate client"); | |||
| } | |||
| } | |||
| void com_exit(char *arg) | |||
| { | |||
| done = 1; | |||
| } | |||
| void com_help(char *); /* forward declaration */ | |||
| void com_locate(char *arg) | |||
| { | |||
| jack_nframes_t frame = 0; | |||
| if (*arg != '\0') | |||
| frame = atoi(arg); | |||
| jack_transport_locate(client, frame); | |||
| } | |||
| void com_master(char *arg) | |||
| { | |||
| int cond = (*arg != '\0'); | |||
| if (jack_set_timebase_callback(client, cond, timebase, NULL) != 0) | |||
| fprintf(stderr, "Unable to take over timebase.\n"); | |||
| } | |||
| void com_play(char *arg) | |||
| { | |||
| jack_transport_start(client); | |||
| } | |||
| void com_release(char *arg) | |||
| { | |||
| jack_release_timebase(client); | |||
| } | |||
| void com_stop(char *arg) | |||
| { | |||
| jack_transport_stop(client); | |||
| } | |||
| /* Change the tempo for the entire timeline, not just from the current | |||
| * location. */ | |||
| void com_tempo(char *arg) | |||
| { | |||
| float tempo = 120.0; | |||
| if (*arg != '\0') | |||
| tempo = atof(arg); | |||
| time_beats_per_minute = tempo; | |||
| time_reset = 1; | |||
| } | |||
| /* Set sync timeout in seconds. */ | |||
| void com_timeout(char *arg) | |||
| { | |||
| double timeout = 2.0; | |||
| if (*arg != '\0') | |||
| timeout = atof(arg); | |||
| jack_set_sync_timeout(client, (jack_time_t) (timeout*1000000)); | |||
| } | |||
| /* Change the tempo for the entire timeline, not just from the current | |||
| * location. */ | |||
| void com_av_ratio(char *arg) | |||
| { | |||
| float avr = 0; | |||
| if (*arg != '\0') | |||
| avr = atof(arg); | |||
| audio_frames_per_video_frame = avr; | |||
| avr_set = 1; | |||
| } | |||
| /* Command parsing based on GNU readline info examples. */ | |||
| typedef void cmd_function_t(char *); /* command function type */ | |||
| /* Transport command table. */ | |||
| typedef struct { | |||
| char *name; /* user printable name */ | |||
| cmd_function_t *func; /* function to call */ | |||
| char *doc; /* documentation */ | |||
| } command_t; | |||
| /* command table must be in alphabetical order */ | |||
| command_t commands[] = { | |||
| {"activate", com_activate, "Call jack_activate()"}, | |||
| {"avr", com_av_ratio, "Set audio/video frame ratio <audio frames per video frame>"}, | |||
| {"exit", com_exit, "Exit transport program"}, | |||
| {"deactivate", com_deactivate, "Call jack_deactivate()"}, | |||
| {"help", com_help, "Display help text [<command>]"}, | |||
| {"locate", com_locate, "Locate to frame <position>"}, | |||
| {"master", com_master, "Become timebase master " | |||
| "[<conditionally>]"}, | |||
| {"play", com_play, "Start transport rolling"}, | |||
| {"quit", com_exit, "Synonym for `exit'"}, | |||
| {"release", com_release, "Release timebase"}, | |||
| {"stop", com_stop, "Stop transport"}, | |||
| {"tempo", com_tempo, "Set beat tempo <beats_per_min>"}, | |||
| {"timeout", com_timeout, "Set sync timeout in <seconds>"}, | |||
| {"?", com_help, "Synonym for `help'" }, | |||
| {(char *)NULL, (cmd_function_t *)NULL, (char *)NULL } | |||
| }; | |||
| command_t *find_command(char *name) | |||
| { | |||
| register int i; | |||
| size_t namelen; | |||
| if ((name == NULL) || (*name == '\0')) | |||
| return ((command_t *)NULL); | |||
| namelen = strlen(name); | |||
| for (i = 0; commands[i].name; i++) | |||
| if (strncmp(name, commands[i].name, namelen) == 0) { | |||
| /* make sure the match is unique */ | |||
| if ((commands[i+1].name) && | |||
| (strncmp(name, commands[i+1].name, namelen) == 0)) | |||
| return ((command_t *)NULL); | |||
| else | |||
| return (&commands[i]); | |||
| } | |||
| return ((command_t *)NULL); | |||
| } | |||
| void com_help(char *arg) | |||
| { | |||
| register int i; | |||
| command_t *cmd; | |||
| if (!*arg) { | |||
| /* print help for all commands */ | |||
| for (i = 0; commands[i].name; i++) { | |||
| printf("%s\t\t%s.\n", commands[i].name, | |||
| commands[i].doc); | |||
| } | |||
| } else if ((cmd = find_command(arg))) { | |||
| printf("%s\t\t%s.\n", cmd->name, cmd->doc); | |||
| } else { | |||
| int printed = 0; | |||
| printf("No `%s' command. Valid command names are:\n", arg); | |||
| for (i = 0; commands[i].name; i++) { | |||
| /* Print in six columns. */ | |||
| if (printed == 6) { | |||
| printed = 0; | |||
| printf ("\n"); | |||
| } | |||
| printf ("%s\t", commands[i].name); | |||
| printed++; | |||
| } | |||
| printf("\n\nTry `help [command]\' for more information.\n"); | |||
| } | |||
| } | |||
| void execute_command(char *line) | |||
| { | |||
| register int i; | |||
| command_t *command; | |||
| char *word; | |||
| /* Isolate the command word. */ | |||
| i = 0; | |||
| while (line[i] && whitespace(line[i])) | |||
| i++; | |||
| word = line + i; | |||
| while (line[i] && !whitespace(line[i])) | |||
| i++; | |||
| if (line[i]) | |||
| line[i++] = '\0'; | |||
| command = find_command(word); | |||
| if (!command) { | |||
| fprintf(stderr, "%s: No such command. There is `help\'.\n", | |||
| word); | |||
| return; | |||
| } | |||
| /* Get argument to command, if any. */ | |||
| while (whitespace(line[i])) | |||
| i++; | |||
| word = line + i; | |||
| /* invoke the command function. */ | |||
| (*command->func)(word); | |||
| } | |||
| /* Strip whitespace from the start and end of string. */ | |||
| char *stripwhite(char *string) | |||
| { | |||
| register char *s, *t; | |||
| s = string; | |||
| while (whitespace(*s)) | |||
| s++; | |||
| if (*s == '\0') | |||
| return s; | |||
| t = s + strlen (s) - 1; | |||
| while (t > s && whitespace(*t)) | |||
| t--; | |||
| *++t = '\0'; | |||
| return s; | |||
| } | |||
| char *dupstr(char *s) | |||
| { | |||
| char *r = malloc(strlen(s) + 1); | |||
| strcpy(r, s); | |||
| return r; | |||
| } | |||
| /* Readline generator function for command completion. */ | |||
| char *command_generator (const char *text, int state) | |||
| { | |||
| static int list_index, len; | |||
| char *name; | |||
| /* If this is a new word to complete, initialize now. This | |||
| includes saving the length of TEXT for efficiency, and | |||
| initializing the index variable to 0. */ | |||
| if (!state) { | |||
| list_index = 0; | |||
| len = strlen (text); | |||
| } | |||
| /* Return the next name which partially matches from the | |||
| command list. */ | |||
| while ((name = commands[list_index].name)) { | |||
| list_index++; | |||
| if (strncmp(name, text, len) == 0) | |||
| return dupstr(name); | |||
| } | |||
| return (char *) NULL; /* No names matched. */ | |||
| } | |||
| void command_loop() | |||
| { | |||
| char *line, *cmd; | |||
| char prompt[32]; | |||
| snprintf(prompt, sizeof(prompt), "%s> ", package); | |||
| /* Allow conditional parsing of the ~/.inputrc file. */ | |||
| rl_readline_name = package; | |||
| /* Define a custom completion function. */ | |||
| rl_completion_entry_function = command_generator; | |||
| /* Read and execute commands until the user quits. */ | |||
| while (!done) { | |||
| line = readline(prompt); | |||
| if (line == NULL) { /* EOF? */ | |||
| printf("\n"); /* close out prompt */ | |||
| done = 1; | |||
| break; | |||
| } | |||
| /* Remove leading and trailing whitespace from the line. */ | |||
| cmd = stripwhite(line); | |||
| /* If anything left, add to history and execute it. */ | |||
| if (*cmd) | |||
| { | |||
| add_history(cmd); | |||
| execute_command(cmd); | |||
| } | |||
| free(line); /* realine() called malloc() */ | |||
| } | |||
| } | |||
| int main(int argc, char *argv[]) | |||
| { | |||
| jack_status_t status; | |||
| /* basename $0 */ | |||
| package = strrchr(argv[0], '/'); | |||
| if (package == 0) | |||
| package = argv[0]; | |||
| else | |||
| package++; | |||
| /* open a connection to the JACK server */ | |||
| client = jack_client_open (package, JackNullOption, &status); | |||
| if (client == NULL) { | |||
| fprintf (stderr, "jack_client_open() failed, " | |||
| "status = 0x%2.0x\n", status); | |||
| return 1; | |||
| } | |||
| signal(SIGQUIT, signal_handler); | |||
| signal(SIGTERM, signal_handler); | |||
| signal(SIGHUP, signal_handler); | |||
| signal(SIGINT, signal_handler); | |||
| jack_on_shutdown(client, jack_shutdown, 0); | |||
| if (jack_activate(client)) { | |||
| fprintf(stderr, "cannot activate client"); | |||
| return 1; | |||
| } | |||
| /* execute commands until done */ | |||
| command_loop(); | |||
| jack_client_close(client); | |||
| exit(0); | |||
| } | |||
| @@ -0,0 +1,204 @@ | |||
| /** @file simple_client.c | |||
| * | |||
| * @brief This simple client demonstrates the basic features of JACK | |||
| * as they would be used by many applications. | |||
| */ | |||
| #include <stdio.h> | |||
| #include <errno.h> | |||
| #include <unistd.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <jack/jack.h> | |||
| jack_port_t *input_port; | |||
| jack_port_t *output_port; | |||
| jack_client_t *client; | |||
| /* a simple state machine for this client */ | |||
| volatile enum { | |||
| Init, | |||
| Run, | |||
| Exit | |||
| } client_state = Init; | |||
| /** | |||
| * The process callback for this JACK application is called in a | |||
| * special realtime thread once for each audio cycle. | |||
| * | |||
| * This client follows a simple rule: when the JACK transport is | |||
| * running, copy the input port to the output. When it stops, exit. | |||
| */ | |||
| int | |||
| _process (jack_nframes_t nframes) | |||
| { | |||
| jack_default_audio_sample_t *in, *out; | |||
| jack_transport_state_t ts = jack_transport_query(client, NULL); | |||
| if (ts == JackTransportRolling) { | |||
| if (client_state == Init) | |||
| client_state = Run; | |||
| in = jack_port_get_buffer (input_port, nframes); | |||
| out = jack_port_get_buffer (output_port, nframes); | |||
| memcpy (out, in, | |||
| sizeof (jack_default_audio_sample_t) * nframes); | |||
| } else if (ts == JackTransportStopped) { | |||
| if (client_state == Run) | |||
| client_state = Exit; | |||
| } | |||
| return 0; | |||
| } | |||
| int | |||
| process (jack_nframes_t nframes, void* arg) | |||
| { | |||
| jack_client_t* client = (jack_client_t*) arg; | |||
| while ((nframes = jack_thread_wait (client, _process (nframes))) != 0); | |||
| return 0; | |||
| } | |||
| /** | |||
| * JACK calls this shutdown_callback if the server ever shuts down or | |||
| * decides to disconnect the client. | |||
| */ | |||
| void | |||
| jack_shutdown (void *arg) | |||
| { | |||
| exit (1); | |||
| } | |||
| int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| const char **ports; | |||
| const char *client_name; | |||
| const char *server_name = NULL; | |||
| jack_options_t options = JackNullOption; | |||
| jack_status_t status; | |||
| if (argc >= 2) { /* client name specified? */ | |||
| client_name = argv[1]; | |||
| if (argc >= 3) { /* server name specified? */ | |||
| server_name = argv[2]; | |||
| options |= JackServerName; | |||
| } | |||
| } else { /* use basename of argv[0] */ | |||
| client_name = strrchr(argv[0], '/'); | |||
| if (client_name == 0) { | |||
| client_name = argv[0]; | |||
| } else { | |||
| client_name++; | |||
| } | |||
| } | |||
| /* open a client connection to the JACK server */ | |||
| client = jack_client_open (client_name, options, &status, server_name); | |||
| if (client == NULL) { | |||
| fprintf (stderr, "jack_client_open() failed, " | |||
| "status = 0x%2.0x\n", status); | |||
| if (status & JackServerFailed) { | |||
| fprintf (stderr, "Unable to connect to JACK server\n"); | |||
| } | |||
| exit (1); | |||
| } | |||
| if (status & JackServerStarted) { | |||
| fprintf (stderr, "JACK server started\n"); | |||
| } | |||
| if (status & JackNameNotUnique) { | |||
| client_name = jack_get_client_name(client); | |||
| fprintf (stderr, "unique name `%s' assigned\n", client_name); | |||
| } | |||
| /* tell the JACK server to call `process()' whenever | |||
| there is work to be done. | |||
| */ | |||
| jack_set_process_callback (client, process, client); | |||
| /* tell the JACK server to call `jack_shutdown()' if | |||
| it ever shuts down, either entirely, or if it | |||
| just decides to stop calling us. | |||
| */ | |||
| jack_on_shutdown (client, jack_shutdown, 0); | |||
| /* display the current sample rate. | |||
| */ | |||
| printf ("engine sample rate: %" PRIu32 "\n", | |||
| jack_get_sample_rate (client)); | |||
| /* create two ports */ | |||
| 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 ((input_port == NULL) || (output_port == NULL)) { | |||
| fprintf(stderr, "no more JACK ports available\n"); | |||
| exit (1); | |||
| } | |||
| /* Tell the JACK server that we are ready to roll. Our | |||
| * process() callback will start running now. */ | |||
| if (jack_activate (client)) { | |||
| fprintf (stderr, "cannot activate client"); | |||
| exit (1); | |||
| } | |||
| /* Connect the ports. You can't do this before the client is | |||
| * activated, because we can't make connections to clients | |||
| * that aren't running. Note the confusing (but necessary) | |||
| * orientation of the driver backend ports: playback ports are | |||
| * "input" to the backend, and capture ports are "output" from | |||
| * it. | |||
| */ | |||
| ports = jack_get_ports (client, NULL, NULL, | |||
| JackPortIsPhysical|JackPortIsOutput); | |||
| if (ports == NULL) { | |||
| fprintf(stderr, "no physical capture ports\n"); | |||
| exit (1); | |||
| } | |||
| if (jack_connect (client, ports[0], jack_port_name (input_port))) { | |||
| fprintf (stderr, "cannot connect input ports\n"); | |||
| } | |||
| free (ports); | |||
| ports = jack_get_ports (client, NULL, NULL, | |||
| JackPortIsPhysical|JackPortIsInput); | |||
| if (ports == NULL) { | |||
| fprintf(stderr, "no physical playback ports\n"); | |||
| exit (1); | |||
| } | |||
| if (jack_connect (client, jack_port_name (output_port), ports[0])) { | |||
| fprintf (stderr, "cannot connect output ports\n"); | |||
| } | |||
| free (ports); | |||
| /* keep running until the transport stops */ | |||
| while (client_state != Exit) { | |||
| sleep (1); | |||
| } | |||
| jack_client_close (client); | |||
| exit (0); | |||
| } | |||
| @@ -0,0 +1,157 @@ | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <unistd.h> | |||
| #include <string.h> | |||
| #include <getopt.h> | |||
| #include <time.h> | |||
| #include <config.h> | |||
| #include <jack/jack.h> | |||
| char * my_name; | |||
| void | |||
| show_version (void) | |||
| { | |||
| fprintf (stderr, "%s: JACK Audio Connection Kit version " VERSION "\n", | |||
| my_name); | |||
| } | |||
| void | |||
| show_usage (void) | |||
| { | |||
| show_version (); | |||
| fprintf (stderr, "\nUsage: %s [options]\n", my_name); | |||
| fprintf (stderr, "Check for jack existence, or wait, until it either quits, or gets started\n"); | |||
| fprintf (stderr, "options:\n"); | |||
| fprintf (stderr, " -s, --server <name> Connect to the jack server named <name>\n"); | |||
| fprintf (stderr, " -w, --wait Wait for server to become available\n"); | |||
| fprintf (stderr, " -q, --quit Wait until server is quit\n"); | |||
| fprintf (stderr, " -c, --check Check wether server is running\n"); | |||
| fprintf (stderr, " -t, --timeout Wait timeout in seconds\n"); | |||
| fprintf (stderr, " -h, --help Display this help message\n"); | |||
| fprintf (stderr, " --version Output version information and exit\n\n"); | |||
| fprintf (stderr, "For more information see http://jackaudio.org/\n"); | |||
| } | |||
| void silent_function( const char *ignore ) | |||
| { | |||
| } | |||
| int | |||
| main (int argc, char *argv[]) | |||
| { | |||
| jack_client_t *client; | |||
| jack_status_t status; | |||
| jack_options_t options = JackNoStartServer; | |||
| int c; | |||
| int option_index; | |||
| char *server_name = NULL; | |||
| int wait_for_start = 0; | |||
| int wait_for_quit = 0; | |||
| int just_check = 0; | |||
| int wait_timeout = 0; | |||
| time_t start_timestamp; | |||
| struct option long_options[] = { | |||
| { "server", 1, 0, 's' }, | |||
| { "wait", 0, 0, 'w' }, | |||
| { "quit", 0, 0, 'q' }, | |||
| { "check", 0, 0, 'c' }, | |||
| { "timeout", 1, 0, 't' }, | |||
| { "help", 0, 0, 'h' }, | |||
| { "version", 0, 0, 'v' }, | |||
| { 0, 0, 0, 0 } | |||
| }; | |||
| my_name = strrchr(argv[0], '/'); | |||
| if (my_name == 0) { | |||
| my_name = argv[0]; | |||
| } else { | |||
| my_name ++; | |||
| } | |||
| while ((c = getopt_long (argc, argv, "s:wqct:hv", long_options, &option_index)) >= 0) { | |||
| switch (c) { | |||
| case 's': | |||
| server_name = (char *) malloc (sizeof (char) * strlen(optarg)); | |||
| strcpy (server_name, optarg); | |||
| options |= JackServerName; | |||
| break; | |||
| case 'w': | |||
| wait_for_start = 1; | |||
| break; | |||
| case 'q': | |||
| wait_for_quit = 1; | |||
| break; | |||
| case 'c': | |||
| just_check = 1; | |||
| break; | |||
| case 't': | |||
| wait_timeout = atoi(optarg); | |||
| break; | |||
| case 'h': | |||
| show_usage (); | |||
| return 1; | |||
| break; | |||
| case 'v': | |||
| show_version (); | |||
| return 1; | |||
| break; | |||
| default: | |||
| show_usage (); | |||
| return 1; | |||
| break; | |||
| } | |||
| } | |||
| /* try to open server in a loop. breaking under certein conditions */ | |||
| start_timestamp = time( NULL ); | |||
| jack_set_info_function( silent_function ); | |||
| while(1) { | |||
| client = jack_client_open ("wait", options, &status, server_name); | |||
| /* check for some real error and bail out */ | |||
| if( (client == NULL) && !(status & JackServerFailed) ) { | |||
| fprintf (stderr, "jack_client_open() failed, " | |||
| "status = 0x%2.0x\n", status); | |||
| return 1; | |||
| } | |||
| if( client == NULL ) { | |||
| if( wait_for_quit ) { | |||
| fprintf( stdout, "server is gone\n" ); | |||
| break; | |||
| } | |||
| if( just_check ) { | |||
| fprintf( stdout, "not running\n" ); | |||
| break; | |||
| } | |||
| } else { | |||
| jack_client_close( client ); | |||
| if( wait_for_start ) { | |||
| fprintf( stdout, "server is available\n" ); | |||
| break; | |||
| } | |||
| if( just_check ) { | |||
| fprintf( stdout, "running\n" ); | |||
| break; | |||
| } | |||
| } | |||
| if( wait_timeout ) { | |||
| if( (time( NULL ) - start_timestamp) > wait_timeout ) { | |||
| fprintf( stdout, "timeout\n" ); | |||
| break; | |||
| } | |||
| } | |||
| // Wait a second, and repeat | |||
| sleep(1); | |||
| } | |||
| exit (0); | |||
| } | |||