git-svn-id: svn+ssh://jackaudio.org/trunk/jack@1095 0c269be4-1314-0410-8aa9-9f06e86f4224tags/0.115.6
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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. | |||
| @@ -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); | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -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__ */ | |||
| @@ -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); | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||