Browse Source

Import jack1 tools from svn r4772

tags/0.124.0
Nedko Arnaudov 13 years ago
commit
c8270e367f
21 changed files with 5314 additions and 0 deletions
  1. +158
    -0
      Makefile.am
  2. +121
    -0
      alias.c
  3. +801
    -0
      alsa_in.c
  4. +803
    -0
      alsa_out.c
  5. +125
    -0
      bufsize.c
  6. +222
    -0
      connect.c
  7. +92
    -0
      evmon.c
  8. +86
    -0
      freewheel.c
  9. +260
    -0
      iodelay.c
  10. +170
    -0
      ipload.c
  11. +76
    -0
      ipunload.c
  12. +120
    -0
      load_test.c
  13. +229
    -0
      lsp.c
  14. +114
    -0
      midi_dump.c
  15. +46
    -0
      monitor_client.c
  16. +783
    -0
      netsource.c
  17. +85
    -0
      samplerate.c
  18. +181
    -0
      session_notify.c
  19. +481
    -0
      transport.c
  20. +204
    -0
      tw.c
  21. +157
    -0
      wait.c

+ 158
- 0
Makefile.am View File

@@ -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

+ 121
- 0
alias.c View File

@@ -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;
}

+ 801
- 0
alsa_in.c View File

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


+ 803
- 0
alsa_out.c View File

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


+ 125
- 0
bufsize.c View File

@@ -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;
}

+ 222
- 0
connect.c View File

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


+ 92
- 0
evmon.c View File

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


+ 86
- 0
freewheel.c View File

@@ -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;
}

+ 260
- 0
iodelay.c View File

@@ -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;
}

// --------------------------------------------------------------------------------

+ 170
- 0
ipload.c View File

@@ -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;
}

+ 76
- 0
ipunload.c View File

@@ -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;
}

+ 120
- 0
load_test.c View File

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

+ 229
- 0
lsp.c View File

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

+ 114
- 0
midi_dump.c View File

@@ -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;
}

+ 46
- 0
monitor_client.c View File

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


+ 783
- 0
netsource.c View File

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

+ 85
- 0
samplerate.c View File

@@ -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;
}

+ 181
- 0
session_notify.c View File

@@ -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;
}

+ 481
- 0
transport.c View File

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

+ 204
- 0
tw.c View File

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

+ 157
- 0
wait.c View File

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

Loading…
Cancel
Save