From 38aaeb99209f879df507c55f22a3a5bf22717b0c Mon Sep 17 00:00:00 2001 From: sletz Date: Thu, 6 Mar 2008 08:51:44 +0000 Subject: [PATCH] Version 0.109.6: add NetJack code in source tree git-svn-id: svn+ssh://jackaudio.org/trunk/jack@1095 0c269be4-1314-0410-8aa9-9f06e86f4224 --- configure.ac | 3 +- drivers/Makefile.am | 4 +- drivers/netjack/Makefile.am | 12 + drivers/netjack/README | 106 +++ drivers/netjack/alsa_client.c | 378 +++++++++ drivers/netjack/alsa_in.c | 569 +++++++++++++ drivers/netjack/alsa_out.c | 550 +++++++++++++ drivers/netjack/net_driver.c | 734 +++++++++++++++++ drivers/netjack/net_driver.h | 76 ++ drivers/netjack/net_source.c | 415 ++++++++++ drivers/netjack/netjack_packet.c | 887 +++++++++++++++++++++ drivers/netjack/netjack_packet.h | 121 +++ drivers/netjack/netjack_packet_noReOrder.c | 595 ++++++++++++++ 13 files changed, 4447 insertions(+), 3 deletions(-) create mode 100644 drivers/netjack/Makefile.am create mode 100644 drivers/netjack/README create mode 100644 drivers/netjack/alsa_client.c create mode 100644 drivers/netjack/alsa_in.c create mode 100644 drivers/netjack/alsa_out.c create mode 100644 drivers/netjack/net_driver.c create mode 100644 drivers/netjack/net_driver.h create mode 100644 drivers/netjack/net_source.c create mode 100644 drivers/netjack/netjack_packet.c create mode 100644 drivers/netjack/netjack_packet.h create mode 100644 drivers/netjack/netjack_packet_noReOrder.c diff --git a/configure.ac b/configure.ac index 25680d6..d12b563 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl changes are made dnl --- JACK_MAJOR_VERSION=0 JACK_MINOR_VERSION=109 -JACK_MICRO_VERSION=5 +JACK_MICRO_VERSION=6 dnl --- dnl HOWTO: updating the jack protocol version @@ -720,6 +720,7 @@ drivers/portaudio/Makefile drivers/coreaudio/Makefile drivers/freebob/Makefile drivers/firewire/Makefile +drivers/netjack/Makefile example-clients/Makefile jack.pc jack.spec diff --git a/drivers/Makefile.am b/drivers/Makefile.am index f8c9a94..123373e 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -42,5 +42,5 @@ else FIREWIRE_DIR = endif -SUBDIRS = $(ALSA_MIDI_DIR) $(ALSA_DIR) dummy $(OSS_DIR) $(PA_DIR) $(CA_DIR) $(FREEBOB_DIR) $(FIREWIRE_DIR) -DIST_SUBDIRS = alsa alsa-midi dummy oss portaudio coreaudio freebob firewire +SUBDIRS = $(ALSA_MIDI_DIR) $(ALSA_DIR) dummy $(OSS_DIR) $(PA_DIR) $(CA_DIR) $(FREEBOB_DIR) $(FIREWIRE_DIR) netjack +DIST_SUBDIRS = alsa alsa-midi dummy oss portaudio coreaudio freebob firewire netjack diff --git a/drivers/netjack/Makefile.am b/drivers/netjack/Makefile.am new file mode 100644 index 0000000..4d03f37 --- /dev/null +++ b/drivers/netjack/Makefile.am @@ -0,0 +1,12 @@ +MAINTAINCLEANFILES = Makefile.in + +AM_CFLAGS = $(JACK_CFLAGS) + +plugindir = $(ADDON_DIR) + +plugin_LTLIBRARIES = jack_net.la + +jack_net_la_LDFLAGS = -module -avoid-version +jack_net_la_SOURCES = net_driver.c netjack_packet.c + +noinst_HEADERS = net_driver.h diff --git a/drivers/netjack/README b/drivers/netjack/README new file mode 100644 index 0000000..21777bc --- /dev/null +++ b/drivers/netjack/README @@ -0,0 +1,106 @@ + + + +see the updated docs on http://netjack.sf.net please. + +or mail me (torbenh@gmx.de) if you have questions. +this Release has the tightest jack sync ever :) + + --- netJack --- + - v0.5pre1 2005 - + +|| AUTHORS(s): + +Torben Hohn +Dan Mills +Robert Jonsson + + +|| CHANGES + +cvs -th- + fixed hardcoeded number of channels. + started + +cvs -th- + added packet_hdr + which needs to be htonled.. + +cvs -rj- + added Sconstruct + added htonl() usage for crossplatform communication. + +0.41 - rj - + added missing Makefile.am + added some configurability to udpsync_source + +0.4 - rj - + support for stereo + support for duplex +... older ... lost in time + +|| WHAT IS THIS? + +jack_net is a backend driver for Jack that takes on the role of a +sound card. This machine is generally designated as the slave machine. + +jacknet_client is a jack application that shall be run from another +computer with the ip adress of the other machine as argument. This +machine is generalled designated as the master machine. + + +|| PREREQUISITES + +Two machines with a _good_ network connection between them. +A Jack (http://jackit.sf.net) source package. + + +|| INSTALLATION: + +compile with +scons jack_source_dir='path/to/jack-src' + +this creates jack_net.so. +copy this to /lib/jack/drivers or wherever jack looks for driver.so`s + +it also creates jacknet_client. a normal program. + +if you build on OSX you need to add + +with_alsa=0 + + +|| USAGE + +The programs will open a bidirectional connection between the two +machines (using UDP). Exposing a stereopair both ways. + +The udpsync_source needs the slave machine as a parameter. + +The alsa-client is a nice thing also, it makes a not jack related +alsa-card available for capturing under jack. + +Best performance is achieved if connecting the machines with an + XOVER cable, omitting switches / hubs / other nasty things. + +|| KNOWN ISSUES + +While running with full duplex, utilizing the slave machine as an +outboard effect it seems very hard (atleast with my network with a +cheap switch inbetween) to use buffers below 512. Sometimes 256 +is usable for a while. +While connecting ports on the slave side it is very prone to +crash if the buffers are small. + +The jitter of a wlan network is a little too hard for the current +sync code. A delay locked loop might be used soon. + +There is always atleast one buffers delay if a port is routed back +through the connection. This is a design issue that might be hard +to remedy. + + +|| LICENSE + +this is free software under the GPL license, see the file COPYING. + diff --git a/drivers/netjack/alsa_client.c b/drivers/netjack/alsa_client.c new file mode 100644 index 0000000..4f1e143 --- /dev/null +++ b/drivers/netjack/alsa_client.c @@ -0,0 +1,378 @@ +/** @file simple_client.c + * + * @brief This simple client demonstrates the basic features of JACK + * as they would be used by many applications. + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#define ALSA_PCM_OLD_HW_PARAMS_API +#define ALSA_PCM_OLD_SW_PARAMS_API +#include "alsa/asoundlib.h" + +#include + +typedef signed short OUTPUTSAMPLE; + +#define SAMPLE_RATE 48000 +#define PRIVATE static +#define SAMPLE jack_default_audio_sample_t + +jack_port_t *input_port; +jack_port_t *output_port1, *output_port2; +jack_client_t *client; + +snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ +int rate = SAMPLE_RATE; /* stream rate */ +int channels = 2; /* count of channels */ +int buffer_time = 1000000*256 / SAMPLE_RATE; /* ring buffer length in us */ +int period_time = 1000000*128 / SAMPLE_RATE; /* period time in us */ + +int target_delay = 150; /* the delay which the program should try to approach. */ +int max_diff = 32; /* the diff value, when a hard readpointer skip should occur */ + +snd_pcm_sframes_t buffer_size; +snd_pcm_sframes_t period_size; +snd_output_t *output = NULL; + +snd_pcm_t *alsa_handle; + +SRC_STATE *src_state; + +static int xrun_recovery(snd_pcm_t *handle, int err) +{ + //printf( "xrun !!!....\n" ); + 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 == -ESTRPIPE) { + while ((err = snd_pcm_resume(handle)) == -EAGAIN) + sleep(1); /* 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; +} + +PRIVATE void audio_input_fragment(snd_pcm_t *handle, SAMPLE *left, SAMPLE *right, int length) +{ + OUTPUTSAMPLE *outbuf; + float *floatbuf, *resampbuf; + int rlen; + int i, err; + snd_pcm_sframes_t delay; + SRC_DATA src_data; + + if (length <= 0) + return; + + snd_pcm_delay( handle, &delay ); + + // Do it the hard way. + // this is for compensating xruns etc... + + if ( delay > (target_delay + max_diff) ) { + OUTPUTSAMPLE *tmp = alloca( (delay - target_delay) * sizeof( OUTPUTSAMPLE ) * 2 ); + snd_pcm_readi( handle, tmp, delay - target_delay ); + printf( "delay = %d\n", (int) delay ); + delay = target_delay; + } + if ( delay < (target_delay - max_diff) ) { + snd_pcm_rewind( handle, target_delay - delay ); + printf( "delay = %d\n", (int) delay ); + delay = target_delay; + } + + // ok... now to the resampling code... + // + + rlen = length - target_delay + delay; //(target_delay/10) + (delay/10); + + + outbuf = alloca( rlen * sizeof( OUTPUTSAMPLE ) * 2 ); + floatbuf = alloca( rlen * sizeof( float ) * 2 ); + resampbuf = alloca( length * sizeof( float ) * 2 ); + +again: + err = snd_pcm_readi(handle, outbuf, rlen); + if ( err < 0 ) { + //printf( "err = %d\n", err ); + if (xrun_recovery(handle, err) < 0) { + //printf("Write error: %s\n", snd_strerror(err)); + //exit(EXIT_FAILURE); + } + goto again; + } + if ( err != rlen ) { + printf( "read = %d\n", rlen ); + } + + for (i = 0; i < (rlen*2); i++) { + floatbuf[i] = (float)outbuf[i] / (float)32767; + } + + src_data.data_in = floatbuf; + src_data.data_out = resampbuf; + src_data.input_frames = rlen; + src_data.output_frames = length; + src_data.src_ratio = (double)length / (double)rlen; + src_data.end_of_input = 0; + + src_set_ratio( src_state, src_data.src_ratio ); + src_process( src_state, &src_data ); + +// for( i=0; i < length*2; i++ ) +// resampbuf[i] = floatbuf[i]; + + for (i = 0; i < length; i++) { + left[i] = resampbuf[i*2]; + right[i] = resampbuf[(i*2)+1]; + } + //printf( "len=%d, err=%d state=%d\n", length, err, snd_pcm_state(handle) ); +} + +static int set_hwparams(snd_pcm_t *handle, + snd_pcm_hw_params_t *params, + snd_pcm_access_t access) +{ + int err, dir; + + /* 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 = snd_pcm_hw_params_set_format(handle, params, format); + if (err < 0) { + printf("Sample format not available for playback: %s\n", snd_strerror(err)); + return err; + } + /* set the count of channels */ + err = snd_pcm_hw_params_set_channels(handle, params, channels); + if (err < 0) { + printf("Channels count (%i) not available for record: %s\n", channels, snd_strerror(err)); + return err; + } + /* set the stream rate */ + err = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0); + if (err < 0) { + printf("Rate %iHz not available for capture: %s\n", rate, snd_strerror(err)); + return err; + } + if (err != rate) { + printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err); + return -EINVAL; + } + /* set the buffer time */ + 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", buffer_time, snd_strerror(err)); + return err; + } + buffer_size = snd_pcm_hw_params_get_buffer_size(params); + /* set the period time */ + 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", period_time, snd_strerror(err)); + return err; + } + period_size = snd_pcm_hw_params_get_period_size(params, &dir); + /* 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; + } + printf( "bs=%d, ps=%d\n", (int)buffer_size, (int)period_size ); + return 0; +} + +static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams) +{ + 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, buffer_size ); + 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, period_size ); + 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; +} + + +PRIVATE snd_pcm_t *open_audiofd( void ) +{ + 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), "hw:0", SND_PCM_STREAM_CAPTURE, 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 )) < 0) { + printf("Setting of hwparams failed: %s\n", snd_strerror(err)); + return NULL; + } + if ((err = set_swparams(handle, swparams)) < 0) { + printf("Setting of swparams failed: %s\n", snd_strerror(err)); + return NULL; + } + + snd_pcm_start( handle ); + snd_pcm_wait( handle, 200 ); + + return handle; +} +/** + * 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_default_audio_sample_t *out1 = (jack_default_audio_sample_t *) jack_port_get_buffer (output_port1, nframes); + jack_default_audio_sample_t *out2 = (jack_default_audio_sample_t *) jack_port_get_buffer (output_port2, nframes); + //jack_default_audio_sample_t *in = (jack_default_audio_sample_t *) jack_port_get_buffer (input_port, nframes); + + //memcpy (out, in, sizeof (jack_default_audio_sample_t) * nframes); + + audio_input_fragment( alsa_handle, out1, out2, nframes ); + + //sendto( sockfd, "x", 1, 0, &destaddr, sizeof( destaddr ) ); + 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); +} + + +int +main (int argc, char *argv[]) +{ + //const char **ports; + + +// if (argc < 2) { +// fprintf (stderr, "usage: udpsync_source desthost\n"); +// return 1; +// } + +// sockfd = socket( PF_INET, SOCK_DGRAM, 0 ); +// init_sockaddr_in( &destaddr, argv[1], 3000 ); + /* try to become a client of the JACK server */ + + + src_state = src_new(SRC_SINC_FASTEST, 2, NULL); + src_set_ratio( src_state, 1.0 ); + + alsa_handle = open_audiofd(); + + if ((client = jack_client_new ("alsa_unsynced_pcm")) == 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); + + /* 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_port1 = jack_port_register(client, "output1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + output_port2 = jack_port_register(client, "output2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + + /* tell the JACK server that we are ready to roll */ + + if (jack_activate(client)) { + fprintf (stderr, "cannot activate client"); + return 1; + } + + while (1) sleep(1); + jack_client_close(client); + exit (0); +} + diff --git a/drivers/netjack/alsa_in.c b/drivers/netjack/alsa_in.c new file mode 100644 index 0000000..c699a77 --- /dev/null +++ b/drivers/netjack/alsa_in.c @@ -0,0 +1,569 @@ +/** @file simple_client.c + * + * @brief This simple client demonstrates the basic features of JACK + * as they would be used by many applications. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define ALSA_PCM_OLD_HW_PARAMS_API +#define ALSA_PCM_OLD_SW_PARAMS_API +#include "alsa/asoundlib.h" + +#include + +typedef signed short ALSASAMPLE; + +// 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; + +// TODO: make the sample format configurable soon... +snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ + +snd_pcm_t *alsa_handle; + +int jack_sample_rate; + +double current_resample_factor = 1.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 = 1000; + +// Debug stuff: + +int print_counter = 10; + +// 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 !!!....\n" ); + 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 == -ESTRPIPE) { + while ((err = snd_pcm_resume(handle)) == -EAGAIN) + sleep(1); /* 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_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; + + /* 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 = snd_pcm_hw_params_set_format(handle, params, format); + if (err < 0) { + printf("Sample format not available for playback: %s\n", snd_strerror(err)); + return err; + } + /* set the count of channels */ + err = snd_pcm_hw_params_set_channels(handle, params, channels); + if (err < 0) { + printf("Channels count (%i) not available for record: %s\n", channels, snd_strerror(err)); + return err; + } + /* set the stream rate */ + err = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0); + if (err < 0) { + printf("Rate %iHz not available for capture: %s\n", rate, snd_strerror(err)); + return err; + } + if (err != rate) { + printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err); + return -EINVAL; + } + /* set the buffer time */ + err = snd_pcm_hw_params_set_buffer_time_near(handle, params, 1000000 * period * nperiods / rate, &dir); + if (err < 0) { + printf("Unable to set buffer time %i for playback: %s\n", 1000000*period*nperiods / rate, snd_strerror(err)); + return err; + } + if ( snd_pcm_hw_params_get_buffer_size(params) != nperiods * period ) { + printf( "WARNING: buffer size does not match: (requested %d, got %d)\n", nperiods * period, (int) snd_pcm_hw_params_get_buffer_size(params) ); + } + /* set the period time */ + err = snd_pcm_hw_params_set_period_time_near(handle, params, 1000000 * period / rate, &dir); + if (err < 0) { + printf("Unable to set period time %i for playback: %s\n", 1000000*period / rate, snd_strerror(err)); + return err; + } + int ps = snd_pcm_hw_params_get_period_size(params, NULL ); + if ( ps != period ) { + printf( "WARNING: period size does not match: (requested %i, got %i)\n", period, ps ); + } + /* 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, 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; +} + + +/** + * The process callback for this JACK application. + * It is called by JACK at the appropriate times. + */ +int process (jack_nframes_t nframes, void *arg) +{ + + ALSASAMPLE *outbuf; + float *floatbuf, *resampbuf; + int rlen; + int err; + snd_pcm_sframes_t delay; + int put_back_samples = 0; + + + snd_pcm_delay( alsa_handle, &delay ); + + delay = delay; + // Do it the hard way. + // this is for compensating xruns etc... + + if ( delay > (target_delay + max_diff) ) { + ALSASAMPLE *tmp = alloca( (delay - target_delay) * sizeof( ALSASAMPLE ) * num_channels ); + snd_pcm_readi( alsa_handle, tmp, delay - target_delay ); + printf( "delay = %d\n", (int) delay ); + delay = target_delay; + // XXX: at least set it to that value. + current_resample_factor = (double) jack_sample_rate / (double) sample_rate; + } + if ( delay < (target_delay - max_diff) ) { + snd_pcm_rewind( alsa_handle, target_delay - delay ); + printf( "delay = %d\n", (int) delay ); + delay = target_delay; + // XXX: at least set it to that value. + current_resample_factor = (double) jack_sample_rate / (double) sample_rate; + } + + /* ok... now we should have target_delay +- max_diff on the alsa side. + * + * calculate the number of frames, we want to get. + */ + + // float resamp_rate = (float)jack_sample_rate / (float)sample_rate; // == nframes / alsa_samples. + // float request_samples = nframes / resamp_rate; //== alsa_samples; + + //float offset = delay - target_delay; + + //float frlen = request_samples + offset / catch_factor; + //rlen = ceil( frlen ); + + // This is the code from the alsa_out... + + double resamp_rate = (double)jack_sample_rate / (double)sample_rate; // == nframes / alsa_samples. + double request_samples = nframes / resamp_rate; //== alsa_samples; + + double offset = delay - target_delay; + + //double frlen = request_samples - offset / catch_factor; + double frlen = request_samples + offset; + //alsa_out.c: double compute_factor = frlen / (double) nframes; + double compute_factor = (double) nframes / frlen; + + double diff_value = pow(current_resample_factor - compute_factor, 3) / (double) catch_factor; + current_resample_factor -= diff_value; + rlen = ceil( ((double)nframes) / current_resample_factor ) + 2; + + if ( (print_counter--) == 0 ) { + print_counter = 10; + printf( "res: %f, \tdiff = %f, \toffset = %f \n", (float)current_resample_factor, (float)diff_value, (float) offset ); + } + + /* + * now this should do it... + */ + + outbuf = alloca( rlen * sizeof( ALSASAMPLE ) * num_channels ); + + floatbuf = alloca( rlen * sizeof( float ) ); + resampbuf = alloca( nframes * sizeof( float ) ); + + // 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; + while ( node != NULL) { + int i; + 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 src; + + for (i = 0; i < rlen; i++) { + resampbuf[i] = (float) outbuf[chn+ i*num_channels] / 32767; + } + + src.data_in = resampbuf; + src.input_frames = rlen; + + src.data_out = buf; + src.output_frames = nframes; + src.end_of_input = 0; + + //src.src_ratio = (float) nframes / frlen; + src.src_ratio = current_resample_factor; + + //src_set_ratio( src_state, src.src_ratio ); + src_process( src_state, &src ); + + put_back_samples = rlen - src.input_frames_used; + + if ( src.output_frames_gen != nframes ) + printf( "did not fill jack_buffer...\n" ); + + src_node = jack_slist_next (src_node); + node = jack_slist_next (node); + chn++; + } + + //printf( "putback = %d\n", put_back_samples ); + snd_pcm_rewind( alsa_handle, put_back_samples ); + + return 0; +} + + +/** + * 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( SRC_SINC_FASTEST, 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( SRC_SINC_FASTEST, 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 - reports a different name to jack\n" + " -d \n" + " -c \n" + " -p \n" + " -n \n" + " -r \n" + " -m \n" + " -t \n" + " -f \n" + "\n"); +} + + +/** + * the main function.... + */ + + +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, ":j:r:c:p:n:d:m:t:f:")) != -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 'm': + max_diff = atoi(optarg); + break; + case 'f': + catch_factor = 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); + } + + // 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; + + if ( !max_diff ) + max_diff = period_size / 2; + + + + if ((client = jack_client_new (jack_name)) == 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); + + + // alloc input ports, which are blasted out to alsa... + alloc_ports( num_channels, 0 ); + + // get jack sample_rate + + jack_sample_rate = jack_get_sample_rate( client ); + + if ( !sample_rate ) + sample_rate = jack_sample_rate; + + current_resample_factor = (double) jack_sample_rate / (double) 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); + + + /* tell the JACK server that we are ready to roll */ + + if (jack_activate (client)) { + fprintf (stderr, "cannot activate client"); + return 1; + } + + while (1) sleep(1); + jack_client_close (client); + exit (0); +} + diff --git a/drivers/netjack/alsa_out.c b/drivers/netjack/alsa_out.c new file mode 100644 index 0000000..8d3c694 --- /dev/null +++ b/drivers/netjack/alsa_out.c @@ -0,0 +1,550 @@ +/** @file simple_client.c + * + * @brief This simple client demonstrates the basic features of JACK + * as they would be used by many applications. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define ALSA_PCM_OLD_HW_PARAMS_API +#define ALSA_PCM_OLD_SW_PARAMS_API +#include "alsa/asoundlib.h" + +#include + +typedef signed short ALSASAMPLE; + +// 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; + +// TODO: make the sample format configurable soon... +snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ + +snd_pcm_t *alsa_handle; + +int jack_sample_rate; + +double current_resample_factor = 1.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 = 1000; + +// Debug stuff: + +int print_counter = 10; + +// 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 !!!....\n" ); + 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 == -ESTRPIPE) { + while ((err = snd_pcm_resume(handle)) == -EAGAIN) + sleep(1); /* 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_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; + + /* 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 = snd_pcm_hw_params_set_format(handle, params, format); + if (err < 0) { + printf("Sample format not available for playback: %s\n", snd_strerror(err)); + return err; + } + /* set the count of channels */ + err = snd_pcm_hw_params_set_channels(handle, params, channels); + if (err < 0) { + printf("Channels count (%i) not available for record: %s\n", channels, snd_strerror(err)); + return err; + } + /* set the stream rate */ + err = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0); + if (err < 0) { + printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err)); + return err; + } + if (err != rate) { + printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err); + return -EINVAL; + } + /* set the buffer time */ + err = snd_pcm_hw_params_set_buffer_time_near(handle, params, 1000000 * period * nperiods / rate, &dir); + if (err < 0) { + printf("Unable to set buffer time %i for playback: %s\n", 1000000*period*nperiods / rate, snd_strerror(err)); + return err; + } + if ( snd_pcm_hw_params_get_buffer_size(params) != nperiods * period ) { + printf( "WARNING: buffer size does not match: (requested %d, got %d)\n", nperiods * period, (int) snd_pcm_hw_params_get_buffer_size(params) ); + } + /* set the period time */ + err = snd_pcm_hw_params_set_period_time_near(handle, params, 1000000 * period / rate, &dir); + if (err < 0) { + printf("Unable to set period time %i for playback: %s\n", 1000000*period / rate, snd_strerror(err)); + return err; + } + int ps = snd_pcm_hw_params_get_period_size(params, NULL ); + if ( ps != period ) { + printf( "WARNING: period size does not match: (requested %i, got %i)\n", period, ps ); + } + /* 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; + ALSASAMPLE *tmp = alloca( num_null_samples * sizeof( ALSASAMPLE ) ); + memset( tmp, 0, num_null_samples * sizeof( ALSASAMPLE ) ); + snd_pcm_writei( handle, tmp, num_null_samples ); + + + return handle; +} + +/** + * The process callback for this JACK application. + * It is called by JACK at the appropriate times. + */ +int process (jack_nframes_t nframes, void *arg) +{ + ALSASAMPLE *outbuf; + float *floatbuf, *resampbuf; + int rlen; + int err; + snd_pcm_sframes_t delay; + + snd_pcm_delay( alsa_handle, &delay ); + + // 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 ); + //snd_pcm_writei( alsa_handle, tmp, target_delay-t_delay ); + printf( "delay = %d", (int) delay ); + snd_pcm_delay( alsa_handle, &delay ); + printf( "... and delay = %d\n", (int) delay ); + delay = target_delay; + // XXX: at least set it to that value. + current_resample_factor = (double) sample_rate / (double) jack_sample_rate; + } + if (delay < (target_delay - max_diff)) { + ALSASAMPLE *tmp = alloca( (target_delay - delay) * sizeof( ALSASAMPLE ) * num_channels ); + memset( tmp, 0, sizeof( ALSASAMPLE ) * num_channels * (target_delay - delay) ); + snd_pcm_writei( alsa_handle, tmp, target_delay - delay ); + printf( "delay = %d", (int) delay ); + snd_pcm_delay( alsa_handle, &delay ); + printf( "... and delay = %d\n", (int) delay ); + delay = target_delay; + // XXX: at least set it to that value. + current_resample_factor = (double) sample_rate / (double) jack_sample_rate; + } + /* ok... now we should have target_delay +- max_diff on the alsa side. + * + * calculate the number of frames, we want to get. + */ + + double resamp_rate = (double)jack_sample_rate / (double)sample_rate; // == nframes / alsa_samples. + double request_samples = nframes / resamp_rate; //== alsa_samples; + + double offset = delay - target_delay; + + //double frlen = request_samples - offset / catch_factor; + double frlen = request_samples - offset; + + double compute_factor = frlen / (double) nframes; + + double diff_value = pow(current_resample_factor - compute_factor, 3) / (double) catch_factor; + current_resample_factor -= diff_value; + rlen = ceil( ((double)nframes) * current_resample_factor ) + 2; + + if ( (print_counter--) == 0 ) { + print_counter = 10; + printf( "res: %f, \tdiff = %f, \toffset = %f \n", (float)current_resample_factor, (float)diff_value, (float) offset ); + } + + /* + * now this should do it... + */ + + outbuf = alloca( rlen * sizeof( ALSASAMPLE ) * num_channels ); + + floatbuf = alloca( rlen * sizeof( float ) ); + resampbuf = alloca( nframes * 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) { + int i; + 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 = (float) frlen / (float) nframes; + src.src_ratio = current_resample_factor; + + //src_set_ratio( src_state, src.src_ratio ); + src_process( src_state, &src ); + + for (i = 0; i < rlen; i++) { + outbuf[chn+ i*num_channels] = resampbuf[i] * 32767; + } + + 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); + 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( "write = %d\n", rlen ); +// } + + return 0; +} + +/** + * 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( SRC_SINC_FASTEST, 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( SRC_SINC_FASTEST, 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 - reports a different name to jack\n" + " -d \n" + " -c \n" + " -p \n" + " -n \n" + " -r \n" + " -m \n" + " -t \n" + " -f \n" + "\n"); +} + +/** + * the main function.... + */ + +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, ":j:r:c:p:n:d:m:t:f:")) != -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 'm': + max_diff = atoi(optarg); + break; + case 'f': + catch_factor = 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); + } + + // 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; + + if (!max_diff) + max_diff = period_size / 2; + + + + if ((client = jack_client_new(jack_name)) == 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); + + + // alloc input ports, which are blasted out to alsa... + alloc_ports( 0, num_channels ); + + // get jack sample_rate + + jack_sample_rate = jack_get_sample_rate(client); + + if (!sample_rate) + sample_rate = jack_sample_rate; + + current_resample_factor = (double) sample_rate / (double) jack_sample_rate; + // 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); + + /* tell the JACK server that we are ready to roll */ + + if (jack_activate (client)) { + fprintf (stderr, "cannot activate client"); + return 1; + } + + while (1) sleep(1); + jack_client_close (client); + exit (0); +} + diff --git a/drivers/netjack/net_driver.c b/drivers/netjack/net_driver.c new file mode 100644 index 0000000..d0b0a6f --- /dev/null +++ b/drivers/netjack/net_driver.c @@ -0,0 +1,734 @@ +/* -*- mode: c; c-file-style: "linux"; -*- */ +/* +NetJack Driver + +Copyright (C) 2006 Torben Hohn +Copyright (C) 2003 Robert Ham +Copyright (C) 2001 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. + +$Id: net_driver.c,v 1.17 2006/04/16 20:16:10 torbenh Exp $ +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +//#include +#include +#include + +#include +#include +#include + +#include + +#include "net_driver.h" +#include "netjack_packet.h" + +#undef DEBUG_WAKEUP + +static int sync_state = TRUE; +static jack_transport_state_t last_transport_state; +static int framecnt; // this is set upon reading a packet. +// and will be written into the pkthdr of an outgoing packet. + + + + +static int +net_driver_sync_cb( jack_transport_state_t state, jack_position_t *pos, net_driver_t *driver ) +{ + int retval = sync_state; + + if ( state == JackTransportStarting && last_transport_state != JackTransportStarting ) { + retval = 0; + } + if ( state == JackTransportStarting ) printf( "Starting sync_state = %d\n", sync_state ); + last_transport_state = state; + return retval; +} + +static jack_nframes_t +net_driver_wait (net_driver_t *driver, int extra_fd, int *status, + float *delayed_usecs) +{ + // ok... we wait for a packet on the socket + // TODO: + // we should be able to run freely if the sync source is not transmitting + // especially we should be able to compensate for packet loss somehow. + // but lets try this out first. + // + // on packet loss we should either detect an xrun or just continue running when we + // think, that the sync source is not running anymore. + +#if 0 + fd_set read_fds; + int res = 0; + while ( res == 0) { + FD_ZERO(&read_fds); + FD_SET(driver->sockfd, &read_fds); + res = select(driver->sockfd + 1, &read_fds, NULL, NULL, NULL); // wait here until there is a packet to get + } +#endif + + socklen_t address_size = sizeof(struct sockaddr_in); + + int bufsize = get_sample_size( driver->bitdepth ) * driver->capture_channels * driver->net_period_down + sizeof(jacknet_packet_header); + + jacknet_packet_header *pkthdr = (jacknet_packet_header *)driver->rx_buf; + +rx_again: + if ( !driver->srcaddress_valid ) { + // XXX: Somthings really bad ;) + puts("!driver->srcaddress_valid"); + return 0; + } + int len = netjack_recvfrom( driver->sockfd, (char *)driver->rx_buf, bufsize, + MSG_WAITALL, (struct sockaddr*) & driver->syncsource_address, &address_size, 1400 ); + + + if (len != bufsize) { + printf( "wrong_packet_len: len=%d, expected=%d\n", len, bufsize ); + goto rx_again; + } + + packet_header_ntoh( pkthdr ); + + + //driver->srcaddress_valid = 0; + + driver->last_wait_ust = jack_get_microseconds (); + driver->engine->transport_cycle_start (driver->engine, + driver->last_wait_ust); + + /* this driver doesn't work so well if we report a delay */ + *delayed_usecs = 0; /* lie about it */ + *status = 0; + return driver->period_size; +} + +static inline int +net_driver_run_cycle (net_driver_t *driver) +{ + jack_engine_t *engine = driver->engine; + int wait_status; + float delayed_usecs; + + jack_nframes_t nframes = net_driver_wait (driver, -1, &wait_status, + &delayed_usecs); + + // currently there is no xrun detection. + // so nframes will always be period_size. + // XXX: i uncomment this code because the signature of delay() + // changed samewhere in the 0.99.x series. so this is the showstopper for 0.99.0 + +#if 0 + if (nframes == 0) { + /* we detected an xrun and restarted: notify + * clients about the delay. */ + engine->delay (engine, delayed_usecs ); + return 0; + } +#endif + + if (wait_status == 0) + return engine->run_cycle (engine, nframes, delayed_usecs); + + if (wait_status < 0) + return -1; + else + return 0; +} + +static int +net_driver_null_cycle (net_driver_t* driver, jack_nframes_t nframes) +{ + //int rx_size = get_sample_size( driver->bitdepth ) * driver->capture_channels * driver->net_period_down + sizeof(jacknet_packet_header); + int tx_size = get_sample_size( driver->bitdepth ) * driver->playback_channels * driver->net_period_up + sizeof(jacknet_packet_header); + unsigned int *packet_buf, *packet_bufX; + + packet_buf = alloca( tx_size ); + jacknet_packet_header *tx_pkthdr = (jacknet_packet_header *)packet_buf; + jacknet_packet_header *rx_pkthdr = (jacknet_packet_header *)driver->rx_buf; + + framecnt = rx_pkthdr->framecnt; + + driver->reply_port = rx_pkthdr->reply_port; + + + + // offset packet_bufX by the packetheader. + packet_bufX = packet_buf + sizeof(jacknet_packet_header) / sizeof(jack_default_audio_sample_t); + + tx_pkthdr->sync_state = (driver->engine->control->sync_remain <= 1); + + tx_pkthdr->framecnt = framecnt; + + // memset 0 the payload. + int payload_size = get_sample_size( driver->bitdepth ) * driver->playback_channels * driver->net_period_up; + memset( packet_bufX, 0, payload_size ); + + packet_header_hton( tx_pkthdr ); + if ( driver->srcaddress_valid ) + if ( driver->reply_port ) + driver->syncsource_address.sin_port = htons( driver->reply_port ); + + netjack_sendto( driver->outsockfd, (char *)packet_buf, tx_size, + 0, (struct sockaddr*)&driver->syncsource_address, sizeof(struct sockaddr_in), 1400 ); + + return 0; +} + +static int +net_driver_bufsize (net_driver_t* driver, jack_nframes_t nframes) +{ + if ( nframes != driver->period_size ) + return EINVAL; + + return 0; +} + +static int +net_driver_read (net_driver_t* driver, jack_nframes_t nframes) +{ + //jack_default_audio_sample_t* buf; + //jack_port_t *port; + jack_position_t local_trans_pos; + jack_transport_state_t local_trans_state; + + //int bufsize = get_sample_size( driver->bitdepth ) * driver->capture_channels * driver->net_period_down + sizeof(jacknet_packet_header); + unsigned int *packet_buf, *packet_bufX; + + packet_buf = driver->rx_buf; + + jacknet_packet_header *pkthdr = (jacknet_packet_header *)packet_buf; + + + + packet_bufX = packet_buf + sizeof(jacknet_packet_header) / sizeof(jack_default_audio_sample_t); + + //packet_header_ntoh( pkthdr ); + + + // fill framecnt from pkthdr. + + if ( pkthdr->framecnt != framecnt + 1 ) + printf( "bogus framecount %d\n", pkthdr->framecnt ); + + framecnt = pkthdr->framecnt; + driver->reply_port = pkthdr->reply_port; + + + // check whether, we should handle the transport sync stuff, or leave trnasports untouched. + + if ( driver->handle_transport_sync ) { + + // read local transport info.... + local_trans_state = jack_transport_query( driver->client, &local_trans_pos ); + + + // Now check if we have to start or stop local transport to sync to remote... + switch ( pkthdr->transport_state ) { + case JackTransportStarting: + // the master transport is starting... so we set our reply to the sync_callback; + if ( local_trans_state == JackTransportStopped ) { + jack_transport_start( driver->client ); + last_transport_state = JackTransportStopped; + sync_state = FALSE; + printf( "locally stopped... starting... \n" ); + } + + if ( local_trans_pos.frame != (pkthdr->transport_frame + (pkthdr->latency) * nframes ) ) { + jack_transport_locate( driver->client, (pkthdr->transport_frame + (pkthdr->latency) * nframes ) ); + last_transport_state = JackTransportRolling; + sync_state = FALSE; + printf( "starting locate to %d\n", pkthdr->transport_frame + (pkthdr->latency)*nframes); + } + break; + case JackTransportStopped: + sync_state = TRUE; + if ( local_trans_pos.frame != (pkthdr->transport_frame) ) { + jack_transport_locate( driver->client, (pkthdr->transport_frame ) ); + printf( "transport is stopped locate to %d\n", pkthdr->transport_frame); + } + if ( local_trans_state != JackTransportStopped ) + jack_transport_stop( driver->client ); + break; + case JackTransportRolling: + sync_state = TRUE; +// if( local_trans_pos.frame != (pkthdr->transport_frame + (pkthdr->latency) * nframes ) ) { +// jack_transport_locate( driver->client, (pkthdr->transport_frame + (pkthdr->latency + 2) * nframes ) ); +// printf( "running locate to %d\n", pkthdr->transport_frame + (pkthdr->latency)*nframes); +// } + if ( local_trans_state != JackTransportRolling ) + jack_transport_start( driver->client ); + break; + + case JackTransportLooping: + break; + } + } + + + render_payload_to_jack_ports( driver->bitdepth, packet_bufX, driver->net_period_down, driver->capture_ports, driver->capture_srcs, nframes ); + + return 0; +} + +static int +net_driver_write (net_driver_t* driver, jack_nframes_t nframes) +{ + uint32_t *packet_buf, *packet_bufX; + + int packet_size = get_sample_size( driver->bitdepth ) * driver->playback_channels * driver->net_period_up + sizeof(jacknet_packet_header); + + packet_buf = alloca( packet_size ); + + jacknet_packet_header *pkthdr = (jacknet_packet_header *)packet_buf; + + // offset packet_bufX by the packetheader. + packet_bufX = packet_buf + sizeof(jacknet_packet_header) / sizeof(jack_default_audio_sample_t); + + pkthdr->sync_state = (driver->engine->control->sync_remain <= 1); + pkthdr->framecnt = framecnt; + + render_jack_ports_to_payload( driver->bitdepth, driver->playback_ports, driver->playback_srcs, nframes, packet_bufX, driver->net_period_up ); + + packet_header_hton( pkthdr ); + if ( driver->srcaddress_valid ) + if ( driver->reply_port ) + driver->syncsource_address.sin_port = htons( driver->reply_port ); + netjack_sendto( driver->outsockfd, (char *)packet_buf, packet_size, + 0, (struct sockaddr*)&driver->syncsource_address, sizeof(struct sockaddr_in), 1400 ); + + return 0; +} + + +static int +net_driver_attach (net_driver_t *driver) +{ + puts ("net_driver_attach"); + jack_port_t * port; + char buf[32]; + unsigned int chn; + int port_flags; + + driver->engine->set_buffer_size (driver->engine, driver->period_size); + driver->engine->set_sample_rate (driver->engine, driver->sample_rate); + + if ( driver->handle_transport_sync ) + jack_set_sync_callback( driver->client, (JackSyncCallback) net_driver_sync_cb, driver ); + + port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; + + for (chn = 0; chn < driver->capture_channels; chn++) { + snprintf (buf, sizeof(buf) - 1, "capture_%u", chn + 1); + + port = jack_port_register (driver->client, buf, + JACK_DEFAULT_AUDIO_TYPE, + port_flags, 0); + if (!port) { + jack_error ("DUMMY: cannot register port for %s", buf); + break; + } + + driver->capture_ports = + jack_slist_append (driver->capture_ports, port); + driver->capture_srcs = jack_slist_append( driver->capture_srcs, src_new( SRC_LINEAR, 1, NULL ) ); + } + + port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; + + for (chn = 0; chn < driver->playback_channels; chn++) { + snprintf (buf, sizeof(buf) - 1, "playback_%u", chn + 1); + + port = jack_port_register (driver->client, buf, + JACK_DEFAULT_AUDIO_TYPE, + port_flags, 0); + + if (!port) { + jack_error ("DUMMY: cannot register port for %s", buf); + break; + } + + driver->playback_ports = + jack_slist_append (driver->playback_ports, port); + driver->playback_srcs = jack_slist_append( driver->playback_srcs, src_new( SRC_LINEAR, 1, NULL ) ); + } + + jack_activate (driver->client); + + return 0; +} + +static int +net_driver_detach (net_driver_t *driver) +{ + JSList * node; + + if (driver->engine == 0) + return 0; +//#if 0 + for (node = driver->capture_ports; node; node = jack_slist_next (node)) + jack_port_unregister (driver->client, + ((jack_port_t *) node->data)); + + jack_slist_free (driver->capture_ports); + driver->capture_ports = NULL; +//#endif + + for (node = driver->playback_ports; node; node = jack_slist_next (node)) + jack_port_unregister (driver->client, + ((jack_port_t *) node->data)); + + jack_slist_free (driver->playback_ports); + driver->playback_ports = NULL; + + return 0; +} + + +static void +net_driver_delete (net_driver_t *driver) +{ + jack_driver_nt_finish ((jack_driver_nt_t *) driver); + free (driver); +} + +static jack_driver_t * +net_driver_new (jack_client_t * client, + char *name, + unsigned int capture_ports, + unsigned int playback_ports, + jack_nframes_t sample_rate, + jack_nframes_t period_size, + unsigned int listen_port, + unsigned int transport_sync, + unsigned int resample_factor, + unsigned int resample_factor_up, + unsigned int bitdepth ) +{ + net_driver_t * driver; + struct sockaddr_in address; + + printf ("creating net driver ... %s|%" PRIu32 "|%" PRIu32 + "|%u|%u|%u|transport_sync:%u\n", name, sample_rate, period_size, listen_port, + capture_ports, playback_ports, transport_sync); + + driver = (net_driver_t *) calloc (1, sizeof (net_driver_t)); + + jack_driver_nt_init ((jack_driver_nt_t *) driver); + + driver->write = (JackDriverWriteFunction) net_driver_write; + driver->read = (JackDriverReadFunction) net_driver_read; + driver->null_cycle = (JackDriverNullCycleFunction) net_driver_null_cycle; + driver->nt_attach = (JackDriverNTAttachFunction) net_driver_attach; + driver->nt_detach = (JackDriverNTDetachFunction) net_driver_detach; + driver->nt_bufsize = (JackDriverNTBufSizeFunction) net_driver_bufsize; + driver->nt_run_cycle = (JackDriverNTRunCycleFunction) net_driver_run_cycle; + + // Fill in driver values. + // might be subject to autoconfig... + // so dont calculate anything with them... + + driver->sample_rate = sample_rate; + driver->period_size = period_size; + + driver->listen_port = listen_port; + driver->last_wait_ust = 0; + + driver->capture_channels = capture_ports; + driver->capture_ports = NULL; + driver->playback_channels = playback_ports; + driver->playback_ports = NULL; + + driver->handle_transport_sync = transport_sync; + driver->client = client; + driver->engine = NULL; + + if ( (bitdepth != 0) && (bitdepth != 8) && (bitdepth != 16) ) { + printf( "Invalid bitdepth: %d (8,16 or 0 for float) !!!\n", bitdepth ); + return NULL; + } + driver->bitdepth = bitdepth; + + + if ( resample_factor_up == 0 ) + resample_factor_up = resample_factor; + + // Now open the socket, and wait for the first packet to arrive... + + + driver->sockfd = socket( PF_INET, SOCK_DGRAM, 0 ); + if ( driver->sockfd == -1 ) { + printf( "socket error\n" ); + return NULL; + } + address.sin_family = AF_INET; + address.sin_port = htons( driver->listen_port ); + address.sin_addr.s_addr = htonl( INADDR_ANY ); + if ( bind( driver->sockfd, (struct sockaddr *) &address, sizeof(address) ) < 0 ) { + printf( "bind error\n" ); + return NULL; + } + + driver->outsockfd = socket( PF_INET, SOCK_DGRAM, 0 ); + if ( driver->sockfd == -1 ) { + printf( "socket error\n" ); + return NULL; + } + driver->srcaddress_valid = 0; + + jacknet_packet_header *first_packet = alloca( sizeof(jacknet_packet_header) ); + socklen_t address_size = sizeof(struct sockaddr_in); + + printf( "Waiting for an incoming packet !!!\n\n" ); + printf( "*** IMPORTANT ***\n Dont connect a client to jackd until the driver is attached to a clock source !!!\n" ); + + + int first_pack_len = recvfrom( driver->sockfd, first_packet, sizeof(jacknet_packet_header), 0, (struct sockaddr*) & driver->syncsource_address, &address_size ); + driver->srcaddress_valid = 1; + + printf( "first_pack_len=%d\n", first_pack_len ); + // A packet is here.... If it wasnt the old trigger packet containing only 'x' evaluate the autoconf data... + + driver->mtu = 0; + + if ( first_pack_len == sizeof(jacknet_packet_header) ) { + packet_header_ntoh( first_packet ); + + printf("AutoConfig Override !!!\n" ); + if ( driver->sample_rate != first_packet->sample_rate ) { + printf("AutoConfig Override: sample_rate = %d\n", first_packet->sample_rate ); + driver->sample_rate = first_packet->sample_rate; + } + + if ( driver->period_size != first_packet->period_size ) { + printf("AutoConfig Override: period_size = %d\n", first_packet->period_size ); + driver->period_size = first_packet->period_size; + } + + driver->mtu = first_packet->mtu; + driver->latency = first_packet->latency; + } + + // After possible Autoconfig: do all calculations... + + driver->period_usecs = + (jack_time_t) floor ((((float) driver->period_size) / driver->sample_rate) + * 1000000.0f); + + + + driver->net_period_down = (float) driver->period_size / (float) resample_factor; + driver->net_period_up = (float) driver->period_size / (float) resample_factor_up; + + driver->rx_buf = malloc( sizeof(jacknet_packet_header) + driver->net_period_down * driver->capture_channels * get_sample_size( driver->bitdepth ) ); + + // XXX: dont need it when packet size < mtu + driver->pkt_buf = malloc( sizeof(jacknet_packet_header) + driver->net_period_down * driver->capture_channels * get_sample_size( driver->bitdepth ) ); + + int rx_bufsize = sizeof(jacknet_packet_header) + driver->net_period_down * driver->capture_channels * get_sample_size( driver->bitdepth ); + global_packcache = packet_cache_new( driver->latency + 5, rx_bufsize, 1400 ); + + printf( "net_period: (up:%d/dn:%d)\n", driver->net_period_up, driver->net_period_down ); + + + return (jack_driver_t *) driver; +} + + +/* DRIVER "PLUGIN" INTERFACE */ + +jack_driver_desc_t * +driver_get_descriptor () +{ + jack_driver_desc_t * desc; + jack_driver_param_desc_t * params; + unsigned int i; + + desc = calloc (1, sizeof (jack_driver_desc_t)); + strcpy (desc->name, "net"); + desc->nparams = 9; + + params = calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); + + i = 0; + strcpy (params[i].name, "inchannels"); + params[i].character = 'i'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 2U; + strcpy (params[i].short_desc, "Number of capture channels (defaults to 2)"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "outchannels"); + params[i].character = 'o'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 2U; + strcpy (params[i].short_desc, "Number of playback channels (defaults to 2)"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "rate"); + params[i].character = 'r'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 48000U; + strcpy (params[i].short_desc, "Sample rate"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "period"); + params[i].character = 'p'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 1024U; + strcpy (params[i].short_desc, "Frames per period"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "listen-port"); + params[i].character = 'l'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 3000U; + strcpy (params[i].short_desc, + "The socket port we are listening on for sync packets"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "factor"); + params[i].character = 'f'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 1U; + strcpy (params[i].short_desc, + "Factor for sample rate reduction"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "upstream-factor"); + params[i].character = 'u'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 0U; + strcpy (params[i].short_desc, + "Factor for sample rate reduction on the upstream"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "bit-depth"); + params[i].character = 'b'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 0U; + strcpy (params[i].short_desc, + "Sample bit-depth (0 for float, 8 for 8bit and 16 for 16bit)"); + strcpy (params[i].long_desc, params[i].short_desc); + i++; + + strcpy (params[i].name, "transport-sync"); + params[i].character = 't'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 1U; + strcpy (params[i].short_desc, + "Whether to slave the transport to the master transport"); + strcpy (params[i].long_desc, params[i].short_desc); + + desc->params = params; + + return desc; +} + +const char driver_client_name[] = "net_pcm"; + +jack_driver_t * +driver_initialize (jack_client_t *client, const JSList * params) +{ + jack_nframes_t sample_rate = 48000; + jack_nframes_t resample_factor = 1; + jack_nframes_t period_size = 1024; + unsigned int capture_ports = 2; + unsigned int playback_ports = 2; + unsigned int listen_port = 3000; + unsigned int resample_factor_up = 0; + unsigned int bitdepth = 0; + unsigned int handle_transport_sync = 1; + const JSList * node; + const jack_driver_param_t * param; + + for (node = params; node; node = jack_slist_next (node)) { + param = (const jack_driver_param_t *) node->data; + + switch (param->character) { + + case 'i': + capture_ports = param->value.ui; + break; + + case 'o': + playback_ports = param->value.ui; + break; + + case 'r': + sample_rate = param->value.ui; + break; + + case 'p': + period_size = param->value.ui; + break; + + case 'l': + listen_port = param->value.ui; + break; + + case 'f': + resample_factor = param->value.ui; + break; + + case 'u': + resample_factor_up = param->value.ui; + break; + + case 'b': + bitdepth = param->value.ui; + break; + + case 't': + handle_transport_sync = param->value.ui; + break; + + } + } + + + return net_driver_new (client, "net_pcm", capture_ports, + playback_ports, sample_rate, period_size, + listen_port, handle_transport_sync, + resample_factor, resample_factor_up, bitdepth); +} + +void +driver_finish (jack_driver_t *driver) +{ + net_driver_delete ((net_driver_t *) driver); +} diff --git a/drivers/netjack/net_driver.h b/drivers/netjack/net_driver.h new file mode 100644 index 0000000..4bb4030 --- /dev/null +++ b/drivers/netjack/net_driver.h @@ -0,0 +1,76 @@ +/* + Copyright (C) 2003 Robert Ham + + 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. +*/ + + +#ifndef __JACK_NET_DRIVER_H__ +#define __JACK_NET_DRIVER_H__ + +#include + +#include +#include +#include +#include + +#include +#include + +typedef struct _net_driver net_driver_t; + +struct _net_driver +{ + JACK_DRIVER_NT_DECL + + jack_nframes_t net_period_up; + jack_nframes_t net_period_down; + + jack_nframes_t sample_rate; + jack_nframes_t bitdepth; + jack_nframes_t period_size; + + unsigned int listen_port; + + unsigned int capture_channels; + unsigned int playback_channels; + + JSList *capture_ports; + JSList *playback_ports; + JSList *playback_srcs; + JSList *capture_srcs; + + jack_client_t *client; + + int sockfd; + int outsockfd; + + struct sockaddr_in syncsource_address; + + int reply_port; + int srcaddress_valid; + + int sync_state; + unsigned int handle_transport_sync; + + unsigned int *rx_buf; + unsigned int *pkt_buf; + unsigned int mtu; + unsigned int latency; +}; + + +#endif /* __JACK_NET_DRIVER_H__ */ diff --git a/drivers/netjack/net_source.c b/drivers/netjack/net_source.c new file mode 100644 index 0000000..985a559 --- /dev/null +++ b/drivers/netjack/net_source.c @@ -0,0 +1,415 @@ +/** @file simple_client.c + * + * @brief This simple client demonstrates the basic features of JACK + * as they would be used by many applications. + */ + +#include +#include (#include +#include +#include + +#include +#include + +#include + +#include + +#include "net_driver.h" +#include "netjack_packet.h" +#include + +JSList *capture_ports = NULL; +JSList *capture_srcs = NULL; +int capture_channels = 2; +JSList *playback_ports = NULL; +JSList *playback_srcs = NULL; +int playback_channels = 2; + +int latency = 1; +jack_nframes_t factor = 1; +int bitdepth = 0; +int reply_port = 0; +jack_client_t *client; + +int outsockfd; +int insockfd; +struct sockaddr destaddr; +struct sockaddr bindaddr; + + +int recv_channels; +int recv_smaple_format; + + +int sync_state; +jack_transport_state_t last_transport_state; + +int framecnt = 0; + +int cont_miss = 0; + +SRC_STATE *src_state; +/* + * This Function allocates all the I/O Ports which are added the lists. + * XXX: jack-midi is underway... so dont forget it. + */ + +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(SRC_LINEAR, 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(SRC_LINEAR, 1, NULL)); + 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 (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; +} + + +/** + * The process callback for this JACK application. + * It is called by JACK at the appropriate times. + */ +int +process (jack_nframes_t nframes, void *arg) +{ + //static int old_count = 0; + + //SRC_DATA src; + jack_nframes_t net_period = (float) nframes / (float) factor; + + //jack_position_t jack_pos; + + int rx_bufsize = get_sample_size(bitdepth) * capture_channels * net_period + sizeof(jacknet_packet_header); + int tx_bufsize = get_sample_size(bitdepth) * playback_channels * net_period + sizeof(jacknet_packet_header); + + jack_default_audio_sample_t *buf; + jack_port_t *port; + JSList *node; + channel_t chn; + int size; + + jack_position_t local_trans_pos; + + uint32_t *packet_buf, *packet_bufX; + + // Allocate a buffer where both In and Out Buffer will fit + packet_buf = alloca((rx_bufsize > tx_bufsize) ? rx_bufsize : tx_bufsize); + + + jacknet_packet_header *pkthdr = (jacknet_packet_header *)packet_buf; + + packet_bufX = packet_buf + sizeof(jacknet_packet_header) / sizeof(uint32_t); + + //////////////// receive //////////////// + //////////////// //////////////// +ReadAgain: + if (reply_port) + size = netjack_recv(insockfd, (char *)packet_buf, rx_bufsize, MSG_DONTWAIT, 1400); + else + size = netjack_recv(outsockfd, (char *)packet_buf, rx_bufsize, MSG_DONTWAIT, 1400); + + packet_header_ntoh(pkthdr); + // evaluate rcvd data. + + // XXX: think about this a little more... + //if((size == rx_bufsize) && (framecnt - pkthdr->framecnt) >= latency) { + if ((size == rx_bufsize)) { + + cont_miss = 0; + if ((framecnt - pkthdr->framecnt) > latency) { + printf("FRAMCNT_DIFF = %d ----- A Packet was lost, or did came too late (try -l %d) \n", pkthdr->framecnt - framecnt, framecnt - pkthdr->framecnt); + goto ReadAgain; + } + + // packet has expected size + + render_payload_to_jack_ports(bitdepth, packet_bufX, net_period, capture_ports, capture_srcs, nframes); + // Now evaluate packet header; + if (sync_state != pkthdr->sync_state) printf("sync = %d\n", pkthdr->sync_state); + sync_state = pkthdr->sync_state; + } else { + printf("Packet Miss: (expected: %d, got: %d) framecnt=%d\n", rx_bufsize, size, framecnt); + cont_miss += 1; + chn = 0; + node = capture_ports; + while (node != NULL) { + int i; + port = (jack_port_t *) node->data; + buf = jack_port_get_buffer (port, nframes); + + for (i = 0; i < nframes; i++) { + buf[i] = 0.0; + } + node = jack_slist_next (node); + chn++; + } + } + //////////////// + //////////////// + + // reset packet_bufX... and then send... + + packet_bufX = packet_buf + 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); + + // fill in packet hdr + + pkthdr->transport_state = jack_transport_query(client, &local_trans_pos); + pkthdr->transport_frame = local_trans_pos.frame; + pkthdr->framecnt = framecnt; + pkthdr->latency = latency; + pkthdr->reply_port = reply_port; + + pkthdr->sample_rate = jack_get_sample_rate(client); + pkthdr->period_size = nframes; + + packet_header_hton(pkthdr); + if (cont_miss < 10) { + netjack_sendto(outsockfd, (char *)packet_buf, tx_bufsize, 0, &destaddr, sizeof(destaddr), 1400); + } else { + if (cont_miss > 50) + cont_miss = 5; + } + + 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) ; + } + name->sin_addr = *(struct in_addr *) hostinfo->h_addr ; + } else { + name->sin_addr.s_addr = htonl (INADDR_ANY) ; + } +} + +void +printUsage() +{ + fprintf(stderr, "usage: net_source [-n ] [-s ] [-C ] [-P ] -p \n" + "\n" + " -n - reports a different name to jack\n" + " -s select another socket than the default (3000).\n" + " -p the hostname of the \"other\" machine running the jack-slave.\n" + " -P number of playback channels.\n" + " -C number of capture channels.\n" + " -l number of packets on the wire to approach\n" + " -r When using a firewall use this port for incoming packets\n" + " -f downsample data in the wire by this factor.\n" + " -b Set transport to use 16bit or 8bit\n" + "\n"); +} + +int +main (int argc, char *argv[]) +{ + char jack_name[30] = "net_source"; + char *peer_ip = "localhost"; + int peer_socket = 3000; + + if (argc < 3) { + printUsage(); + return 1; + } + extern char *optarg; + extern int optind, optopt; + int errflg = 0; + int c; + + while ((c = getopt(argc, argv, ":n:p:s:C:P:l:r:f:b:")) != -1) { + switch (c) { + case 'n': + strcpy(jack_name, optarg); + break; + case 'p': + peer_ip = optarg; + break; + case 's': + peer_socket = atoi(optarg); + break; + case 'P': + playback_channels = atoi(optarg); + break; + case 'C': + capture_channels = atoi(optarg); + break; + case 'l': + latency = atoi(optarg); + break; + case 'r': + reply_port = atoi(optarg); + break; + case 'f': + factor = atoi(optarg); + break; + case 'b': + bitdepth = atoi(optarg); + break; + case ':': /* -n or -p without operand */ + 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); + } + + //src_state = src_new(SRC_LINEAR, 1, NULL); + + outsockfd = socket(PF_INET, SOCK_DGRAM, 0); + insockfd = socket(PF_INET, SOCK_DGRAM, 0); + init_sockaddr_in((struct sockaddr_in *)&destaddr, peer_ip, peer_socket); + if (reply_port) { + init_sockaddr_in((struct sockaddr_in *)&bindaddr, NULL, reply_port); + bind(insockfd, &bindaddr, sizeof(bindaddr)); + } + /* try to become a client of the JACK server */ + + if ((client = jack_client_new (jack_name)) == 0) { + fprintf (stderr, "jack server not running?\n"); + return 1; + } + + /* + send a ping to the peer + -- this needs to be made more robust -- + */ + + //sendto(outsockfd, "x", 1, 0, &destaddr, sizeof(destaddr)); + + /* tell the JACK server to call `process()' whenever + there is work to be done. + */ + + jack_set_process_callback (client, process, 0); + jack_set_sync_callback (client, sync_cb, 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); + + + /* display the current sample rate. + */ + + printf ("engine sample rate: %" PRIu32 "\n", + jack_get_sample_rate (client)); + + alloc_ports(capture_channels, playback_channels); + + jack_nframes_t net_period = (float) jack_get_buffer_size(client) / (float) factor; + int rx_bufsize = get_sample_size(bitdepth) * capture_channels * net_period + sizeof(jacknet_packet_header); + global_packcache = packet_cache_new(latency + 5, rx_bufsize, 1400); + + /* tell the JACK server that we are ready to roll */ + + if (jack_activate (client)) { + fprintf (stderr, "cannot activate client"); + return 1; + } + + // Now sleep forever....... + + while (1) + sleep(100); + + // Never reached. Well we will be a GtkApp someday.... + + packet_cache_free(global_packcache); + jack_client_close(client); + exit (0); +} + diff --git a/drivers/netjack/netjack_packet.c b/drivers/netjack/netjack_packet.c new file mode 100644 index 0000000..9adf5d4 --- /dev/null +++ b/drivers/netjack/netjack_packet.c @@ -0,0 +1,887 @@ + +/* + * NetJack - Packet Handling functions + * + * used by the driver and the jacknet_client + * + * Copyright (C) 2006 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. + * + * $Id: net_driver.c,v 1.16 2006/03/20 19:41:37 torbenh Exp $ + * + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include "net_driver.h" +#include "netjack_packet.h" + +int fraggo = 0; + +packet_cache *global_packcache; + +void +packet_header_hton(jacknet_packet_header *pkthdr) +{ + pkthdr->channels = htonl(pkthdr->channels); + pkthdr->period_size = htonl(pkthdr->period_size); + pkthdr->sample_rate = htonl(pkthdr->sample_rate); + pkthdr->sync_state = htonl(pkthdr->sync_state); + pkthdr->transport_frame = htonl(pkthdr->transport_frame); + pkthdr->transport_state = htonl(pkthdr->transport_state); + pkthdr->framecnt = htonl(pkthdr->framecnt); + pkthdr->latency = htonl(pkthdr->latency); + pkthdr->reply_port = htonl(pkthdr->reply_port); + pkthdr->mtu = htonl(pkthdr->mtu); + pkthdr->fragment_nr = htonl(pkthdr->fragment_nr); +} + +void +packet_header_ntoh(jacknet_packet_header *pkthdr) +{ + pkthdr->channels = ntohl(pkthdr->channels); + pkthdr->period_size = ntohl(pkthdr->period_size); + pkthdr->sample_rate = ntohl(pkthdr->sample_rate); + pkthdr->sync_state = ntohl(pkthdr->sync_state); + pkthdr->transport_frame = ntohl(pkthdr->transport_frame); + pkthdr->transport_state = ntohl(pkthdr->transport_state); + pkthdr->framecnt = ntohl(pkthdr->framecnt); + pkthdr->latency = ntohl(pkthdr->latency); + pkthdr->reply_port = ntohl(pkthdr->reply_port); + pkthdr->mtu = ntohl(pkthdr->mtu); + pkthdr->fragment_nr = ntohl(pkthdr->fragment_nr); +} + +int get_sample_size(int bitdepth) +{ + if (bitdepth == 8) + return sizeof(int8_t); + if (bitdepth == 16) + return sizeof(int16_t); + return sizeof(int32_t); +} + +// fragment management functions. + +packet_cache *packet_cache_new(int num_packets, int pkt_size, int mtu) +{ + int fragment_payload_size = mtu - sizeof(jacknet_packet_header); + int fragment_number = (pkt_size - sizeof(jacknet_packet_header) - 1) / fragment_payload_size + 1; + int i; + + packet_cache *pcache = malloc(sizeof(packet_cache)); + if (pcache == NULL) { + printf("could not allocate packet cache (1)\n"); + return NULL; + } + + pcache->size = num_packets; + pcache->packets = malloc(sizeof(cache_packet) * num_packets); + if (pcache->packets == NULL) { + printf("could not allocate packet cache (2)\n"); + return NULL; + } + + for (i = 0; i < num_packets; i++) { + pcache->packets[i].valid = 0; + pcache->packets[i].num_fragments = fragment_number; + pcache->packets[i].packet_size = pkt_size; + pcache->packets[i].mtu = mtu; + pcache->packets[i].framecnt = 0; + pcache->packets[i].fragment_array = malloc(sizeof(char) * fragment_number); + pcache->packets[i].packet_buf = malloc(pkt_size); + + if ((pcache->packets[i].fragment_array == NULL) || (pcache->packets[i].packet_buf == NULL)) { + printf("could not allocate packet cache (3)\n"); + return NULL; + } + } + + return pcache; +} + +void packet_cache_free(packet_cache *pcache) +{ + int i; + + for (i = 0; i < pcache->size; i++) { + free(pcache->packets[i].fragment_array); + free(pcache->packets[i].packet_buf); + } + + free(pcache->packets); + free(pcache); +} + +cache_packet *packet_cache_get_packet(packet_cache *pcache, jack_nframes_t framecnt) +{ + int i; + cache_packet *retval; + + for (i = 0; i < pcache->size; i++) { + if (pcache->packets[i].valid && (pcache->packets[i].framecnt == framecnt)) + return &(pcache->packets[i]); + } + + // The Packet is not in the packet cache. + // find a free packet. + + retval = packet_cache_get_free_packet(pcache); + if (retval != NULL) { + cache_packet_set_framecnt(retval, framecnt); + return retval; + } + + // No Free Packet available + // Get The Oldest packet and reset it. + + retval = packet_cache_get_oldest_packet(pcache); + cache_packet_reset(retval); + cache_packet_set_framecnt(retval, framecnt); + + return retval; +} + +cache_packet *packet_cache_get_oldest_packet(packet_cache *pcache) +{ + jack_nframes_t minimal_frame = 0; + cache_packet *retval = &(pcache->packets[0]); + int i; + + for (i = 0; i < pcache->size; i++) { + if (pcache->packets[i].valid && (pcache->packets[i].framecnt < minimal_frame)) { + minimal_frame = pcache->packets[i].framecnt; + retval = &(pcache->packets[i]); + } + } + + return retval; +} + + +cache_packet *packet_cache_get_free_packet(packet_cache *pcache) +{ + int i; + + for (i = 0; i < pcache->size; i++) { + if (pcache->packets[i].valid == 0) + return &(pcache->packets[i]); + } + + return NULL; +} + +void cache_packet_reset(cache_packet *pack) +{ + int i; + pack->valid = 0; + + // XXX: i dont think this is necessary here... + // fragement array is cleared in _set_framecnt() + + for (i = 0; i < pack->num_fragments; i++) + pack->fragment_array[i] = 0; +} + +void cache_packet_set_framecnt(cache_packet *pack, jack_nframes_t framecnt) +{ + int i; + + pack->framecnt = framecnt; + + for (i = 0; i < pack->num_fragments; i++) + pack->fragment_array[i] = 0; + + pack->valid = 1; +} + +void cache_packet_add_fragment(cache_packet *pack, char *packet_buf, int rcv_len) +{ + jacknet_packet_header *pkthdr = (jacknet_packet_header *) packet_buf; + int fragment_payload_size = pack->mtu - sizeof(jacknet_packet_header); + char *packet_bufX = pack->packet_buf + sizeof(jacknet_packet_header); + char *dataX = packet_buf + sizeof(jacknet_packet_header); + + jack_nframes_t fragment_nr = ntohl(pkthdr->fragment_nr); + jack_nframes_t framecnt = ntohl(pkthdr->framecnt); + + if (framecnt != pack->framecnt) { + printf("errror. framecnts dont match\n"); + return; + } + + if (fragment_nr == 0) { + memcpy(pack->packet_buf, packet_buf, pack->mtu); + pack->fragment_array[0] = 1; + + return; + } + + if ((fragment_nr < pack->num_fragments) && (fragment_nr > 0)) { + if ((fragment_nr * fragment_payload_size + rcv_len - sizeof(jacknet_packet_header)) <= (pack->packet_size - sizeof(jacknet_packet_header))) { + memcpy(packet_bufX + fragment_nr * fragment_payload_size, dataX, rcv_len - sizeof(jacknet_packet_header)); + pack->fragment_array[fragment_nr] = 1; + } else { + printf("too long packet received..."); + } + } +} + +int cache_packet_is_complete(cache_packet *pack) +{ + int i; + for (i = 0; i < pack->num_fragments; i++) + if (pack->fragment_array[i] == 0) + return FALSE; + + return TRUE; +} + +// fragmented packet IO + +int netjack_recvfrom(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, socklen_t *addr_size, int mtu) +{ + if (pkt_size <= mtu) { + return recvfrom(sockfd, packet_buf, pkt_size, flags, addr, addr_size); + } else { + + char *rx_packet = alloca(mtu); + jacknet_packet_header *pkthdr = (jacknet_packet_header *)rx_packet; + int rcv_len; + jack_nframes_t framecnt; + + cache_packet *cpack; + +rx_again: + rcv_len = recvfrom(sockfd, rx_packet, mtu, 0, addr, addr_size); + if (rcv_len < 0) + return rcv_len; + + framecnt = ntohl(pkthdr->framecnt); + + cpack = packet_cache_get_packet(global_packcache, framecnt); + cache_packet_add_fragment(cpack, rx_packet, rcv_len); + if (cache_packet_is_complete(cpack)) { + memcpy(packet_buf, cpack->packet_buf, pkt_size); + cache_packet_reset(cpack); + return pkt_size; + } + + goto rx_again; + } +} + +int netjack_recv(int sockfd, char *packet_buf, int pkt_size, int flags, int mtu) +{ + + if (pkt_size <= mtu) { + return recv(sockfd, packet_buf, pkt_size, flags); + } else { + + char *rx_packet = alloca(mtu); + jacknet_packet_header *pkthdr = (jacknet_packet_header *)rx_packet; + int rcv_len; + jack_nframes_t framecnt; + + cache_packet *cpack; + +rx_again: + rcv_len = recv(sockfd, rx_packet, mtu, flags); + if (rcv_len < 0) + return rcv_len; + + framecnt = ntohl(pkthdr->framecnt); + + cpack = packet_cache_get_packet(global_packcache, framecnt); + cache_packet_add_fragment(cpack, rx_packet, rcv_len); + if (cache_packet_is_complete(cpack)) { + memcpy(packet_buf, cpack->packet_buf, pkt_size); + cache_packet_reset(cpack); + return pkt_size; + } + + goto rx_again; + } +} +#if 0 +int netjack_recvfrom(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, socklen_t *addr_size, int mtu) +{ + char *rx_packet; + char *dataX; + int i; + + int fragment_payload_size; + + char *fragment_array; + int fragment_number; + int rx_frag_count, framenum; + + jacknet_packet_header *pkthdr; + + // Now loop and send all + char *packet_bufX; + + // wait for fragment_nr == 0 + int rcv_len; + + rx_packet = alloca(mtu); + dataX = rx_packet + sizeof(jacknet_packet_header); + + fragment_payload_size = mtu - sizeof(jacknet_packet_header); + + pkthdr = (jacknet_packet_header *)rx_packet; + + packet_bufX = packet_buf + sizeof(jacknet_packet_header); + + fragment_number = (pkt_size - sizeof(jacknet_packet_header) - 1) / fragment_payload_size + 1; + fragment_array = alloca(fragment_number); + for (i = 0; i < fragment_number; i++) + fragment_array[i] = 0; + + if (pkt_size <= mtu) { + return recvfrom(sockfd, packet_buf, pkt_size, flags, addr, addr_size); + } else { +rx_again: + rcv_len = recvfrom(sockfd, rx_packet, mtu, 0, addr, addr_size); + if (rcv_len < 0) + return rcv_len; + + if (rcv_len >= sizeof(jacknet_packet_header)) { + //printf("got fragmentooooo_nr = %d recv_len = %d\n", ntohl(pkthdr->fragment_nr), rcv_len); + if ((ntohl(pkthdr->fragment_nr)) != 0) + goto rx_again; + } else { + goto rx_again; + } + + // ok... we have read a fragement 0; + // copy the data into the packet buffer... + memcpy(packet_buf, rx_packet, mtu); + + rx_frag_count = 1; + framenum = ntohl(pkthdr->framecnt); + + fragment_array[0] = 1; + + while (rx_frag_count < fragment_number) { + + + rcv_len = recvfrom(sockfd, rx_packet, mtu, 0, addr, addr_size); + if (rcv_len < 0) + return -1; + + if (ntohl(pkthdr->framecnt) < framenum) { + //printf("Out of Order Framecnt: i abort on this packet\n"); + printf("Old Fragment !!! (got: %d, exp: %d)\n", ntohl(pkthdr->framecnt), framenum); + continue; + } + + if (ntohl(pkthdr->framecnt) > framenum) { + printf("Newer Fragment !!! (got: %d, exp: %d) Switching to new Packet.\n", ntohl(pkthdr->framecnt), framenum); + // Copy the new Packetheader up Front + memcpy(packet_buf, rx_packet, sizeof(jacknet_packet_header)); + + rx_frag_count = 0; + framenum = ntohl(pkthdr->framecnt); + } +#if 0 + if (ntohl(pkthdr->framecnt) > framenum) { + printf("Newer Fragment !!! (got: %d, exp: %d) Dropping it\n", ntohl(pkthdr->framecnt), framenum); + continue; + } +#endif + + // copy the payload into the packet buffer... + if ((ntohl(pkthdr->fragment_nr) < fragment_number) && (ntohl(pkthdr->fragment_nr) >= 0)) { + if ((ntohl(pkthdr->fragment_nr) * fragment_payload_size + rcv_len - sizeof(jacknet_packet_header)) <= (pkt_size - sizeof(jacknet_packet_header))) { + memcpy(packet_bufX + (ntohl(pkthdr->fragment_nr) * fragment_payload_size), dataX, rcv_len - sizeof(jacknet_packet_header)); + rx_frag_count++; + } else { + printf("too long packet received..."); + } + } + } + } + return pkt_size; +} +#endif + +#if 0 +int netjack_recv(int sockfd, char *packet_buf, int pkt_size, int flags, int mtu) +{ + char *rx_packet; + char *dataX; + int i; + + int fragment_payload_size; + + char *fragment_array; + int fragment_number; + int rx_frag_count, framenum; + + jacknet_packet_header *pkthdr; + + // Now loop and send all + char *packet_bufX; + + // wait for fragment_nr == 0 + int rcv_len; + + rx_packet = alloca(mtu); + dataX = rx_packet + sizeof(jacknet_packet_header); + + fragment_payload_size = mtu - sizeof(jacknet_packet_header); + + pkthdr = (jacknet_packet_header *)rx_packet; + + packet_bufX = packet_buf + sizeof(jacknet_packet_header); + + fragment_number = (pkt_size - sizeof(jacknet_packet_header) - 1) / fragment_payload_size + 1; + fragment_array = alloca(fragment_number); + for (i = 0; i < fragment_number; i++) + fragment_array[i] = 0; + + if (pkt_size <= mtu) { + return recv(sockfd, packet_buf, pkt_size, flags); + } else { +rx_again: + rcv_len = recv(sockfd, rx_packet, mtu, flags); + if (rcv_len < 0) + return rcv_len; + + if (rcv_len >= sizeof(jacknet_packet_header)) { + if ((ntohl(pkthdr->fragment_nr)) != 0) + goto rx_again; + } else { + goto rx_again; + } + + // ok... we have read a fragement 0; + // copy the data into the packet buffer... + memcpy(packet_buf, rx_packet, mtu); + + rx_frag_count = 1; + framenum = ntohl(pkthdr->framecnt); + + fragment_array[0] = 1; + + while (rx_frag_count < fragment_number) { + + rcv_len = recv(sockfd, rx_packet, mtu, flags); + if (rcv_len < 0) + return -1; +/////////////////// + if (ntohl(pkthdr->framecnt) < framenum) { + //printf("Out of Order Framecnt: i abort on this packet\n"); + printf("Old Fragment !!! (got: %d, exp: %d)\n", ntohl(pkthdr->framecnt), framenum); + continue; + } + +#if 0 + if (ntohl(pkthdr->framecnt) > framenum) { + printf("Newer Fragment !!! (got: %d, exp: %d) Switching to new Packet.\n", ntohl(pkthdr->framecnt), framenum); + // Copy the new Packetheader up Front + memcpy(packet_buf, rx_packet, sizeof(jacknet_packet_header)); + + rx_frag_count = 0; + framenum = ntohl(pkthdr->framecnt); + } +#endif + if (ntohl(pkthdr->framecnt) > framenum) { + printf("Newer Fragment !!! (got: %d, exp: %d) Dropping it\n", ntohl(pkthdr->framecnt), framenum); + continue; + } + +///////////////////////////////// +// + // copy the payload into the packet buffer... + if ((ntohl(pkthdr->fragment_nr) < fragment_number) && (ntohl(pkthdr->fragment_nr) >= 0)) { + if ((ntohl(pkthdr->fragment_nr) * fragment_payload_size + rcv_len - sizeof(jacknet_packet_header)) <= (pkt_size - sizeof(jacknet_packet_header))) { + memcpy(packet_bufX + (ntohl(pkthdr->fragment_nr) * fragment_payload_size), dataX, rcv_len - sizeof(jacknet_packet_header)); + rx_frag_count++; + } else { + printf("too long packet received..."); + } + } + } + } + return pkt_size; +} +#endif + +void netjack_sendto(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, int addr_size, int mtu) +{ + int frag_cnt = 0; + char *tx_packet, *dataX; + jacknet_packet_header *pkthdr; + + tx_packet = alloca(mtu + 10); + dataX = tx_packet + sizeof(jacknet_packet_header); + pkthdr = (jacknet_packet_header *)tx_packet; + + int fragment_payload_size = mtu - sizeof(jacknet_packet_header); + + if (pkt_size <= mtu) { + sendto(sockfd, packet_buf, pkt_size, flags, addr, addr_size); + } else { + + // Copy the packet header to the tx pack first. + memcpy(tx_packet, packet_buf, sizeof(jacknet_packet_header)); + + // Now loop and send all + char *packet_bufX = packet_buf + sizeof(jacknet_packet_header); + + while (packet_bufX < (packet_buf + pkt_size - fragment_payload_size)) { + pkthdr->fragment_nr = htonl(frag_cnt++); + memcpy(dataX, packet_bufX, fragment_payload_size); + sendto(sockfd, tx_packet, mtu, flags, addr, addr_size); + packet_bufX += fragment_payload_size; + } + + int last_payload_size = packet_buf + pkt_size - packet_bufX; + memcpy(dataX, packet_bufX, last_payload_size); + pkthdr->fragment_nr = htonl(frag_cnt); + //printf("last fragment_count = %d, payload_size = %d\n", fragment_count, last_payload_size); + + // sendto(last_pack_size); + sendto(sockfd, tx_packet, last_payload_size + sizeof(jacknet_packet_header), flags, addr, addr_size); + } +} + +// render functions for float +void render_payload_to_jack_ports_float( void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) +{ + channel_t chn = 0; + JSList *node = capture_ports; + JSList *src_node = capture_srcs; + + uint32_t *packet_bufX = (uint32_t *)packet_payload; + + while (node != NULL) { + int i; + int_float_t val; + SRC_DATA src; + + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + + if (net_period_down != nframes) { + SRC_STATE *src_state = src_node->data; + for (i = 0; i < net_period_down; i++) { + packet_bufX[i] = ntohl(packet_bufX[i]); + } + + src.data_in = (float *)packet_bufX; + src.input_frames = net_period_down; + + src.data_out = buf; + src.output_frames = nframes; + + src.src_ratio = (float) nframes / (float) net_period_down; + src.end_of_input = 0; + + src_set_ratio(src_state, src.src_ratio); + src_process(src_state, &src); + src_node = jack_slist_next (src_node); + } else { + for (i = 0; i < net_period_down; i++) { + val.i = packet_bufX[i]; + val.i = ntohl(val.i); + buf[i] = val.f; + } + } + + packet_bufX = (packet_bufX + net_period_down); + node = jack_slist_next (node); + chn++; + } +} + +void render_jack_ports_to_payload_float(JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) +{ + channel_t chn = 0; + JSList *node = playback_ports; + JSList *src_node = playback_srcs; + + uint32_t *packet_bufX = (uint32_t *)packet_payload; + + while (node != NULL) { + SRC_DATA src; + int i; + int_float_t val; + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + + if (net_period_up != nframes) { + SRC_STATE *src_state = src_node->data; + src.data_in = buf; + src.input_frames = nframes; + + src.data_out = (float *) packet_bufX; + src.output_frames = net_period_up; + + src.src_ratio = (float) net_period_up / (float) nframes; + src.end_of_input = 0; + + src_set_ratio(src_state, src.src_ratio); + src_process(src_state, &src); + + for (i = 0; i < net_period_up; i++) { + packet_bufX[i] = htonl(packet_bufX[i]); + } + src_node = jack_slist_next (src_node); + } else { + for (i = 0; i < net_period_up; i++) { + val.f = buf[i]; + val.i = htonl(val.i); + packet_bufX[i] = val.i; + } + } + + packet_bufX = (packet_bufX + net_period_up); + node = jack_slist_next (node); + chn++; + } +} + +// render functions for 16bit +void render_payload_to_jack_ports_16bit(void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) +{ + channel_t chn = 0; + JSList *node = capture_ports; + JSList *src_node = capture_srcs; + + uint16_t *packet_bufX = (uint16_t *)packet_payload; + + while (node != NULL) { + int i; + //uint32_t val; + SRC_DATA src; + + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + + float *floatbuf = alloca(sizeof(float) * net_period_down); + + if (net_period_down != nframes) { + SRC_STATE *src_state = src_node->data; + for (i = 0; i < net_period_down; i++) { + floatbuf[i] = ((float) ntohs(packet_bufX[i])) / 32767.0 - 1.0; + } + + src.data_in = floatbuf; + src.input_frames = net_period_down; + + src.data_out = buf; + src.output_frames = nframes; + + src.src_ratio = (float) nframes / (float) net_period_down; + src.end_of_input = 0; + + src_set_ratio(src_state, src.src_ratio); + src_process(src_state, &src); + src_node = jack_slist_next (src_node); + } else { + for (i = 0; i < net_period_down; i++) { + buf[i] = ((float) ntohs(packet_bufX[i])) / 32768.0 - 1.0; + } + } + + packet_bufX = (packet_bufX + net_period_down); + node = jack_slist_next (node); + chn++; + } +} + +void render_jack_ports_to_payload_16bit(JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) +{ + channel_t chn = 0; + JSList *node = playback_ports; + JSList *src_node = playback_srcs; + + uint16_t *packet_bufX = (uint16_t *)packet_payload; + + while (node != NULL) { + SRC_DATA src; + int i; + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + + if (net_period_up != nframes) { + SRC_STATE *src_state = src_node->data; + + float *floatbuf = alloca(sizeof(float) * net_period_up); + + src.data_in = buf; + src.input_frames = nframes; + + src.data_out = floatbuf; + src.output_frames = net_period_up; + + src.src_ratio = (float) net_period_up / (float) nframes; + src.end_of_input = 0; + + src_set_ratio(src_state, src.src_ratio); + src_process(src_state, &src); + + for (i = 0; i < net_period_up; i++) { + packet_bufX[i] = htons((floatbuf[i] + 1.0) * 32767.0); + } + src_node = jack_slist_next (src_node); + } else { + for (i = 0; i < net_period_up; i++) { + packet_bufX[i] = htons((buf[i] + 1.0) * 32767.0); + } + } + + packet_bufX = (packet_bufX + net_period_up); + node = jack_slist_next (node); + chn++; + } +} + +// render functions for 8bit +void render_payload_to_jack_ports_8bit(void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) +{ + channel_t chn = 0; + JSList *node = capture_ports; + JSList *src_node = capture_srcs; + + int8_t *packet_bufX = (int8_t *)packet_payload; + + while (node != NULL) { + int i; + //uint32_t val; + SRC_DATA src; + + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + + float *floatbuf = alloca(sizeof(float) * net_period_down); + + if (net_period_down != nframes) { + SRC_STATE *src_state = src_node->data; + for (i = 0; i < net_period_down; i++) { + floatbuf[i] = ((float) packet_bufX[i]) / 127.0; + } + + src.data_in = floatbuf; + src.input_frames = net_period_down; + + src.data_out = buf; + src.output_frames = nframes; + + src.src_ratio = (float) nframes / (float) net_period_down; + src.end_of_input = 0; + + src_set_ratio(src_state, src.src_ratio); + src_process(src_state, &src); + src_node = jack_slist_next (src_node); + } else { + for (i = 0; i < net_period_down; i++) { + buf[i] = ((float) packet_bufX[i]) / 127.0; + } + } + + packet_bufX = (packet_bufX + net_period_down); + node = jack_slist_next (node); + chn++; + } +} + +void render_jack_ports_to_payload_8bit(JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) +{ + channel_t chn = 0; + JSList *node = playback_ports; + JSList *src_node = playback_srcs; + + int8_t *packet_bufX = (int8_t *)packet_payload; + + while (node != NULL) { + SRC_DATA src; + int i; + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + + if (net_period_up != nframes) { + SRC_STATE *src_state = src_node->data; + + float *floatbuf = alloca(sizeof(float) * net_period_up); + + src.data_in = buf; + src.input_frames = nframes; + + src.data_out = floatbuf; + src.output_frames = net_period_up; + + src.src_ratio = (float) net_period_up / (float) nframes; + src.end_of_input = 0; + + src_set_ratio(src_state, src.src_ratio); + src_process(src_state, &src); + + for (i = 0; i < net_period_up; i++) { + packet_bufX[i] = floatbuf[i] * 127.0; + } + src_node = jack_slist_next (src_node); + } else { + for (i = 0; i < net_period_up; i++) { + packet_bufX[i] = buf[i] * 127.0; + } + } + + packet_bufX = (packet_bufX + net_period_up); + node = jack_slist_next (node); + chn++; + } +} + +// wrapper functions with bitdepth argument... +void render_payload_to_jack_ports(int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) +{ + if (bitdepth == 8) + render_payload_to_jack_ports_8bit(packet_payload, net_period_down, capture_ports, capture_srcs, nframes); + else if (bitdepth == 16) + render_payload_to_jack_ports_16bit(packet_payload, net_period_down, capture_ports, capture_srcs, nframes); + else + render_payload_to_jack_ports_float(packet_payload, net_period_down, capture_ports, capture_srcs, nframes); +} + +void render_jack_ports_to_payload (int bitdepth, JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) +{ + if (bitdepth == 8) + render_jack_ports_to_payload_8bit(playback_ports, playback_srcs, nframes, packet_payload, net_period_up); + else if (bitdepth == 16) + render_jack_ports_to_payload_16bit(playback_ports, playback_srcs, nframes, packet_payload, net_period_up); + else + render_jack_ports_to_payload_float(playback_ports, playback_srcs, nframes, packet_payload, net_period_up); +} diff --git a/drivers/netjack/netjack_packet.h b/drivers/netjack/netjack_packet.h new file mode 100644 index 0000000..e9be23a --- /dev/null +++ b/drivers/netjack/netjack_packet.h @@ -0,0 +1,121 @@ + +/* + * NetJack - Packet Handling functions + * + * used by the driver and the jacknet_client + * + * Copyright (C) 2006 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. + * + * $Id: net_driver.c,v 1.16 2006/03/20 19:41:37 torbenh Exp $ + * + */ + +#ifndef __JACK_NET_PACKET_H__ +#define __JACK_NET_PACKET_H__ + +#include +#include +#include + +#include +// The Packet Header. + +typedef struct _jacknet_packet_header jacknet_packet_header; + +struct _jacknet_packet_header +{ + // General AutoConf Data + jack_nframes_t channels; + jack_nframes_t period_size; + jack_nframes_t sample_rate; + + // Transport Sync + jack_nframes_t sync_state; + jack_nframes_t transport_frame; + jack_nframes_t transport_state; + + // Packet loss Detection, and latency reduction + jack_nframes_t framecnt; + jack_nframes_t latency; + jack_nframes_t reply_port; + + jack_nframes_t mtu; + jack_nframes_t fragment_nr; +}; + +typedef union _int_float int_float_t; + +union _int_float +{ + uint32_t i; + float f; +}; + +// fragment reorder cache. +typedef struct _cache_packet cache_packet; + +struct _cache_packet +{ + int valid; + int num_fragments; + int packet_size; + int mtu; + jack_nframes_t framecnt; + char * fragment_array; + char * packet_buf; +}; + +typedef struct _packet_cache packet_cache; + +struct _packet_cache +{ + int size; + cache_packet *packets; +}; + +extern packet_cache *global_packcache; + +// fragment cache function prototypes +packet_cache *packet_cache_new(int num_packets, int pkt_size, int mtu); +void packet_cache_free(packet_cache *pkt_cache); + +cache_packet *packet_cache_get_packet(packet_cache *pkt_cache, jack_nframes_t framecnt); +cache_packet *packet_cache_get_oldest_packet(packet_cache *pkt_cache); +cache_packet *packet_cache_get_free_packet(packet_cache *pkt_cache); + +void cache_packet_reset(cache_packet *pack); +void cache_packet_set_framecnt(cache_packet *pack, jack_nframes_t framecnt); +void cache_packet_add_fragment(cache_packet *pack, char *packet_buf, int rcv_len); +int cache_packet_is_complete(cache_packet *pack); + +// Function Prototypes + +void netjack_sendto(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, int addr_size, int mtu); +int netjack_recvfrom(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, socklen_t *addr_size, int mtu); +int netjack_recv(int sockfd, char *packet_buf, int pkt_size, int flags, int mtu); + +int get_sample_size(int bitdepth); +void packet_header_hton(jacknet_packet_header *pkthdr); + +void packet_header_ntoh(jacknet_packet_header *pkthdr); + +void render_payload_to_jack_ports(int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes); + +void render_jack_ports_to_payload(int bitdepth, JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up); + +#endif + diff --git a/drivers/netjack/netjack_packet_noReOrder.c b/drivers/netjack/netjack_packet_noReOrder.c new file mode 100644 index 0000000..5d0a426 --- /dev/null +++ b/drivers/netjack/netjack_packet_noReOrder.c @@ -0,0 +1,595 @@ + +/* + * NetJack - Packet Handling functions + * + * used by the driver and the jacknet_client + * + * Copyright (C) 2006 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. + * + * $Id: net_driver.c,v 1.16 2006/03/20 19:41:37 torbenh Exp $ + * + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include "net_driver.h" +#include "netjack_packet.h" + +int fraggo = 0; + +void +packet_header_hton(jacknet_packet_header *pkthdr) +{ + pkthdr->channels = htonl(pkthdr->channels); + pkthdr->period_size = htonl(pkthdr->period_size); + pkthdr->sample_rate = htonl(pkthdr->sample_rate); + pkthdr->sync_state = htonl(pkthdr->sync_state); + pkthdr->transport_frame = htonl(pkthdr->transport_frame); + pkthdr->transport_state = htonl(pkthdr->transport_state); + pkthdr->framecnt = htonl(pkthdr->framecnt); + pkthdr->latency = htonl(pkthdr->latency); + pkthdr->reply_port = htonl(pkthdr->reply_port); + pkthdr->mtu = htonl(pkthdr->mtu); + pkthdr->fragment_nr = htonl(pkthdr->fragment_nr); +} + +void +packet_header_ntoh(jacknet_packet_header *pkthdr) +{ + pkthdr->channels = ntohl(pkthdr->channels); + pkthdr->period_size = ntohl(pkthdr->period_size); + pkthdr->sample_rate = ntohl(pkthdr->sample_rate); + pkthdr->sync_state = ntohl(pkthdr->sync_state); + pkthdr->transport_frame = ntohl(pkthdr->transport_frame); + pkthdr->transport_state = ntohl(pkthdr->transport_state); + pkthdr->framecnt = ntohl(pkthdr->framecnt); + pkthdr->latency = ntohl(pkthdr->latency); + pkthdr->reply_port = ntohl(pkthdr->reply_port); + pkthdr->mtu = ntohl(pkthdr->mtu); + pkthdr->fragment_nr = ntohl(pkthdr->fragment_nr); +} + +int get_sample_size(int bitdepth) +{ + if (bitdepth == 8) + return sizeof(int8_t); + if (bitdepth == 16) + return sizeof(int16_t); + + return sizeof(int32_t); +} + + +// fragmented packet IO + +int netjack_recvfrom(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, socklen_t *addr_size, int mtu) +{ + char *rx_packet; + char *dataX; + + int fragment_payload_size; + + // Copy the packet header to the tx pack first. + //memcpy(tx_packet, packet_buf, sizeof(jacknet_packet_header)); + + jacknet_packet_header *pkthdr; + + // Now loop and send all + char *packet_bufX; + + // wait for fragment_nr == 0 + int rcv_len; + + rx_packet = alloca(mtu); + dataX = rx_packet + sizeof(jacknet_packet_header); + + fragment_payload_size = mtu - sizeof(jacknet_packet_header); + + // Copy the packet header to the tx pack first. + //memcpy(tx_packet, packet_buf, sizeof(jacknet_packet_header)); + + pkthdr = (jacknet_packet_header *)rx_packet; + + // Now loop and send all + packet_bufX = packet_buf + sizeof(jacknet_packet_header); + + + if (pkt_size <= mtu) + { + return recvfrom(sockfd, packet_buf, pkt_size, flags, addr, addr_size); + } else + { +rx_again: + rcv_len = recvfrom(sockfd, rx_packet, mtu, 0, addr, addr_size); + if (rcv_len < 0) + return rcv_len; + + if (rcv_len >= sizeof(jacknet_packet_header)) { + //printf("got fragmentooooo_nr = %d recv_len = %d\n", ntohl(pkthdr->fragment_nr), rcv_len); + if ((ntohl(pkthdr->fragment_nr)) != 0) + goto rx_again; + } else { + goto rx_again; + } + //goto rx_again; + + //printf("ok... lets go...\n"); + // ok... we have read a fragement 0; + // copy the packet header... + memcpy(packet_buf, rx_packet, sizeof(jacknet_packet_header)); + + int fragment_count = 0; + + while (packet_bufX <= (packet_buf + pkt_size - fragment_payload_size)) { + + //printf("enter loop: fragment_count = %d, pkthdr->fragment_nr = %d\n", fragment_count, pkthdr->fragment_nr); + // check fragment number. + if ((ntohl(pkthdr->fragment_nr)) != fragment_count) { + printf("got unexpected fragment %d (expected %d)\n", ntohl(pkthdr->fragment_nr), fragment_count); + return sizeof(jacknet_packet_header) + (fragment_count) * fragment_payload_size; + } else + //printf("expected fragment %d\n", fragment_count); + + // copy the payload into the packet buffer... + memcpy(packet_bufX, dataX, fragment_payload_size); + + rcv_len = recvfrom(sockfd, rx_packet, mtu, 0, addr, addr_size); + //printf("got fragmen_nr = %d rcv_len = %d\n", ntohl(pkthdr->fragment_nr), rcv_len); + //printf("got fragmen_nr = %d\n", ntohl(pkthdr->fragment_nr)); + if (rcv_len < 0) + return -1; + + packet_bufX += fragment_payload_size; + fragment_count++; + } + + //printf("at the end rcv_len = %d\n ", rcv_len); + int last_payload_size = packet_bufX - packet_buf - pkt_size; + memcpy(packet_bufX, dataX, rcv_len - sizeof(jacknet_packet_header)); + } + return pkt_size; +} + +int netjack_recv(int sockfd, char *packet_buf, int pkt_size, int flags, int mtu) +{ + if (pkt_size <= mtu) { + return recv(sockfd, packet_buf, pkt_size, flags); + } else { + char *rx_packet = alloca(mtu); + char *dataX = rx_packet + sizeof(jacknet_packet_header); + + int fragment_payload_size = mtu - sizeof(jacknet_packet_header); + jacknet_packet_header *pkthdr = (jacknet_packet_header *)rx_packet; + + // Now loop and send all + char *packet_bufX = packet_buf + sizeof(jacknet_packet_header); + + // wait for fragment_nr == 0 + int rcv_len; +rx_again: + rcv_len = recv(sockfd, rx_packet, mtu, flags); + if (rcv_len < 0) + return rcv_len; + + if (rcv_len >= sizeof(jacknet_packet_header)) { + //printf("got fragmentooo_nr = %d\n", ntohl(pkthdr->fragment_nr)); + if (ntohl(pkthdr->fragment_nr) != 0) + goto rx_again; + } else { + goto rx_again; + } + + //printf("ok we got a fragment 0\n"); + // ok... we have read a fragement 0; + // copy the packet header... + memcpy(packet_buf, rx_packet, sizeof(jacknet_packet_header)); + + int fragment_count = 0; + + while (packet_bufX <= (packet_buf + pkt_size - fragment_payload_size)) { + + // check fragment number. + if (ntohl(pkthdr->fragment_nr) != fragment_count) { + printf("got unexpected fragment %d (expected %d)\n", ntohl(pkthdr->fragment_nr), fragment_count); + return sizeof(jacknet_packet_header) + (fragment_count - 1) * fragment_payload_size; + } + + // copy the payload into the packet buffer... + memcpy(packet_bufX, dataX, fragment_payload_size); + + rcv_len = recv(sockfd, rx_packet, mtu, flags); + //printf("got fragmen_nr = %d rcv_len = %d\n", ntohl(pkthdr->fragment_nr), rcv_len); + if (rcv_len < 0) + return -1; + + packet_bufX += fragment_payload_size; + fragment_count++; + } + + //int last_payload_size = packet_bufX - packet_buf - pkt_size; + //memcpy(packet_bufX, dataX, rcv_len - sizeof(jacknet_packet_header)); + } + return pkt_size; +} + +void netjack_sendto(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, int addr_size, int mtu) +{ + int frag_cnt = 0; + char *tx_packet, *dataX; + jacknet_packet_header *pkthdr; + + tx_packet = alloca(mtu + 10); + dataX = tx_packet + sizeof(jacknet_packet_header); + pkthdr = (jacknet_packet_header *)tx_packet; + + int fragment_payload_size = mtu - sizeof(jacknet_packet_header); + + if (pkt_size <= mtu) { + sendto(sockfd, packet_buf, pkt_size, flags, addr, addr_size); + } else { + + // Copy the packet header to the tx pack first. + memcpy(tx_packet, packet_buf, sizeof(jacknet_packet_header)); + + // Now loop and send all + char *packet_bufX = packet_buf + sizeof(jacknet_packet_header); + + + while (packet_bufX < (packet_buf + pkt_size - fragment_payload_size)) { + pkthdr->fragment_nr = htonl(frag_cnt++); + memcpy(dataX, packet_bufX, fragment_payload_size); + + int err = sendto(sockfd, tx_packet, mtu, flags, addr, addr_size); + + packet_bufX += fragment_payload_size; + } + + int last_payload_size = packet_buf + pkt_size - packet_bufX; + memcpy(dataX, packet_bufX, last_payload_size); + pkthdr->fragment_nr = htonl(frag_cnt); + //printf("last fragment_count = %d, payload_size = %d\n", fragment_count, last_payload_size); + + // sendto(last_pack_size); + sendto(sockfd, tx_packet, last_payload_size + sizeof(jacknet_packet_header), flags, addr, addr_size); + } +} + +// render functions for float +void render_payload_to_jack_ports_float( void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) +{ + channel_t chn = 0; + JSList *node = capture_ports; + JSList *src_node = capture_srcs; + + uint32_t *packet_bufX = (uint32_t *)packet_payload; + + while (node != NULL) { + int i; + int_float_t val; + SRC_DATA src; + + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + + if (net_period_down != nframes) { + SRC_STATE *src_state = src_node->data; + for (i = 0; i < net_period_down; i++) { + packet_bufX[i] = ntohl(packet_bufX[i]); + } + + src.data_in = (float *)packet_bufX; + src.input_frames = net_period_down; + + src.data_out = buf; + src.output_frames = nframes; + + src.src_ratio = (float) nframes / (float) net_period_down; + src.end_of_input = 0; + + src_set_ratio(src_state, src.src_ratio); + src_process(src_state, &src); + src_node = jack_slist_next (src_node); + } else { + for (i = 0; i < net_period_down; i++) { + val.i = packet_bufX[i]; + val.i = ntohl(val.i); + buf[i] = val.f; + } + } + + packet_bufX = (packet_bufX + net_period_down); + node = jack_slist_next (node); + chn++; + } +} + +void render_jack_ports_to_payload_float (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) +{ + channel_t chn = 0; + JSList *node = playback_ports; + JSList *src_node = playback_srcs; + + uint32_t *packet_bufX = (uint32_t *)packet_payload; + + while (node != NULL) { + SRC_DATA src; + int i; + int_float_t val; + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + + if (net_period_up != nframes) { + SRC_STATE *src_state = src_node->data; + src.data_in = buf; + src.input_frames = nframes; + + src.data_out = (float *) packet_bufX; + src.output_frames = net_period_up; + + src.src_ratio = (float) net_period_up / (float) nframes; + src.end_of_input = 0; + + src_set_ratio(src_state, src.src_ratio); + src_process(src_state, &src); + + for (i = 0; i < net_period_up; i++) { + packet_bufX[i] = htonl(packet_bufX[i]); + } + src_node = jack_slist_next (src_node); + } else { + for (i = 0; i < net_period_up; i++) { + val.f = buf[i]; + val.i = htonl(val.i); + packet_bufX[i] = val.i; + } + } + + packet_bufX = (packet_bufX + net_period_up); + node = jack_slist_next (node); + chn++; + } +} + +// render functions for 16bit +void render_payload_to_jack_ports_16bit( void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) +{ + channel_t chn = 0; + JSList *node = capture_ports; + JSList *src_node = capture_srcs; + + uint16_t *packet_bufX = (uint16_t *)packet_payload; + + while (node != NULL) { + int i; + //uint32_t val; + SRC_DATA src; + + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + + float *floatbuf = alloca(sizeof(float) * net_period_down); + + if (net_period_down != nframes) { + SRC_STATE *src_state = src_node->data; + for (i = 0; i < net_period_down; i++) { + floatbuf[i] = ((float) ntohs(packet_bufX[i])) / 32767.0 - 1.0; + } + + src.data_in = floatbuf; + src.input_frames = net_period_down; + + src.data_out = buf; + src.output_frames = nframes; + + src.src_ratio = (float) nframes / (float) net_period_down; + src.end_of_input = 0; + + src_set_ratio(src_state, src.src_ratio); + src_process(src_state, &src); + src_node = jack_slist_next (src_node); + } else { + for (i = 0; i < net_period_down; i++) { + buf[i] = ((float) ntohs(packet_bufX[i])) / 32768.0 - 1.0; + } + } + + packet_bufX = (packet_bufX + net_period_down); + node = jack_slist_next (node); + chn++; + } +} + +void render_jack_ports_to_payload_16bit (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) +{ + channel_t chn = 0; + JSList *node = playback_ports; + JSList *src_node = playback_srcs; + + uint16_t *packet_bufX = (uint16_t *)packet_payload; + + while (node != NULL) { + SRC_DATA src; + int i; + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + + if (net_period_up != nframes) { + SRC_STATE *src_state = src_node->data; + + float *floatbuf = alloca(sizeof(float) * net_period_up); + + src.data_in = buf; + src.input_frames = nframes; + + src.data_out = floatbuf; + src.output_frames = net_period_up; + + src.src_ratio = (float) net_period_up / (float) nframes; + src.end_of_input = 0; + + src_set_ratio(src_state, src.src_ratio); + src_process(src_state, &src); + + for (i = 0; i < net_period_up; i++) { + packet_bufX[i] = htons((floatbuf[i] + 1.0) * 32767.0); + } + src_node = jack_slist_next (src_node); + } else { + for (i = 0; i < net_period_up; i++) { + packet_bufX[i] = htons((buf[i] + 1.0) * 32767.0); + } + } + + packet_bufX = (packet_bufX + net_period_up); + node = jack_slist_next (node); + chn++; + } +} + +// render functions for 8bit + +void render_payload_to_jack_ports_8bit(void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) +{ + channel_t chn = 0; + JSList *node = capture_ports; + JSList *src_node = capture_srcs; + + int8_t *packet_bufX = (int8_t *)packet_payload; + + while (node != NULL) { + int i; + //uint32_t val; + SRC_DATA src; + + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + + float *floatbuf = alloca(sizeof(float) * net_period_down); + + if (net_period_down != nframes) { + SRC_STATE *src_state = src_node->data; + for (i = 0; i < net_period_down; i++) { + floatbuf[i] = ((float) packet_bufX[i]) / 127.0; + } + + src.data_in = floatbuf; + src.input_frames = net_period_down; + + src.data_out = buf; + src.output_frames = nframes; + + src.src_ratio = (float) nframes / (float) net_period_down; + src.end_of_input = 0; + + src_set_ratio(src_state, src.src_ratio); + src_process(src_state, &src); + src_node = jack_slist_next (src_node); + } else { + for (i = 0; i < net_period_down; i++) { + buf[i] = ((float) packet_bufX[i]) / 127.0; + } + } + + packet_bufX = (packet_bufX + net_period_down); + node = jack_slist_next (node); + chn++; + } +} + +void render_jack_ports_to_payload_8bit(JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) +{ + channel_t chn = 0; + JSList *node = playback_ports; + JSList *src_node = playback_srcs; + + int8_t *packet_bufX = (int8_t *)packet_payload; + + while (node != NULL) { + SRC_DATA src; + int i; + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + + if (net_period_up != nframes) { + SRC_STATE *src_state = src_node->data; + + float *floatbuf = alloca(sizeof(float) * net_period_up); + + src.data_in = buf; + src.input_frames = nframes; + + src.data_out = floatbuf; + src.output_frames = net_period_up; + + src.src_ratio = (float) net_period_up / (float) nframes; + src.end_of_input = 0; + + src_set_ratio(src_state, src.src_ratio); + src_process(src_state, &src); + + for (i = 0; i < net_period_up; i++) { + packet_bufX[i] = floatbuf[i] * 127.0; + } + src_node = jack_slist_next (src_node); + } else { + for (i = 0; i < net_period_up; i++) { + packet_bufX[i] = buf[i] * 127.0; + } + } + + packet_bufX = (packet_bufX + net_period_up); + node = jack_slist_next (node); + chn++; + } +} + +// wrapper functions with bitdepth argument... +void render_payload_to_jack_ports(int bitdepth, void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) +{ + if (bitdepth == 8) + render_payload_to_jack_ports_8bit(packet_payload, net_period_down, capture_ports, capture_srcs, nframes); + else if (bitdepth == 16) + render_payload_to_jack_ports_16bit(packet_payload, net_period_down, capture_ports, capture_srcs, nframes); + else + render_payload_to_jack_ports_float(packet_payload, net_period_down, capture_ports, capture_srcs, nframes); +} + +void render_jack_ports_to_payload(int bitdepth, JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) +{ + if (bitdepth == 8) + render_jack_ports_to_payload_8bit(playback_ports, playback_srcs, nframes, packet_payload, net_period_up); + else if (bitdepth == 16) + render_jack_ports_to_payload_16bit(playback_ports, playback_srcs, nframes, packet_payload, net_period_up); + else + render_jack_ports_to_payload_float(playback_ports, playback_srcs, nframes, packet_payload, net_period_up); +}