Browse Source

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
tags/0.115.6
sletz 17 years ago
parent
commit
38aaeb9920
13 changed files with 4447 additions and 3 deletions
  1. +2
    -1
      configure.ac
  2. +2
    -2
      drivers/Makefile.am
  3. +12
    -0
      drivers/netjack/Makefile.am
  4. +106
    -0
      drivers/netjack/README
  5. +378
    -0
      drivers/netjack/alsa_client.c
  6. +569
    -0
      drivers/netjack/alsa_in.c
  7. +550
    -0
      drivers/netjack/alsa_out.c
  8. +734
    -0
      drivers/netjack/net_driver.c
  9. +76
    -0
      drivers/netjack/net_driver.h
  10. +415
    -0
      drivers/netjack/net_source.c
  11. +887
    -0
      drivers/netjack/netjack_packet.c
  12. +121
    -0
      drivers/netjack/netjack_packet.h
  13. +595
    -0
      drivers/netjack/netjack_packet_noReOrder.c

+ 2
- 1
configure.ac View File

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


+ 2
- 2
drivers/Makefile.am View File

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

+ 12
- 0
drivers/netjack/Makefile.am View File

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

+ 106
- 0
drivers/netjack/README View File

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


+ 378
- 0
drivers/netjack/alsa_client.c View File

@@ -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 <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <alloca.h>

#include <jack/jack.h>

#define ALSA_PCM_OLD_HW_PARAMS_API
#define ALSA_PCM_OLD_SW_PARAMS_API
#include "alsa/asoundlib.h"

#include <samplerate.h>

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


+ 569
- 0
drivers/netjack/alsa_in.c View File

@@ -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 <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <alloca.h>
#include <math.h>

#include <jack/jack.h>
#include <jack/jslist.h>

#define ALSA_PCM_OLD_HW_PARAMS_API
#define ALSA_PCM_OLD_SW_PARAMS_API
#include "alsa/asoundlib.h"

#include <samplerate.h>

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 <jack name> - reports a different name to jack\n"
" -d <alsa_device> \n"
" -c <channels> \n"
" -p <period_size> \n"
" -n <num_period> \n"
" -r <sample_rate> \n"
" -m <max_diff> \n"
" -t <target_delay> \n"
" -f <catch_factor> \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);
}


+ 550
- 0
drivers/netjack/alsa_out.c View File

@@ -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 <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <alloca.h>
#include <math.h>

#include <jack/jack.h>
#include <jack/jslist.h>

#define ALSA_PCM_OLD_HW_PARAMS_API
#define ALSA_PCM_OLD_SW_PARAMS_API
#include "alsa/asoundlib.h"

#include <samplerate.h>

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 <jack name> - reports a different name to jack\n"
" -d <alsa_device> \n"
" -c <channels> \n"
" -p <period_size> \n"
" -n <num_period> \n"
" -r <sample_rate> \n"
" -m <max_diff> \n"
" -t <target_delay> \n"
" -f <catch_factor> \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);
}


+ 734
- 0
drivers/netjack/net_driver.c View File

@@ -0,0 +1,734 @@
/* -*- mode: c; c-file-style: "linux"; -*- */
/*
NetJack Driver

Copyright (C) 2006 Torben Hohn <torbenh@gmx.de>
Copyright (C) 2003 Robert Ham <rah@bash.sh>
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 <math.h>
#include <stdio.h>
#include <memory.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <sys/poll.h>

#include <jack/types.h>
//#include <jack/internal.h>
#include <jack/engine.h>
#include <sysdeps/time.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <samplerate.h>

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

+ 76
- 0
drivers/netjack/net_driver.h View File

@@ -0,0 +1,76 @@
/*
Copyright (C) 2003 Robert Ham <rah@bash.sh>

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 <unistd.h>

#include <jack/types.h>
#include <jack/driver.h>
#include <jack/jack.h>
#include <jack/transport.h>

#include <netinet/in.h>
#include <samplerate.h>

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__ */

+ 415
- 0
drivers/netjack/net_source.c View File

@@ -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 <stdio.h>
#include <errno.h>(#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include <netinet/in.h>
#include <netdb.h>

#include <alloca.h>

#include <jack/jack.h>

#include "net_driver.h"
#include "netjack_packet.h"
#include <samplerate.h>

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 <jack name>] [-s <socket>] [-C <num channels>] [-P <num channels>] -p <host peer>\n"
"\n"
" -n <jack name> - reports a different name to jack\n"
" -s <socket> select another socket than the default (3000).\n"
" -p <host peer> the hostname of the \"other\" machine running the jack-slave.\n"
" -P <num channels> number of playback channels.\n"
" -C <num channels> number of capture channels.\n"
" -l <latency in periods> number of packets on the wire to approach\n"
" -r <reply port> When using a firewall use this port for incoming packets\n"
" -f <downsample ratio> downsample data in the wire by this factor.\n"
" -b <bitdepth> 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);
}


+ 887
- 0
drivers/netjack/netjack_packet.c View File

@@ -0,0 +1,887 @@

/*
* NetJack - Packet Handling functions
*
* used by the driver and the jacknet_client
*
* Copyright (C) 2006 Torben Hohn <torbenh@gmx.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: net_driver.c,v 1.16 2006/03/20 19:41:37 torbenh Exp $
*
*/


#include <math.h>
#include <stdio.h>
#include <memory.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>

#include <jack/types.h>
#include <jack/engine.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <samplerate.h>

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

+ 121
- 0
drivers/netjack/netjack_packet.h View File

@@ -0,0 +1,121 @@

/*
* NetJack - Packet Handling functions
*
* used by the driver and the jacknet_client
*
* Copyright (C) 2006 Torben Hohn <torbenh@gmx.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $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 <jack/jack.h>
#include <jack/types.h>
#include <jack/engine.h>

#include <netinet/in.h>
// 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


+ 595
- 0
drivers/netjack/netjack_packet_noReOrder.c View File

@@ -0,0 +1,595 @@

/*
* NetJack - Packet Handling functions
*
* used by the driver and the jacknet_client
*
* Copyright (C) 2006 Torben Hohn <torbenh@gmx.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: net_driver.c,v 1.16 2006/03/20 19:41:37 torbenh Exp $
*
*/


#include <math.h>
#include <stdio.h>
#include <memory.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>

#include <jack/types.h>
#include <jack/engine.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <samplerate.h>

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

Loading…
Cancel
Save