From 38f5f4f7f20605f3cf31cc486fe90dff93d24e75 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Sun, 25 Oct 2009 19:57:19 +0100 Subject: [PATCH 01/20] Pure Add netone driver --- common/JackNetOneDriver.cpp | 1117 +++++++++++++++++++++++++ common/JackNetOneDriver.h | 95 +++ common/netjack.c | 687 ++++++++++++++++ common/netjack.h | 144 ++++ common/netjack_packet.c | 1527 +++++++++++++++++++++++++++++++++++ common/netjack_packet.h | 162 ++++ 6 files changed, 3732 insertions(+) create mode 100644 common/JackNetOneDriver.cpp create mode 100644 common/JackNetOneDriver.h create mode 100644 common/netjack.c create mode 100644 common/netjack.h create mode 100644 common/netjack_packet.c create mode 100644 common/netjack_packet.h diff --git a/common/JackNetOneDriver.cpp b/common/JackNetOneDriver.cpp new file mode 100644 index 00000000..b9de94de --- /dev/null +++ b/common/JackNetOneDriver.cpp @@ -0,0 +1,1117 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2008 Romain Moret at Grame + +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. +*/ + +#define HAVE_CELT 1 + +#ifdef WIN32 +#include +#endif + +#include "JackNetOneDriver.h" +#include "JackEngineControl.h" +#include "JackGraphManager.h" +#include "JackWaitThreadedDriver.h" +#include "JackException.h" +#include "driver_interface.h" + +#include "netjack.h" +#include "netjack_packet.h" + +#if HAVE_SAMPLERATE +#include "samplerate.h" +#endif + +#if HAVE_CELT +#include "celt/celt.h" +#endif + +#define MIN(x,y) ((x)<(y) ? (x) : (y)) + +using namespace std; + +namespace Jack +{ + JackNetOneDriver::JackNetOneDriver ( const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, + int port, int mtu, int capture_ports, int playback_ports, int midi_input_ports, int midi_output_ports, + int sample_rate, int period_size, int resample_factor, + char* net_name, uint transport_sync, int bitdepth, int use_autoconfig, + int latency, int redundancy, int dont_htonl_floats, int always_deadline ) + : JackAudioDriver ( name, alias, engine, table ) + { + jack_log ( "JackNetOneDriver::JackNetOneDriver port %d", port ); + +#ifdef WIN32 + WSADATA wsa; + int rc = WSAStartup(MAKEWORD(2,0),&wsa); +#endif + + netjack_init( & (this->netj), + NULL, // client + name, + capture_ports, + playback_ports, + midi_input_ports, + midi_output_ports, + sample_rate, + period_size, + port, + transport_sync, + resample_factor, + 0, + bitdepth, + use_autoconfig, + latency, + redundancy, + dont_htonl_floats, + always_deadline); + } + + JackNetOneDriver::~JackNetOneDriver() + { + // No destructor yet. + } + +//open, close, attach and detach------------------------------------------------------ + int JackNetOneDriver::Open ( jack_nframes_t buffer_size, jack_nframes_t samplerate, bool capturing, bool playing, + int inchannels, int outchannels, bool monitor, + const char* capture_driver_name, const char* playback_driver_name, + jack_nframes_t capture_latency, jack_nframes_t playback_latency ) + { + if ( JackAudioDriver::Open ( buffer_size, + samplerate, + capturing, + playing, + inchannels, + outchannels, + monitor, + capture_driver_name, + playback_driver_name, + capture_latency, + playback_latency ) == 0 ) + { + fEngineControl->fPeriod = 0; + fEngineControl->fComputation = 500 * 1000; + fEngineControl->fConstraint = 500 * 1000; + return 0; + } + else + { + jack_error( "open fail\n" ); + return -1; + } + } + + int JackNetOneDriver::Close() + { + FreePorts(); + netjack_release( &netj ); + return JackDriver::Close(); + } + + int JackNetOneDriver::Attach() + { + return 0; + } + + int JackNetOneDriver::Detach() + { + return 0; + } + + int JackNetOneDriver::AllocPorts() + { + jack_port_id_t port_id; + char buf[64]; + unsigned int chn; + int port_flags; + + + //if (netj.handle_transport_sync) + // jack_set_sync_callback(netj.client, (JackSyncCallback) net_driver_sync_cb, NULL); + + port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; + + for (chn = 0; chn < netj.capture_channels_audio; chn++) { + snprintf (buf, sizeof(buf) - 1, "system:capture_%u", chn + 1); + + if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, buf, JACK_DEFAULT_AUDIO_TYPE, + static_cast ( port_flags ), fEngineControl->fBufferSize ) ) == NO_PORT ) + { + jack_error ( "driver: cannot register port for %s", buf ); + return -1; + } + //port = fGraphManager->GetPort ( port_id ); + + netj.capture_ports = + jack_slist_append (netj.capture_ports, (void *)(intptr_t)port_id); + + if( netj.bitdepth == 1000 ) { +#if HAVE_CELT + celt_int32_t lookahead; + // XXX: memory leak + CELTMode *celt_mode = celt_mode_create( netj.sample_rate, 1, netj.period_size, NULL ); + celt_mode_info( celt_mode, CELT_GET_LOOKAHEAD, &lookahead ); + netj.codec_latency = 2*lookahead; + + netj.capture_srcs = jack_slist_append(netj.capture_srcs, (void *)celt_decoder_create( celt_mode ) ); +#endif + } else { +#if HAVE_SAMPLERATE + netj.capture_srcs = jack_slist_append(netj.capture_srcs, (void *)src_new(SRC_LINEAR, 1, NULL)); +#endif + } + } + for (chn = netj.capture_channels_audio; chn < netj.capture_channels; chn++) { + snprintf (buf, sizeof(buf) - 1, "system:capture_%u", chn + 1); + + if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, buf, JACK_DEFAULT_MIDI_TYPE, + static_cast ( port_flags ), fEngineControl->fBufferSize ) ) == NO_PORT ) + { + jack_error ( "driver: cannot register port for %s", buf ); + return -1; + } + //port = fGraphManager->GetPort ( port_id ); + + netj.capture_ports = + jack_slist_append (netj.capture_ports, (void *)(intptr_t)port_id); + } + + port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; + + for (chn = 0; chn < netj.playback_channels_audio; chn++) { + snprintf (buf, sizeof(buf) - 1, "system:playback_%u", chn + 1); + + if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, buf, JACK_DEFAULT_AUDIO_TYPE, + static_cast ( port_flags ), fEngineControl->fBufferSize ) ) == NO_PORT ) + { + jack_error ( "driver: cannot register port for %s", buf ); + return -1; + } + //port = fGraphManager->GetPort ( port_id ); + + netj.playback_ports = + jack_slist_append (netj.playback_ports, (void *)(intptr_t)port_id); + + if( netj.bitdepth == 1000 ) { +#if HAVE_CELT + // XXX: memory leak + CELTMode *celt_mode = celt_mode_create( netj.sample_rate, 1, netj.period_size, NULL ); + netj.playback_srcs = jack_slist_append(netj.playback_srcs, (void *)celt_encoder_create( celt_mode ) ); +#endif + } else { +#if HAVE_SAMPLERATE + netj.playback_srcs = jack_slist_append(netj.playback_srcs, (void *)src_new(SRC_LINEAR, 1, NULL)); +#endif + } + } + for (chn = netj.playback_channels_audio; chn < netj.playback_channels; chn++) { + snprintf (buf, sizeof(buf) - 1, "system:playback_%u", chn + 1); + + if ( ( port_id = fGraphManager->AllocatePort ( fClientControl.fRefNum, buf, JACK_DEFAULT_MIDI_TYPE, + static_cast ( port_flags ), fEngineControl->fBufferSize ) ) == NO_PORT ) + { + jack_error ( "driver: cannot register port for %s", buf ); + return -1; + } + //port = fGraphManager->GetPort ( port_id ); + + netj.playback_ports = + jack_slist_append (netj.playback_ports, (void *)(intptr_t)port_id); + } + return 0; + } + +//init and restart-------------------------------------------------------------------- + bool JackNetOneDriver::Initialize() + { + jack_log ( "JackNetOneDriver::Init()" ); + + if( global_packcache != NULL ) { + FreePorts(); + netjack_release( &netj ); + } + + //display some additional infos + jack_info ( "NetOne driver started" ); + if( netjack_startup( &netj ) ) { + return false; + } + + //register jack ports + if ( AllocPorts() != 0 ) + { + jack_error ( "Can't allocate ports." ); + return false; + } + + + //monitor + //driver parametering + JackAudioDriver::SetBufferSize ( netj.period_size ); + JackAudioDriver::SetSampleRate ( netj.sample_rate ); + + JackDriver::NotifyBufferSize ( netj.period_size ); + JackDriver::NotifySampleRate ( netj.sample_rate ); + + //transport engine parametering + fEngineControl->fTransport.SetNetworkSync ( true ); + return true; + } + + +//jack ports and buffers-------------------------------------------------------------- + +//driver processes-------------------------------------------------------------------- + int JackNetOneDriver::Read() + { + netjack_wait( &netj ); + + if( (netj.num_lost_packets * netj.period_size / netj.sample_rate) > 10 ) + throw JackNetException(); + + //netjack_read( &netj, netj.period_size ); + JackDriver::CycleTakeBeginTime(); + + jack_position_t local_trans_pos; + jack_transport_state_t local_trans_state; + + unsigned int *packet_buf, *packet_bufX; + + if( ! netj.packet_data_valid ) { + jack_log( "data not valid" ); + render_payload_to_jack_ports (netj.bitdepth, NULL, netj.net_period_down, netj.capture_ports, netj.capture_srcs, netj.period_size, netj.dont_htonl_floats ); + return 0; + } + packet_buf = netj.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); + + netj.reply_port = pkthdr->reply_port; + netj.latency = pkthdr->latency; + + // Special handling for latency=0 + if( netj.latency == 0 ) + netj.resync_threshold = 0; + else + netj.resync_threshold = MIN( 15, pkthdr->latency-1 ); + + // check whether, we should handle the transport sync stuff, or leave trnasports untouched. + if (netj.handle_transport_sync) { +#if 1 + unsigned int compensated_tranport_pos = (pkthdr->transport_frame + (pkthdr->latency * netj.period_size) + netj.codec_latency); + + // read local transport info.... + //local_trans_state = jack_transport_query(netj.client, &local_trans_pos); + + local_trans_state = fEngineControl->fTransport.Query ( &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) { + fEngineControl->fTransport.SetCommand ( TransportCommandStart ); + //jack_transport_start(netj.client); + //last_transport_state = JackTransportStopped; + netj.sync_state = 0; + jack_info("locally stopped... starting..."); + } + + if (local_trans_pos.frame != compensated_tranport_pos) + { + jack_position_t new_pos = local_trans_pos; + new_pos.frame = compensated_tranport_pos + 2*netj.period_size; + new_pos.valid = (jack_position_bits_t) 0; + + + fEngineControl->fTransport.RequestNewPos ( &new_pos ); + //jack_transport_locate(netj.client, compensated_tranport_pos); + //last_transport_state = JackTransportRolling; + netj.sync_state = 0; + jack_info("starting locate to %d", compensated_tranport_pos ); + } + break; + case JackTransportStopped: + netj.sync_state = 1; + if (local_trans_pos.frame != (pkthdr->transport_frame)) { + jack_position_t new_pos = local_trans_pos; + new_pos.frame = pkthdr->transport_frame; + new_pos.valid = (jack_position_bits_t)0; + fEngineControl->fTransport.RequestNewPos ( &new_pos ); + //jack_transport_locate(netj.client, (pkthdr->transport_frame)); + jack_info("transport is stopped locate to %d", pkthdr->transport_frame); + } + if (local_trans_state != JackTransportStopped) + //jack_transport_stop(netj.client); + fEngineControl->fTransport.SetCommand ( TransportCommandStop ); + break; + case JackTransportRolling: + netj.sync_state = 1; + // if(local_trans_pos.frame != (pkthdr->transport_frame + (pkthdr->latency) * netj.period_size)) { + // jack_transport_locate(netj.client, (pkthdr->transport_frame + (pkthdr->latency + 2) * netj.period_size)); + // jack_info("running locate to %d", pkthdr->transport_frame + (pkthdr->latency)*netj.period_size); + // } + if (local_trans_state != JackTransportRolling) + //jack_transport_start (netj.client); + fEngineControl->fTransport.SetState ( JackTransportRolling ); + + break; + + case JackTransportLooping: + break; + } +#endif + } + + render_payload_to_jack_ports (netj.bitdepth, packet_bufX, netj.net_period_down, netj.capture_ports, netj.capture_srcs, netj.period_size, netj.dont_htonl_floats ); + packet_cache_release_packet(global_packcache, netj.expected_framecnt ); + return 0; + } + + int JackNetOneDriver::Write() + { + int syncstate = netj.sync_state | ((fEngineControl->fTransport.GetState() == JackTransportNetStarting) ? 1 : 0 ); + //netjack_write( &netj, netj.period_size, 0 ); + uint32_t *packet_buf, *packet_bufX; + + int packet_size = get_sample_size(netj.bitdepth) * netj.playback_channels * netj.net_period_up + sizeof(jacknet_packet_header); + jacknet_packet_header *pkthdr; + + packet_buf = (uint32_t *) alloca(packet_size); + pkthdr = (jacknet_packet_header *)packet_buf; + + if( netj.running_free ) { + return 0; + } + + // offset packet_bufX by the packetheader. + packet_bufX = packet_buf + sizeof(jacknet_packet_header) / sizeof(jack_default_audio_sample_t); + + pkthdr->sync_state = syncstate;; + pkthdr->latency = netj.time_to_deadline; + //printf( "time to deadline = %d goodness=%d\n", (int)netj.time_to_deadline, netj.deadline_goodness ); + pkthdr->framecnt = netj.expected_framecnt; + + + render_jack_ports_to_payload(netj.bitdepth, netj.playback_ports, netj.playback_srcs, netj.period_size, packet_bufX, netj.net_period_up, netj.dont_htonl_floats ); + + packet_header_hton(pkthdr); + if (netj.srcaddress_valid) + { + unsigned int r; + +#ifdef __APPLE__ + static const int flag = 0; +#else + static const int flag = 0; +#endif + + if (netj.reply_port) + netj.syncsource_address.sin_port = htons(netj.reply_port); + + for( r=0; rdata; + node = jack_slist_remove_link( node, this_node ); + jack_slist_free_1( this_node ); + fGraphManager->ReleasePort( fClientControl.fRefNum, port_id ); + } + netj.capture_ports = NULL; + + node = netj.playback_ports; + while( node != NULL ) { + JSList *this_node = node; + jack_port_id_t port_id = (jack_port_id_t)(intptr_t) node->data; + node = jack_slist_remove_link( node, this_node ); + jack_slist_free_1( this_node ); + fGraphManager->ReleasePort( fClientControl.fRefNum, port_id ); + } + netj.playback_ports = NULL; + + if( netj.bitdepth == 1000 ) { +#if HAVE_CELT + node = netj.playback_srcs; + while( node != NULL ) { + JSList *this_node = node; + CELTEncoder *enc = (CELTEncoder *) node->data; + node = jack_slist_remove_link( node, this_node ); + jack_slist_free_1( this_node ); + celt_encoder_destroy( enc ); + } + netj.playback_srcs = NULL; + + node = netj.capture_srcs; + while( node != NULL ) { + JSList *this_node = node; + CELTDecoder *dec = (CELTDecoder *) node->data; + node = jack_slist_remove_link( node, this_node ); + jack_slist_free_1( this_node ); + celt_decoder_destroy( dec ); + } + netj.capture_srcs = NULL; +#endif + } else { +#if HAVE_SAMPLERATE + node = netj.playback_srcs; + while( node != NULL ) { + JSList *this_node = node; + SRC_STATE *state = (SRC_STATE *) node->data; + node = jack_slist_remove_link( node, this_node ); + jack_slist_free_1( this_node ); + src_delete( state ); + } + netj.playback_srcs = NULL; + + node = netj.capture_srcs; + while( node != NULL ) { + JSList *this_node = node; + SRC_STATE *state = (SRC_STATE *) node->data; + node = jack_slist_remove_link( node, this_node ); + jack_slist_free_1( this_node ); + src_delete( state ); + } + netj.capture_srcs = NULL; +#endif + } +} +//Render functions-------------------------------------------------------------------- + +// render functions for float +void +JackNetOneDriver::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, int dont_htonl_floats) +{ + uint32_t chn = 0; + JSList *node = capture_ports; +#if HAVE_SAMPLERATE + JSList *src_node = capture_srcs; +#endif + + uint32_t *packet_bufX = (uint32_t *)packet_payload; + + if( !packet_payload ) + return; + + while (node != NULL) + { + unsigned int i; + int_float_t val; +#if HAVE_SAMPLERATE + SRC_DATA src; +#endif + + jack_port_id_t port_id = (jack_port_id_t)(intptr_t) node->data; + JackPort *port = fGraphManager->GetPort( port_id ); + + jack_default_audio_sample_t* buf = + (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_id, fEngineControl->fBufferSize); + + const char *porttype = port->GetType(); + + if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + { +#if HAVE_SAMPLERATE + // audio port, resample if necessary + if (net_period_down != nframes) + { + SRC_STATE *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 +#endif + { + if( dont_htonl_floats ) + { + memcpy( buf, packet_bufX, net_period_down*sizeof(jack_default_audio_sample_t)); + } + else + { + for (i = 0; i < net_period_down; i++) + { + val.i = packet_bufX[i]; + val.i = ntohl (val.i); + buf[i] = val.f; + } + } + } + } + else if (strncmp (porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + { + // midi port, decode midi events + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_down; + uint32_t * buffer_uint32 = (uint32_t*)packet_bufX; + decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); + } + packet_bufX = (packet_bufX + net_period_down); + node = jack_slist_next (node); + chn++; + } +} + +void +JackNetOneDriver::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, int dont_htonl_floats ) +{ + uint32_t chn = 0; + JSList *node = playback_ports; +#if HAVE_SAMPLERATE + JSList *src_node = playback_srcs; +#endif + + uint32_t *packet_bufX = (uint32_t *) packet_payload; + + while (node != NULL) + { +#if HAVE_SAMPLERATE + SRC_DATA src; +#endif + unsigned int i; + int_float_t val; + jack_port_id_t port_id = (jack_port_id_t)(intptr_t) node->data; + JackPort *port = fGraphManager->GetPort( port_id ); + + jack_default_audio_sample_t* buf = + (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_id, fEngineControl->fBufferSize); + + const char *porttype = port->GetType(); + + if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + { + // audio port, resample if necessary + +#if HAVE_SAMPLERATE + if (net_period_up != nframes) { + SRC_STATE *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 +#endif + { + if( dont_htonl_floats ) + { + memcpy( packet_bufX, buf, net_period_up*sizeof(jack_default_audio_sample_t) ); + } + else + { + for (i = 0; i < net_period_up; i++) + { + val.f = buf[i]; + val.i = htonl (val.i); + packet_bufX[i] = val.i; + } + } + } + } + else if (strncmp(porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + { + // encode midi events from port to packet + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_up; + uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; + encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); + } + packet_bufX = (packet_bufX + net_period_up); + node = jack_slist_next (node); + chn++; + } +} + +#if HAVE_CELT +// render functions for celt. +void +JackNetOneDriver::render_payload_to_jack_ports_celt (void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) +{ + uint32_t chn = 0; + JSList *node = capture_ports; + JSList *src_node = capture_srcs; + + unsigned char *packet_bufX = (unsigned char *)packet_payload; + + while (node != NULL) + { + jack_port_id_t port_id = (jack_port_id_t) (intptr_t)node->data; + JackPort *port = fGraphManager->GetPort( port_id ); + + jack_default_audio_sample_t* buf = + (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_id, fEngineControl->fBufferSize); + + const char *portname = port->GetType(); + + + if (strncmp(portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + { + // audio port, decode celt data. + + CELTDecoder *decoder = (CELTDecoder *)src_node->data; + if( !packet_payload ) + celt_decode_float( decoder, NULL, net_period_down, buf ); + else + celt_decode_float( decoder, packet_bufX, net_period_down, buf ); + + src_node = jack_slist_next (src_node); + } + else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + { + // midi port, decode midi events + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_down / 2; + uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; + if( packet_payload ) + decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); + } + packet_bufX = (packet_bufX + net_period_down); + node = jack_slist_next (node); + chn++; + } +} + +void +JackNetOneDriver::render_jack_ports_to_payload_celt (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) +{ + uint32_t chn = 0; + JSList *node = playback_ports; + JSList *src_node = playback_srcs; + + unsigned char *packet_bufX = (unsigned char *)packet_payload; + + while (node != NULL) + { + jack_port_id_t port_id = (jack_port_id_t) (intptr_t) node->data; + JackPort *port = fGraphManager->GetPort( port_id ); + + jack_default_audio_sample_t* buf = + (jack_default_audio_sample_t*)fGraphManager->GetBuffer(port_id, fEngineControl->fBufferSize); + + const char *portname = port->GetType(); + + if (strncmp (portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + { + // audio port, encode celt data. + + int encoded_bytes; + float *floatbuf = (float *)alloca (sizeof(float) * nframes ); + memcpy( floatbuf, buf, nframes*sizeof(float) ); + CELTEncoder *encoder = (CELTEncoder *)src_node->data; + encoded_bytes = celt_encode_float( encoder, floatbuf, NULL, packet_bufX, net_period_up ); + if( encoded_bytes != (int)net_period_up ) + jack_error( "something in celt changed. netjack needs to be changed to handle this.\n" ); + src_node = jack_slist_next( src_node ); + } + else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + { + // encode midi events from port to packet + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_up / 2; + uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; + encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); + } + packet_bufX = (packet_bufX + net_period_up); + node = jack_slist_next (node); + chn++; + } +} + +#endif +/* Wrapper functions with bitdepth argument... */ +void +JackNetOneDriver::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, int dont_htonl_floats) +{ +#if HAVE_CELT + if (bitdepth == 1000) + render_payload_to_jack_ports_celt (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); + else +#endif + render_payload_to_jack_ports_float (packet_payload, net_period_down, capture_ports, capture_srcs, nframes, dont_htonl_floats); +} + +void +JackNetOneDriver::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, int dont_htonl_floats) +{ +#if HAVE_CELT + if (bitdepth == 1000) + render_jack_ports_to_payload_celt (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); + else +#endif + render_jack_ports_to_payload_float (playback_ports, playback_srcs, nframes, packet_payload, net_period_up, dont_htonl_floats); +} + + + +//driver loader----------------------------------------------------------------------- + +#ifdef __cplusplus + extern "C" + { +#endif + SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor () + { + jack_driver_desc_t* desc = ( jack_driver_desc_t* ) calloc ( 1, sizeof ( jack_driver_desc_t ) ); + jack_driver_param_desc_t * params; + + strcpy ( desc->name, "netone" ); // size MUST be less then JACK_DRIVER_NAME_MAX + 1 + strcpy ( desc->desc, "netjack one slave backend component" ); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1 + + desc->nparams = 17; + params = ( jack_driver_param_desc_t* ) calloc ( desc->nparams, sizeof ( jack_driver_param_desc_t ) ); + + int 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, "midi inchannels"); + params[i].character = 'I'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 1U; + strcpy (params[i].short_desc, "Number of midi capture channels (defaults to 1)"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "midi outchannels"); + params[i].character = 'O'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 1U; + strcpy (params[i].short_desc, "Number of midi playback channels (defaults to 1)"); + 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, "celt"); + params[i].character = 'c'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 0U; + strcpy (params[i].short_desc, + "sets celt encoding and number of bytes per channel"); + 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 = JackDriverParamBool; + 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); + + i++; + strcpy (params[i].name, "autoconf"); + params[i].character = 'a'; + params[i].type = JackDriverParamBool; + params[i].value.ui = 1U; + strcpy (params[i].short_desc, + "Whether to use Autoconfig, or just start."); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "latency"); + params[i].character = 'L'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 5U; + strcpy (params[i].short_desc, + "Latency setting"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "redundancy"); + params[i].character = 'R'; + params[i].type = JackDriverParamUInt; + params[i].value.ui = 1U; + strcpy (params[i].short_desc, + "Send packets N times"); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "no-htonl"); + params[i].character = 'H'; + params[i].type = JackDriverParamBool; + params[i].value.ui = 0U; + strcpy (params[i].short_desc, + "Dont convert samples to network byte order."); + strcpy (params[i].long_desc, params[i].short_desc); + + i++; + strcpy (params[i].name, "deadline"); + params[i].character = 'D'; + params[i].type = JackDriverParamBool; + params[i].value.ui = 0U; + strcpy (params[i].short_desc, + "always use deadline (recommended for internet connect)"); + strcpy (params[i].long_desc, params[i].short_desc); + + desc->params = params; + + return desc; + } + + SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize ( Jack::JackLockedEngine* engine, Jack::JackSynchro* table, 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 capture_ports_midi = 1; + unsigned int playback_ports_midi = 1; + unsigned int listen_port = 3000; + unsigned int resample_factor_up = 0; + unsigned int bitdepth = 0; + unsigned int handle_transport_sync = 1; + unsigned int use_autoconfig = 1; + unsigned int latency = 5; + unsigned int redundancy = 1; + unsigned int mtu = 1400; + int dont_htonl_floats = 0; + int always_deadline = 0; + 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 'I': + capture_ports_midi = param->value.ui; + break; + + case 'O': + playback_ports_midi = 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': +#if HAVE_SAMPLERATE + resample_factor = param->value.ui; +#else + jack_error( "not built with libsamplerate support\n" ); + exit(10); +#endif + break; + + case 'u': +#if HAVE_SAMPLERATE + resample_factor_up = param->value.ui; +#else + jack_error( "not built with libsamplerate support\n" ); + exit(10); +#endif + break; + + case 'b': + bitdepth = param->value.ui; + break; + + case 'c': +#if HAVE_CELT + bitdepth = 1000; + resample_factor = param->value.ui; +#else + jack_error( "not built with celt support\n" ); + exit(10); +#endif + break; + + case 't': + handle_transport_sync = param->value.ui; + break; + + case 'a': + use_autoconfig = param->value.ui; + break; + + case 'L': + latency = param->value.ui; + break; + + case 'R': + redundancy = param->value.ui; + break; + + case 'H': + dont_htonl_floats = param->value.ui; + break; + + case 'D': + always_deadline = param->value.ui; + break; + } + } + + try + { + + Jack::JackDriverClientInterface* driver = + new Jack::JackWaitThreadedDriver ( + new Jack::JackNetOneDriver ( "system", "net_pcm", engine, table, listen_port, mtu, + capture_ports_midi, playback_ports_midi, capture_ports, playback_ports, + sample_rate, period_size, resample_factor, + "net_pcm", handle_transport_sync, bitdepth, use_autoconfig, latency, redundancy, + dont_htonl_floats, always_deadline ) ); + + if ( driver->Open ( period_size, sample_rate, 1, 1, capture_ports, playback_ports, + 0, "from_master_", "to_master_", 0, 0 ) == 0 ) + { + return driver; + } + else + { + delete driver; + return NULL; + } + + } + catch ( ... ) + { + return NULL; + } + } + +#ifdef __cplusplus + } +#endif +} diff --git a/common/JackNetOneDriver.h b/common/JackNetOneDriver.h new file mode 100644 index 00000000..ce30e06e --- /dev/null +++ b/common/JackNetOneDriver.h @@ -0,0 +1,95 @@ +/* +Copyright (C) 2001 Paul Davis +Copyright (C) 2008 Romain Moret at Grame + +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 __JackNetDriver__ +#define __JackNetDriver__ + +#include "JackAudioDriver.h" +#include "netjack.h" + +namespace Jack +{ + /** + \Brief This class describes the Net Backend + */ + + class JackNetOneDriver : public JackAudioDriver + { + private: + netjack_driver_state_t netj; + +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, int dont_htonl_floats); +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, int dont_htonl_floats ); +#ifdef HAVE_CELT +void +render_payload_to_jack_ports_celt (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_celt (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up); +#endif +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, int dont_htonl_floats); +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, int dont_htonl_floats); + + public: + JackNetOneDriver ( const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table, + int port, int mtu, int capture_ports, int playback_ports, int midi_input_ports, int midi_output_ports, + int sample_rate, int period_size, int resample_factor, + char* net_name, uint transport_sync, int bitdepth, int use_autoconfig, + int latency, int redundancy, int dont_htonl_floats, int always_deadline ); + ~JackNetOneDriver(); + + int Open ( jack_nframes_t frames_per_cycle, jack_nframes_t rate, bool capturing, bool playing, + int inchannels, int outchannels, bool monitor, const char* capture_driver_name, + const char* playback_driver_name, jack_nframes_t capture_latency, jack_nframes_t playback_latency ); + + int Close(); + int Attach(); + int Detach(); + + int Read(); + int Write(); + + bool Initialize(); + int AllocPorts(); + void FreePorts(); + + // BufferSize can't be changed + bool IsFixedBufferSize() + { + return true; + } + + int SetBufferSize ( jack_nframes_t buffer_size ) + { + return -1; + } + + int SetSampleRate ( jack_nframes_t sample_rate ) + { + return -1; + } + + }; +} + +#endif diff --git a/common/netjack.c b/common/netjack.c new file mode 100644 index 00000000..83436d7a --- /dev/null +++ b/common/netjack.c @@ -0,0 +1,687 @@ + +/* -*- mode: c; c-file-style: "linux"; -*- */ +/* +NetJack Abstraction. + +Copyright (C) 2008 Pieter Palmers +Copyright (C) 2006 Torben Hohn +Copyright (C) 2003 Robert Ham +Copyright (C) 2001 Paul Davis + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +$Id: net_driver.c,v 1.17 2006/04/16 20:16:10 torbenh Exp $ +*/ + +#define HAVE_CELT 1 + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "jack/jslist.h" + +#include + +#ifdef WIN32 +#include +#include +#else +#include +#include +#endif + +#include "netjack.h" + +//#include "config.h" +#if HAVE_SAMPLERATE +#include +#endif + +#if HAVE_CELT +#include +#endif + +#include "netjack.h" +#include "netjack_packet.h" + +// JACK2 +#include "jack/control.h" + +#define MIN(x,y) ((x)<(y) ? (x) : (y)) + +static int sync_state = 1; +static jack_transport_state_t last_transport_state; + +static int +net_driver_sync_cb(jack_transport_state_t state, jack_position_t *pos, void *data) +{ + int retval = sync_state; + + if (state == JackTransportStarting && last_transport_state != JackTransportStarting) { + retval = 0; + } +// if (state == JackTransportStarting) +// jack_info("Starting sync_state = %d", sync_state); + last_transport_state = state; + return retval; +} + +void netjack_wait( netjack_driver_state_t *netj ) +{ + int we_have_the_expected_frame = 0; + jack_nframes_t next_frame_avail; + jack_time_t packet_recv_time_stamp; + jacknet_packet_header *pkthdr; + + if( !netj->next_deadline_valid ) { + if( netj->latency == 0 ) + // for full sync mode... always wait for packet. + netj->next_deadline = jack_get_time() + 50*netj->period_usecs; + else if( netj->latency == 1 ) + // for normal 1 period latency mode, only 1 period for dealine. + netj->next_deadline = jack_get_time() + 110 * netj->period_usecs /100; + else + // looks like waiting 1 period always is correct. + // not 100% sure yet. with the improved resync, it might be better, + // to have more than one period headroom for high latency. + //netj->next_deadline = jack_get_time() + 5*netj->latency*netj->period_usecs/4; + netj->next_deadline = jack_get_time() + netj->period_usecs + 10*netj->latency*netj->period_usecs/100; + + netj->next_deadline_valid = 1; + } else { + netj->next_deadline += netj->period_usecs; + } + + // Increment expected frame here. + + netj->expected_framecnt += 1; + + //jack_log( "expect %d", netj->expected_framecnt ); + // Now check if required packet is already in the cache. + // then poll (have deadline calculated) + // then drain socket, rinse and repeat. + while(1) { + if( packet_cache_get_next_available_framecnt( global_packcache, netj->expected_framecnt, &next_frame_avail) ) { + if( next_frame_avail == netj->expected_framecnt ) { + we_have_the_expected_frame = 1; + if( !netj->always_deadline ) + break; + } + } + if( ! netjack_poll_deadline( netj->sockfd, netj->next_deadline ) ) { + break; + } + + packet_cache_drain_socket( global_packcache, netj->sockfd ); + } + + // check if we know who to send our packets too. + if (!netj->srcaddress_valid) + if( global_packcache->master_address_valid ) { + memcpy (&(netj->syncsource_address), &(global_packcache->master_address), sizeof( struct sockaddr_in ) ); + netj->srcaddress_valid = 1; + } + + // XXX: switching mode unconditionally is stupid. + // if we were running free perhaps we like to behave differently + // ie. fastforward one packet etc. + // well... this is the first packet we see. hmm.... dunno ;S + // it works... so... + netj->running_free = 0; + + if( !we_have_the_expected_frame ) + jack_log( "xrun... %d", netj->expected_framecnt ); + + if( we_have_the_expected_frame ) { + netj->time_to_deadline = netj->next_deadline - jack_get_time() - netj->period_usecs; + packet_cache_retreive_packet_pointer( global_packcache, netj->expected_framecnt, (char **) &(netj->rx_buf), netj->rx_bufsize , &packet_recv_time_stamp); + pkthdr = (jacknet_packet_header *) netj->rx_buf; + packet_header_ntoh(pkthdr); + netj->deadline_goodness = (int)pkthdr->sync_state; + netj->packet_data_valid = 1; + + // TODO: Queue state could be taken into account. + // But needs more processing, cause, when we are running as + // fast as we can, recv_time_offset can be zero, which is + // good. + // need to add (now-deadline) and check that. + /* + if( recv_time_offset < netj->period_usecs ) + //netj->next_deadline -= netj->period_usecs*netj->latency/100; + netj->next_deadline += netj->period_usecs/1000; + */ + + if( netj->deadline_goodness < (netj->period_usecs/4+10*(int)netj->period_usecs*netj->latency/100) ) { + netj->next_deadline += netj->period_usecs/100; + //jack_log( "goodness: %d, Adjust deadline: --- %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); + } + if( netj->deadline_goodness > (netj->period_usecs/4+10*(int)netj->period_usecs*netj->latency/100) ) { + netj->next_deadline -= netj->period_usecs/100; + //jack_log( "goodness: %d, Adjust deadline: +++ %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); + } + } else { + netj->time_to_deadline = 0; + // bah... the packet is not there. + // either + // - it got lost. + // - its late + // - sync source is not sending anymore. + + // lets check if we have the next packets, we will just run a cycle without data. + // in that case. + + if( packet_cache_get_next_available_framecnt( global_packcache, netj->expected_framecnt, &next_frame_avail) ) + { + jack_nframes_t offset = next_frame_avail - netj->expected_framecnt; + + //XXX: hmm... i need to remember why resync_threshold wasnt right. + //if( offset < netj->resync_threshold ) + if( offset < 10 ) { + // ok. dont do nothing. we will run without data. + // this seems to be one or 2 lost packets. + // + // this can also be reordered packet jitter. + // (maybe this is not happening in real live) + // but it happens in netem. + + netj->packet_data_valid = 0; + + // I also found this happening, when the packet queue, is too full. + // but wtf ? use a smaller latency. this link can handle that ;S + if( packet_cache_get_fill( global_packcache, netj->expected_framecnt ) > 80.0 ) + netj->next_deadline -= netj->period_usecs/2; + + + } else { + // the diff is too high. but we have a packet in the future. + // lets resync. + netj->expected_framecnt = next_frame_avail; + packet_cache_retreive_packet_pointer( global_packcache, netj->expected_framecnt, (char **) &(netj->rx_buf), netj->rx_bufsize, NULL ); + pkthdr = (jacknet_packet_header *) netj->rx_buf; + packet_header_ntoh(pkthdr); + //netj->deadline_goodness = 0; + netj->deadline_goodness = (int)pkthdr->sync_state - (int)netj->period_usecs * offset; + netj->next_deadline_valid = 0; + netj->packet_data_valid = 1; + } + + } else { + // no packets in buffer. + netj->packet_data_valid = 0; + + //printf( "frame %d No Packet in queue. num_lost_packets = %d \n", netj->expected_framecnt, netj->num_lost_packets ); + if( netj->num_lost_packets < 5 ) { + // ok. No Packet in queue. The packet was either lost, + // or we are running too fast. + // + // Adjusting the deadline unconditionally resulted in + // too many xruns on master. + // But we need to adjust for the case we are running too fast. + // So lets check if the last packet is there now. + // + // It would not be in the queue anymore, if it had been + // retrieved. This might break for redundancy, but + // i will make the packet cache drop redundant packets, + // that have already been retreived. + // + if( packet_cache_get_highest_available_framecnt( global_packcache, &next_frame_avail) ) { + if( next_frame_avail == (netj->expected_framecnt - 1) ) { + // Ok. the last packet is there now. + // and it had not been retrieved. + // + // TODO: We are still dropping 2 packets. + // perhaps we can adjust the deadline + // when (num_packets lost == 0) + + // This might still be too much. + netj->next_deadline += netj->period_usecs; + } + } + } else if( (netj->num_lost_packets <= 100) ) { + // lets try adjusting the deadline harder, for some packets, we might have just ran 2 fast. + netj->next_deadline += netj->period_usecs*netj->latency/8; + } else { + + // But now we can check for any new frame available. + // + if( packet_cache_get_highest_available_framecnt( global_packcache, &next_frame_avail) ) { + netj->expected_framecnt = next_frame_avail; + packet_cache_retreive_packet_pointer( global_packcache, netj->expected_framecnt, (char **) &(netj->rx_buf), netj->rx_bufsize, NULL ); + pkthdr = (jacknet_packet_header *) netj->rx_buf; + packet_header_ntoh(pkthdr); + netj->deadline_goodness = pkthdr->sync_state; + netj->next_deadline_valid = 0; + netj->packet_data_valid = 1; + netj->running_free = 0; + jack_info( "resync after freerun... %d\n", netj->expected_framecnt ); + } else { + // give up. lets run freely. + // XXX: hmm... + + netj->running_free = 1; + + // when we really dont see packets. + // reset source address. and open possibility for new master. + // maybe dsl reconnect. Also restart of netsource without fix + // reply address changes port. + if (netj->num_lost_packets > 200 ) { + netj->srcaddress_valid = 0; + packet_cache_reset_master_address( global_packcache ); + } + } + } + } + } + + if( !netj->packet_data_valid ) + netj->num_lost_packets += 1; + else { + netj->num_lost_packets = 0; + } +} + +void netjack_send_silence( netjack_driver_state_t *netj, int syncstate ) +{ + int tx_size = get_sample_size(netj->bitdepth) * netj->playback_channels * netj->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 *)netj->rx_buf; + + //framecnt = rx_pkthdr->framecnt; + + netj->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 = syncstate; + tx_pkthdr->framecnt = netj->expected_framecnt; + + // memset 0 the payload. + int payload_size = get_sample_size(netj->bitdepth) * netj->playback_channels * netj->net_period_up; + memset(packet_bufX, 0, payload_size); + + packet_header_hton(tx_pkthdr); + if (netj->srcaddress_valid) + { + int r; + if (netj->reply_port) + netj->syncsource_address.sin_port = htons(netj->reply_port); + + for( r=0; rredundancy; r++ ) + netjack_sendto(netj->outsockfd, (char *)packet_buf, tx_size, + 0, (struct sockaddr*)&(netj->syncsource_address), sizeof(struct sockaddr_in), netj->mtu); + } +} + + +void netjack_attach( netjack_driver_state_t *netj ) +{ + //puts ("net_driver_attach"); + jack_port_t * port; + char buf[32]; + unsigned int chn; + int port_flags; + + + if (netj->handle_transport_sync) + jack_set_sync_callback(netj->client, (JackSyncCallback) net_driver_sync_cb, NULL); + + port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; + + for (chn = 0; chn < netj->capture_channels_audio; chn++) { + snprintf (buf, sizeof(buf) - 1, "capture_%u", chn + 1); + + port = jack_port_register (netj->client, buf, + JACK_DEFAULT_AUDIO_TYPE, + port_flags, 0); + if (!port) { + jack_error ("NET: cannot register port for %s", buf); + break; + } + + netj->capture_ports = + jack_slist_append (netj->capture_ports, port); + + if( netj->bitdepth == 1000 ) { +#if HAVE_CELT + celt_int32_t lookahead; + // XXX: memory leak + CELTMode *celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL ); + celt_mode_info( celt_mode, CELT_GET_LOOKAHEAD, &lookahead ); + netj->codec_latency = 2*lookahead; + + netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( celt_mode ) ); +#endif + } else { +#if HAVE_SAMPLERATE + netj->capture_srcs = jack_slist_append(netj->capture_srcs, src_new(SRC_LINEAR, 1, NULL)); +#endif + } + } + for (chn = netj->capture_channels_audio; chn < netj->capture_channels; chn++) { + snprintf (buf, sizeof(buf) - 1, "capture_%u", chn + 1); + + port = jack_port_register (netj->client, buf, + JACK_DEFAULT_MIDI_TYPE, + port_flags, 0); + if (!port) { + jack_error ("NET: cannot register port for %s", buf); + break; + } + + netj->capture_ports = + jack_slist_append (netj->capture_ports, port); + } + + port_flags = JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal; + + for (chn = 0; chn < netj->playback_channels_audio; chn++) { + snprintf (buf, sizeof(buf) - 1, "playback_%u", chn + 1); + + port = jack_port_register (netj->client, buf, + JACK_DEFAULT_AUDIO_TYPE, + port_flags, 0); + + if (!port) { + jack_error ("NET: cannot register port for %s", buf); + break; + } + + netj->playback_ports = + jack_slist_append (netj->playback_ports, port); + if( netj->bitdepth == 1000 ) { +#if HAVE_CELT + // XXX: memory leak + CELTMode *celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL ); + netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_encoder_create( celt_mode ) ); +#endif + } else { +#if HAVE_SAMPLERATE + netj->playback_srcs = jack_slist_append(netj->playback_srcs, src_new(SRC_LINEAR, 1, NULL)); +#endif + } + } + for (chn = netj->playback_channels_audio; chn < netj->playback_channels; chn++) { + snprintf (buf, sizeof(buf) - 1, "playback_%u", chn + 1); + + port = jack_port_register (netj->client, buf, + JACK_DEFAULT_MIDI_TYPE, + port_flags, 0); + + if (!port) { + jack_error ("NET: cannot register port for %s", buf); + break; + } + + netj->playback_ports = + jack_slist_append (netj->playback_ports, port); + } + + jack_activate (netj->client); +} + + +void netjack_detach( netjack_driver_state_t *netj ) +{ + JSList * node; + + + for (node = netj->capture_ports; node; node = jack_slist_next (node)) + jack_port_unregister (netj->client, + ((jack_port_t *) node->data)); + + jack_slist_free (netj->capture_ports); + netj->capture_ports = NULL; + + for (node = netj->playback_ports; node; node = jack_slist_next (node)) + jack_port_unregister (netj->client, + ((jack_port_t *) node->data)); + + jack_slist_free (netj->playback_ports); + netj->playback_ports = NULL; +} + + +netjack_driver_state_t *netjack_init (netjack_driver_state_t *netj, + jack_client_t * client, + const char *name, + unsigned int capture_ports, + unsigned int playback_ports, + unsigned int capture_ports_midi, + unsigned int playback_ports_midi, + 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, + unsigned int use_autoconfig, + unsigned int latency, + unsigned int redundancy, + int dont_htonl_floats, + int always_deadline) +{ + + // Fill in netj values. + // might be subject to autoconfig... + // so dont calculate anything with them... + + + netj->sample_rate = sample_rate; + netj->period_size = period_size; + netj->dont_htonl_floats = dont_htonl_floats; + + netj->listen_port = listen_port; + + netj->capture_channels = capture_ports + capture_ports_midi; + netj->capture_channels_audio = capture_ports; + netj->capture_channels_midi = capture_ports_midi; + netj->capture_ports = NULL; + netj->playback_channels = playback_ports + playback_ports_midi; + netj->playback_channels_audio = playback_ports; + netj->playback_channels_midi = playback_ports_midi; + netj->playback_ports = NULL; + netj->codec_latency = 0; + + netj->handle_transport_sync = transport_sync; + netj->mtu = 1400; + netj->latency = latency; + netj->redundancy = redundancy; + netj->use_autoconfig = use_autoconfig; + netj->always_deadline = always_deadline; + + + netj->client = client; + + + if ((bitdepth != 0) && (bitdepth != 8) && (bitdepth != 16) && (bitdepth != 1000)) + { + jack_info ("Invalid bitdepth: %d (8, 16 or 0 for float) !!!", bitdepth); + return NULL; + } + netj->bitdepth = bitdepth; + + + if (resample_factor_up == 0) + resample_factor_up = resample_factor; + + netj->resample_factor = resample_factor; + netj->resample_factor_up = resample_factor_up; + + + return netj; +} + +void netjack_release( netjack_driver_state_t *netj ) +{ + close( netj->sockfd ); + close( netj->outsockfd ); + + packet_cache_free( global_packcache ); + global_packcache = NULL; +} + +int +netjack_startup( netjack_driver_state_t *netj ) +{ + int first_pack_len; + struct sockaddr_in address; + // Now open the socket, and wait for the first packet to arrive... + netj->sockfd = socket (AF_INET, SOCK_DGRAM, 0); +#ifdef WIN32 + if (netj->sockfd == INVALID_SOCKET) +#else + if (netj->sockfd == -1) +#endif + { + jack_info ("socket error"); + return -1; + } + address.sin_family = AF_INET; + address.sin_port = htons(netj->listen_port); + address.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind (netj->sockfd, (struct sockaddr *) &address, sizeof (address)) < 0) + { + jack_info("bind error"); + return -1; + } + + netj->outsockfd = socket (AF_INET, SOCK_DGRAM, 0); +#ifdef WIN32 + if (netj->outsockfd == INVALID_SOCKET) +#else + if (netj->outsockfd == -1) +#endif + { + jack_info ("socket error"); + return -1; + } + netj->srcaddress_valid = 0; + if (netj->use_autoconfig) + { + jacknet_packet_header *first_packet = alloca (sizeof (jacknet_packet_header)); +#ifdef WIN32 + int address_size = sizeof( struct sockaddr_in ); +#else + socklen_t address_size = sizeof (struct sockaddr_in); +#endif + //jack_info ("Waiting for an incoming packet !!!"); + //jack_info ("*** IMPORTANT *** Dont connect a client to jackd until the driver is attached to a clock source !!!"); + + while(1) { + first_pack_len = recvfrom (netj->sockfd, (char *)first_packet, sizeof (jacknet_packet_header), 0, (struct sockaddr*) & netj->syncsource_address, &address_size); +#ifdef WIN32 + if( first_pack_len == -1 ) { + first_pack_len = sizeof(jacknet_packet_header); + break; + } +#else + if (first_pack_len == sizeof (jacknet_packet_header)) + break; +#endif + } + netj->srcaddress_valid = 1; + + if (first_pack_len == sizeof (jacknet_packet_header)) + { + packet_header_ntoh (first_packet); + + jack_info ("AutoConfig Override !!!"); + if (netj->sample_rate != first_packet->sample_rate) + { + jack_info ("AutoConfig Override: Master JACK sample rate = %d", first_packet->sample_rate); + netj->sample_rate = first_packet->sample_rate; + } + + if (netj->period_size != first_packet->period_size) + { + jack_info ("AutoConfig Override: Master JACK period size is %d", first_packet->period_size); + netj->period_size = first_packet->period_size; + } + if (netj->capture_channels_audio != first_packet->capture_channels_audio) + { + jack_info ("AutoConfig Override: capture_channels_audio = %d", first_packet->capture_channels_audio); + netj->capture_channels_audio = first_packet->capture_channels_audio; + } + if (netj->capture_channels_midi != first_packet->capture_channels_midi) + { + jack_info ("AutoConfig Override: capture_channels_midi = %d", first_packet->capture_channels_midi); + netj->capture_channels_midi = first_packet->capture_channels_midi; + } + if (netj->playback_channels_audio != first_packet->playback_channels_audio) + { + jack_info ("AutoConfig Override: playback_channels_audio = %d", first_packet->playback_channels_audio); + netj->playback_channels_audio = first_packet->playback_channels_audio; + } + if (netj->playback_channels_midi != first_packet->playback_channels_midi) + { + jack_info ("AutoConfig Override: playback_channels_midi = %d", first_packet->playback_channels_midi); + netj->playback_channels_midi = first_packet->playback_channels_midi; + } + + netj->mtu = first_packet->mtu; + jack_info ("MTU is set to %d bytes", first_packet->mtu); + netj->latency = first_packet->latency; + } + } + netj->capture_channels = netj->capture_channels_audio + netj->capture_channels_midi; + netj->playback_channels = netj->playback_channels_audio + netj->playback_channels_midi; + + // After possible Autoconfig: do all calculations... + netj->period_usecs = + (jack_time_t) floor ((((float) netj->period_size) / (float)netj->sample_rate) + * 1000000.0f); + + if( netj->bitdepth == 1000 ) { + // celt mode. + // TODO: this is a hack. But i dont want to change the packet header. + netj->net_period_down = netj->resample_factor; + netj->net_period_up = netj->resample_factor_up; + } else { + netj->net_period_down = (float) netj->period_size / (float) netj->resample_factor; + netj->net_period_up = (float) netj->period_size / (float) netj->resample_factor_up; + } + + netj->rx_bufsize = sizeof (jacknet_packet_header) + netj->net_period_down * netj->capture_channels * get_sample_size (netj->bitdepth); + netj->pkt_buf = malloc (netj->rx_bufsize); + global_packcache = packet_cache_new (netj->latency + 50, netj->rx_bufsize, netj->mtu); + + netj->expected_framecnt_valid = 0; + netj->num_lost_packets = 0; + netj->next_deadline_valid = 0; + netj->deadline_goodness = 0; + netj->time_to_deadline = 0; + + // Special handling for latency=0 + if( netj->latency == 0 ) + netj->resync_threshold = 0; + else + netj->resync_threshold = MIN( 15, netj->latency-1 ); + + netj->running_free = 0; + + return 0; +} diff --git a/common/netjack.h b/common/netjack.h new file mode 100644 index 00000000..db30377a --- /dev/null +++ b/common/netjack.h @@ -0,0 +1,144 @@ + +/* + Copyright (C) 2003 Robert Ham + Copyright (C) 2005 Torben Hohn + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __NETJACK_H__ +#define __NETJACK_H__ + +#include + +#include +//#include +#include +#include + +#include "jack/jslist.h" + +//#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct _netjack_driver_state netjack_driver_state_t; + +struct _netjack_driver_state { + 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; + jack_time_t period_usecs; + int dont_htonl_floats; + int always_deadline; + + jack_nframes_t codec_latency; + + unsigned int listen_port; + + unsigned int capture_channels; + unsigned int playback_channels; + unsigned int capture_channels_audio; + unsigned int playback_channels_audio; + unsigned int capture_channels_midi; + unsigned int playback_channels_midi; + + JSList *capture_ports; + JSList *playback_ports; + JSList *playback_srcs; + JSList *capture_srcs; + + jack_client_t *client; + +#ifdef WIN32 + SOCKET sockfd; + SOCKET outsockfd; +#else + int sockfd; + int outsockfd; +#endif + + 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 rx_bufsize; + //unsigned int tx_bufsize; + unsigned int mtu; + unsigned int latency; + unsigned int redundancy; + + jack_nframes_t expected_framecnt; + int expected_framecnt_valid; + unsigned int num_lost_packets; + jack_time_t next_deadline; + int next_deadline_valid; + int packet_data_valid; + int resync_threshold; + int running_free; + int deadline_goodness; + jack_time_t time_to_deadline; + unsigned int use_autoconfig; + unsigned int resample_factor; + unsigned int resample_factor_up; +}; + +void netjack_wait( netjack_driver_state_t *netj ); +void netjack_send_silence( netjack_driver_state_t *netj, int syncstate ); +void netjack_read( netjack_driver_state_t *netj, jack_nframes_t nframes ) ; +void netjack_write( netjack_driver_state_t *netj, jack_nframes_t nframes, int syncstate ); +void netjack_attach( netjack_driver_state_t *netj ); +void netjack_detach( netjack_driver_state_t *netj ); + +netjack_driver_state_t *netjack_init (netjack_driver_state_t *netj, + jack_client_t * client, + const char *name, + unsigned int capture_ports, + unsigned int playback_ports, + unsigned int capture_ports_midi, + unsigned int playback_ports_midi, + 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, + unsigned int use_autoconfig, + unsigned int latency, + unsigned int redundancy, + int dont_htonl_floats, + int always_deadline); + +void netjack_release( netjack_driver_state_t *netj ); +int netjack_startup( netjack_driver_state_t *netj ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/common/netjack_packet.c b/common/netjack_packet.c new file mode 100644 index 00000000..cd51dca9 --- /dev/null +++ b/common/netjack_packet.c @@ -0,0 +1,1527 @@ + +/* + * NetJack - Packet Handling functions + * + * used by the driver and the jacknet_client + * + * Copyright (C) 2008 Marc-Olivier Barre + * Copyright (C) 2008 Pieter Palmers + * Copyright (C) 2006 Torben Hohn + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: net_driver.c,v 1.16 2006/03/20 19:41:37 torbenh Exp $ + * + */ + +//#include "config.h" + +#define HAVE_CELT 1 + +#define _XOPEN_SOURCE 600 +#define _BSD_SOURCE + +#ifdef __APPLE__ +#define _DARWIN_C_SOURCE +#endif + +#if HAVE_PPOLL +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +//#include + +#include + +#ifdef WIN32 +#include +#include +#else +#include +#include +#include +#endif + +#include +#include + +#if HAVE_SAMPLERATE +#include +#endif + +#if HAVE_CELT +#include +#endif + +#include "netjack_packet.h" + +// JACK2 specific. +#include "jack/control.h" + +#ifdef NO_JACK_ERROR +#define jack_error printf +#endif + +int fraggo = 0; + +packet_cache *global_packcache = NULL; + +void +packet_header_hton (jacknet_packet_header *pkthdr) +{ + pkthdr->capture_channels_audio = htonl(pkthdr->capture_channels_audio); + pkthdr->playback_channels_audio = htonl(pkthdr->playback_channels_audio); + pkthdr->capture_channels_midi = htonl(pkthdr->capture_channels_midi); + pkthdr->playback_channels_midi = htonl(pkthdr->playback_channels_midi); + 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->capture_channels_audio = ntohl(pkthdr->capture_channels_audio); + pkthdr->playback_channels_audio = ntohl(pkthdr->playback_channels_audio); + pkthdr->capture_channels_midi = ntohl(pkthdr->capture_channels_midi); + pkthdr->playback_channels_midi = ntohl(pkthdr->playback_channels_midi); + 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); + if( bitdepth == 1000 ) + return sizeof( unsigned char ); + 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 i, fragment_number; + + if( pkt_size == sizeof(jacknet_packet_header) ) + fragment_number = 1; + else + fragment_number = (pkt_size - sizeof (jacknet_packet_header) - 1) / fragment_payload_size + 1; + + packet_cache *pcache = malloc (sizeof (packet_cache)); + if (pcache == NULL) + { + jack_error ("could not allocate packet cache (1)\n"); + return NULL; + } + + pcache->size = num_packets; + pcache->packets = malloc (sizeof (cache_packet) * num_packets); + pcache->master_address_valid = 0; + pcache->last_framecnt_retreived = 0; + pcache->last_framecnt_retreived_valid = 0; + + if (pcache->packets == NULL) + { + jack_error ("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)) + { + jack_error ("could not allocate packet cache (3)\n"); + return NULL; + } + } + pcache->mtu = mtu; + + return pcache; +} + +void +packet_cache_free (packet_cache *pcache) +{ + int i; + if( pcache == NULL ) + return; + + 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); + //printf( "Dropping %d from Cache :S\n", retval->framecnt ); + cache_packet_reset (retval); + cache_packet_set_framecnt (retval, framecnt); + + return retval; +} + +// TODO: fix wrapping case... need to pass +// current expected frame here. +// +// or just save framecount into packet_cache. + +cache_packet +*packet_cache_get_oldest_packet (packet_cache *pcache) +{ + jack_nframes_t minimal_frame = JACK_MAX_FRAMES; + 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) + { + jack_error ("errror. framecnts dont match\n"); + return; + } + + + if (fragment_nr == 0) + { + memcpy (pack->packet_buf, packet_buf, rcv_len); + 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 + jack_error ("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 0; + + return 1; +} + +#ifndef WIN32 +// new poll using nanoseconds resolution and +// not waiting forever. +int +netjack_poll_deadline (int sockfd, jack_time_t deadline) +{ + struct pollfd fds; + int i, poll_err = 0; + sigset_t sigmask; + struct sigaction action; +#if HAVE_PPOLL + struct timespec timeout_spec = { 0, 0 }; +#else + sigset_t rsigmask; + int timeout; +#endif + + + jack_time_t now = jack_get_time(); + if( now >= deadline ) + return 0; + +#if HAVE_PPOLL + timeout_spec.tv_nsec = (deadline - now) * 1000; +#else + timeout = lrintf( (float)(deadline - now) / 1000.0 ); +#endif + + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGHUP); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGQUIT); + sigaddset(&sigmask, SIGPIPE); + sigaddset(&sigmask, SIGTERM); + sigaddset(&sigmask, SIGUSR1); + sigaddset(&sigmask, SIGUSR2); + + action.sa_handler = SIG_DFL; + action.sa_mask = sigmask; + action.sa_flags = SA_RESTART; + + for (i = 1; i < NSIG; i++) + if (sigismember (&sigmask, i)) + sigaction (i, &action, 0); + + fds.fd = sockfd; + fds.events = POLLIN; + +#if HAVE_PPOLL + poll_err = ppoll (&fds, 1, &timeout_spec, &sigmask); +#else + sigprocmask (SIG_UNBLOCK, &sigmask, &rsigmask); + poll_err = poll (&fds, 1, timeout); + sigprocmask (SIG_SETMASK, &rsigmask, NULL); +#endif + + if (poll_err == -1) + { + switch (errno) + { + case EBADF: + jack_error ("Error %d: An invalid file descriptor was given in one of the sets", errno); + break; + case EFAULT: + jack_error ("Error %d: The array given as argument was not contained in the calling program's address space", errno); + break; + case EINTR: + jack_error ("Error %d: A signal occurred before any requested event", errno); + break; + case EINVAL: + jack_error ("Error %d: The nfds value exceeds the RLIMIT_NOFILE value", errno); + break; + case ENOMEM: + jack_error ("Error %d: There was no space to allocate file descriptor tables", errno); + break; + } + } + return poll_err; +} + +int +netjack_poll (int sockfd, int timeout) +{ + struct pollfd fds; + int i, poll_err = 0; + sigset_t sigmask, rsigmask; + struct sigaction action; + + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGHUP); + sigaddset(&sigmask, SIGINT); + sigaddset(&sigmask, SIGQUIT); + sigaddset(&sigmask, SIGPIPE); + sigaddset(&sigmask, SIGTERM); + sigaddset(&sigmask, SIGUSR1); + sigaddset(&sigmask, SIGUSR2); + + action.sa_handler = SIG_DFL; + action.sa_mask = sigmask; + action.sa_flags = SA_RESTART; + + for (i = 1; i < NSIG; i++) + if (sigismember (&sigmask, i)) + sigaction (i, &action, 0); + + fds.fd = sockfd; + fds.events = POLLIN; + + sigprocmask(SIG_UNBLOCK, &sigmask, &rsigmask); + while (poll_err == 0) + { + poll_err = poll (&fds, 1, timeout); + } + sigprocmask(SIG_SETMASK, &rsigmask, NULL); + + if (poll_err == -1) + { + switch (errno) + { + case EBADF: + jack_error ("Error %d: An invalid file descriptor was given in one of the sets", errno); + break; + case EFAULT: + jack_error ("Error %d: The array given as argument was not contained in the calling program's address space", errno); + break; + case EINTR: + jack_error ("Error %d: A signal occurred before any requested event", errno); + break; + case EINVAL: + jack_error ("Error %d: The nfds value exceeds the RLIMIT_NOFILE value", errno); + break; + case ENOMEM: + jack_error ("Error %d: There was no space to allocate file descriptor tables", errno); + break; + } + return 0; + } + return 1; +} + +#else +int +netjack_poll (int sockfd, int timeout) +{ + jack_error( "netjack_poll not implemented\n" ); + return 0; +} +int +netjack_poll_deadline (int sockfd, jack_time_t deadline) +{ + fd_set fds; + FD_ZERO( &fds ); + FD_SET( sockfd, &fds ); + + struct timeval timeout; + while( 1 ) { + jack_time_t now = jack_get_time(); + if( now >= deadline ) + return 0; + + int timeout_usecs = (deadline - now); + //jack_error( "timeout = %d", timeout_usecs ); + timeout.tv_sec = 0; + timeout.tv_usec = (timeout_usecs < 500) ? 500 : timeout_usecs; + + int poll_err = select (0, &fds, NULL, NULL, &timeout); + if( poll_err != 0 ) + return poll_err; + } + + return 0; +} +#endif +// This now reads all a socket has into the cache. +// replacing netjack_recv functions. + +void +packet_cache_drain_socket( packet_cache *pcache, int sockfd ) +{ + char *rx_packet = alloca (pcache->mtu); + jacknet_packet_header *pkthdr = (jacknet_packet_header *) rx_packet; + int rcv_len; + jack_nframes_t framecnt; + cache_packet *cpack; + struct sockaddr_in sender_address; +#ifdef WIN32 + size_t senderlen = sizeof( struct sockaddr_in ); + u_long parm = 1; + ioctlsocket( sockfd, FIONBIO, &parm ); +#else + socklen_t senderlen = sizeof( struct sockaddr_in ); +#endif + while (1) + { +#ifdef WIN32 + rcv_len = recvfrom (sockfd, rx_packet, pcache->mtu, 0, + (struct sockaddr*) &sender_address, &senderlen); +#else + rcv_len = recvfrom (sockfd, rx_packet, pcache->mtu, MSG_DONTWAIT, + (struct sockaddr*) &sender_address, &senderlen); +#endif + if (rcv_len < 0) + return; + + if (pcache->master_address_valid) { + // Verify its from our master. + if (memcmp (&sender_address, &(pcache->master_address), senderlen) != 0) + continue; + } else { + // Setup this one as master + //printf( "setup master...\n" ); + memcpy ( &(pcache->master_address), &sender_address, senderlen ); + pcache->master_address_valid = 1; + } + + framecnt = ntohl (pkthdr->framecnt); + if( pcache->last_framecnt_retreived_valid && (framecnt <= pcache->last_framecnt_retreived )) + continue; + + cpack = packet_cache_get_packet (global_packcache, framecnt); + cache_packet_add_fragment (cpack, rx_packet, rcv_len); + cpack->recv_timestamp = jack_get_time(); + } +} + +void +packet_cache_reset_master_address( packet_cache *pcache ) +{ + pcache->master_address_valid = 0; + pcache->last_framecnt_retreived = 0; + pcache->last_framecnt_retreived_valid = 0; +} + +void +packet_cache_clear_old_packets (packet_cache *pcache, jack_nframes_t framecnt ) +{ + int i; + + for (i = 0; i < pcache->size; i++) + { + if (pcache->packets[i].valid && (pcache->packets[i].framecnt < framecnt)) + { + cache_packet_reset (&(pcache->packets[i])); + } + } +} + +int +packet_cache_retreive_packet_pointer( packet_cache *pcache, jack_nframes_t framecnt, char **packet_buf, int pkt_size, jack_time_t *timestamp ) +{ + int i; + cache_packet *cpack = NULL; + + + for (i = 0; i < pcache->size; i++) { + if (pcache->packets[i].valid && (pcache->packets[i].framecnt == framecnt)) { + cpack = &(pcache->packets[i]); + break; + } + } + + if( cpack == NULL ) { + //printf( "retreive packet: %d....not found\n", framecnt ); + return -1; + } + + if( !cache_packet_is_complete( cpack ) ) { + return -1; + } + + // ok. cpack is the one we want and its complete. + *packet_buf = cpack->packet_buf; + if( timestamp ) + *timestamp = cpack->recv_timestamp; + + pcache->last_framecnt_retreived_valid = 1; + pcache->last_framecnt_retreived = framecnt; + + return pkt_size; +} + +int +packet_cache_release_packet( packet_cache *pcache, jack_nframes_t framecnt ) +{ + int i; + cache_packet *cpack = NULL; + + + for (i = 0; i < pcache->size; i++) { + if (pcache->packets[i].valid && (pcache->packets[i].framecnt == framecnt)) { + cpack = &(pcache->packets[i]); + break; + } + } + + if( cpack == NULL ) { + //printf( "retreive packet: %d....not found\n", framecnt ); + return -1; + } + + if( !cache_packet_is_complete( cpack ) ) { + return -1; + } + + cache_packet_reset (cpack); + packet_cache_clear_old_packets( pcache, framecnt ); + + return 0; +} +float +packet_cache_get_fill( packet_cache *pcache, jack_nframes_t expected_framecnt ) +{ + int num_packets_before_us = 0; + int i; + + for (i = 0; i < pcache->size; i++) + { + cache_packet *cpack = &(pcache->packets[i]); + if (cpack->valid && cache_packet_is_complete( cpack )) + if( cpack->framecnt >= expected_framecnt ) + num_packets_before_us += 1; + } + + return 100.0 * (float)num_packets_before_us / (float)( pcache->size ) ; +} + +// Returns 0 when no valid packet is inside the cache. +int +packet_cache_get_next_available_framecnt( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ) +{ + int i; + jack_nframes_t best_offset = JACK_MAX_FRAMES/2-1; + int retval = 0; + + for (i = 0; i < pcache->size; i++) + { + cache_packet *cpack = &(pcache->packets[i]); + //printf( "p%d: valid=%d, frame %d\n", i, cpack->valid, cpack->framecnt ); + + if (!cpack->valid || !cache_packet_is_complete( cpack )) { + //printf( "invalid\n" ); + continue; + } + + if( (cpack->framecnt - expected_framecnt) > best_offset ) { + continue; + } + + best_offset = cpack->framecnt - expected_framecnt; + retval = 1; + + if( best_offset == 0 ) + break; + } + if( retval && framecnt ) + *framecnt = expected_framecnt + best_offset; + + return retval; +} + +int +packet_cache_get_highest_available_framecnt( packet_cache *pcache, jack_nframes_t *framecnt ) +{ + int i; + jack_nframes_t best_value = 0; + int retval = 0; + + for (i = 0; i < pcache->size; i++) + { + cache_packet *cpack = &(pcache->packets[i]); + //printf( "p%d: valid=%d, frame %d\n", i, cpack->valid, cpack->framecnt ); + + if (!cpack->valid || !cache_packet_is_complete( cpack )) { + //printf( "invalid\n" ); + continue; + } + + if (cpack->framecnt < best_value) { + continue; + } + + best_value = cpack->framecnt; + retval = 1; + + } + if( retval && framecnt ) + *framecnt = best_value; + + return retval; +} + +// Returns 0 when no valid packet is inside the cache. +int +packet_cache_find_latency( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ) +{ + int i; + jack_nframes_t best_offset = 0; + int retval = 0; + + for (i = 0; i < pcache->size; i++) + { + cache_packet *cpack = &(pcache->packets[i]); + //printf( "p%d: valid=%d, frame %d\n", i, cpack->valid, cpack->framecnt ); + + if (!cpack->valid || !cache_packet_is_complete( cpack )) { + //printf( "invalid\n" ); + continue; + } + + if( (cpack->framecnt - expected_framecnt) < best_offset ) { + continue; + } + + best_offset = cpack->framecnt - expected_framecnt; + retval = 1; + + if( best_offset == 0 ) + break; + } + if( retval && framecnt ) + *framecnt = JACK_MAX_FRAMES - best_offset; + + return retval; +} +// fragmented packet IO +int +netjack_recvfrom (int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, size_t *addr_size, int mtu) +{ + int retval; + socklen_t from_len = *addr_size; + if (pkt_size <= mtu) { + retval = recvfrom (sockfd, packet_buf, pkt_size, flags, addr, &from_len); + *addr_size = from_len; + return retval; + } + + char *rx_packet = alloca (mtu); + jacknet_packet_header *pkthdr = (jacknet_packet_header *) rx_packet; + int rcv_len; + jack_nframes_t framecnt; + cache_packet *cpack; + do + { + rcv_len = recvfrom (sockfd, rx_packet, mtu, 0, addr, &from_len); + 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); + } while (!cache_packet_is_complete (cpack)); + memcpy (packet_buf, cpack->packet_buf, pkt_size); + cache_packet_reset (cpack); + *addr_size = from_len; + 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); + char *rx_packet = alloca (mtu); + jacknet_packet_header *pkthdr = (jacknet_packet_header *) rx_packet; + int rcv_len; + jack_nframes_t framecnt; + cache_packet *cpack; + do + { + 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); + } while (!cache_packet_is_complete (cpack)); + memcpy (packet_buf, cpack->packet_buf, pkt_size); + cache_packet_reset (cpack); + 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) { + int err; + pkthdr = (jacknet_packet_header *) packet_buf; + pkthdr->fragment_nr = htonl (0); + err = sendto(sockfd, packet_buf, pkt_size, flags, addr, addr_size); + if( err<0 ) { + //printf( "error in send\n" ); + perror( "send" ); + } + } + else + { + int err; + // 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); + //jack_log("last fragment_count = %d, payload_size = %d\n", fragment_count, last_payload_size); + + // sendto(last_pack_size); + err = sendto(sockfd, tx_packet, last_payload_size + sizeof(jacknet_packet_header), flags, addr, addr_size); + if( err<0 ) { + //printf( "error in send\n" ); + perror( "send" ); + } + } +} + + +void +decode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, jack_default_audio_sample_t* buf) +{ + int i; + jack_midi_clear_buffer (buf); + for (i = 0; i < buffer_size_uint32 - 3;) + { + uint32_t payload_size; + payload_size = buffer_uint32[i]; + payload_size = ntohl (payload_size); + if (payload_size) + { + jack_midi_event_t event; + event.time = ntohl (buffer_uint32[i+1]); + event.size = ntohl (buffer_uint32[i+2]); + event.buffer = (jack_midi_data_t*) (&(buffer_uint32[i+3])); + jack_midi_event_write (buf, event.time, event.buffer, event.size); + + // skip to the next event + unsigned int nb_data_quads = (((event.size-1) & ~0x3) >> 2)+1; + i += 3+nb_data_quads; + } + else + break; // no events can follow an empty event, we're done + } +} + +void +encode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, jack_default_audio_sample_t* buf) +{ + int i; + unsigned int written = 0; + // midi port, encode midi events + unsigned int nevents = jack_midi_get_event_count (buf); + for (i = 0; i < nevents; ++i) + { + jack_midi_event_t event; + jack_midi_event_get (&event, buf, i); + unsigned int nb_data_quads = (((event.size - 1) & ~0x3) >> 2) + 1; + unsigned int payload_size = 3 + nb_data_quads; + // only write if we have sufficient space for the event + // otherwise drop it + if (written + payload_size < buffer_size_uint32 - 1) + { + // write header + buffer_uint32[written]=htonl (payload_size); + written++; + buffer_uint32[written]=htonl (event.time); + written++; + buffer_uint32[written]=htonl (event.size); + written++; + + // write data + jack_midi_data_t* tmpbuff = (jack_midi_data_t*)(&(buffer_uint32[written])); + memcpy (tmpbuff, event.buffer, event.size); + written += nb_data_quads; + } + else + { + // buffer overflow + jack_error ("midi buffer overflow"); + break; + } + } + // now put a netjack_midi 'no-payload' event, signaling EOF + buffer_uint32[written]=0; +} + +// 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, int dont_htonl_floats) +{ + int chn = 0; + JSList *node = capture_ports; +#if HAVE_SAMPLERATE + JSList *src_node = capture_srcs; +#endif + + uint32_t *packet_bufX = (uint32_t *)packet_payload; + + if( !packet_payload ) + return; + + while (node != NULL) + { + int i; + int_float_t val; +#if HAVE_SAMPLERATE + SRC_DATA src; +#endif + + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + + const char *porttype = jack_port_type (port); + + if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + { +#if HAVE_SAMPLERATE + // audio port, resample if necessary + 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 +#endif + { + if( dont_htonl_floats ) + { + memcpy( buf, packet_bufX, net_period_down*sizeof(jack_default_audio_sample_t)); + } + else + { + for (i = 0; i < net_period_down; i++) + { + val.i = packet_bufX[i]; + val.i = ntohl (val.i); + buf[i] = val.f; + } + } + } + } + else if (strncmp (porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + { + // midi port, decode midi events + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_down; + uint32_t * buffer_uint32 = (uint32_t*)packet_bufX; + decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); + } + 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, int dont_htonl_floats ) +{ + int chn = 0; + JSList *node = playback_ports; +#if HAVE_SAMPLERATE + JSList *src_node = playback_srcs; +#endif + + uint32_t *packet_bufX = (uint32_t *) packet_payload; + + while (node != NULL) + { +#if HAVE_SAMPLERATE + SRC_DATA src; +#endif + 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); + + const char *porttype = jack_port_type (port); + + if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + { + // audio port, resample if necessary + +#if HAVE_SAMPLERATE + 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 +#endif + { + if( dont_htonl_floats ) + { + memcpy( packet_bufX, buf, net_period_up*sizeof(jack_default_audio_sample_t) ); + } + else + { + for (i = 0; i < net_period_up; i++) + { + val.f = buf[i]; + val.i = htonl (val.i); + packet_bufX[i] = val.i; + } + } + } + } + else if (strncmp(porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + { + // encode midi events from port to packet + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_up; + uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; + encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); + } + 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) +{ + int chn = 0; + JSList *node = capture_ports; +#if HAVE_SAMPLERATE + JSList *src_node = capture_srcs; +#endif + + uint16_t *packet_bufX = (uint16_t *)packet_payload; + + if( !packet_payload ) + return; + + while (node != NULL) + { + int i; + //uint32_t val; +#if HAVE_SAMPLERATE + SRC_DATA src; +#endif + + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + +#if HAVE_SAMPLERATE + float *floatbuf = alloca (sizeof(float) * net_period_down); +#endif + const char *portname = jack_port_type (port); + + if (strncmp(portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + { + // audio port, resample if necessary + +#if HAVE_SAMPLERATE + 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 +#endif + for (i = 0; i < net_period_down; i++) + buf[i] = ((float) ntohs (packet_bufX[i])) / 32768.0 - 1.0; + } + else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + { + // midi port, decode midi events + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_down / 2; + uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; + decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); + } + 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) +{ + int chn = 0; + JSList *node = playback_ports; +#if HAVE_SAMPLERATE + JSList *src_node = playback_srcs; +#endif + + uint16_t *packet_bufX = (uint16_t *)packet_payload; + + while (node != NULL) + { +#if HAVE_SAMPLERATE + SRC_DATA src; +#endif + int i; + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + const char *portname = jack_port_type (port); + + if (strncmp (portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + { + // audio port, resample if necessary + +#if HAVE_SAMPLERATE + 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 (((uint16_t)((floatbuf[i] + 1.0) * 32767.0))); + } + src_node = jack_slist_next (src_node); + } + else +#endif + for (i = 0; i < net_period_up; i++) + packet_bufX[i] = htons(((uint16_t)((buf[i] + 1.0) * 32767.0))); + } + else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + { + // encode midi events from port to packet + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_up / 2; + uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; + encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); + } + 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) +{ + int chn = 0; + JSList *node = capture_ports; + +#if HAVE_SAMPLERATE + JSList *src_node = capture_srcs; +#endif + + int8_t *packet_bufX = (int8_t *)packet_payload; + + if( !packet_payload ) + return; + + while (node != NULL) + { + int i; + //uint32_t val; +#if HAVE_SAMPLERATE + SRC_DATA src; +#endif + + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + +#if HAVE_SAMPLERATE + float *floatbuf = alloca (sizeof (float) * net_period_down); +#endif + const char *portname = jack_port_type (port); + + if (strncmp (portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + { +#if HAVE_SAMPLERATE + // audio port, resample if necessary + 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 +#endif + for (i = 0; i < net_period_down; i++) + buf[i] = ((float) packet_bufX[i]) / 127.0; + } + else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + { + // midi port, decode midi events + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_down / 2; + uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; + decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); + } + 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) +{ + int chn = 0; + JSList *node = playback_ports; +#if HAVE_SAMPLERATE + JSList *src_node = playback_srcs; +#endif + + int8_t *packet_bufX = (int8_t *)packet_payload; + + while (node != NULL) + { +#if HAVE_SAMPLERATE + SRC_DATA src; +#endif + int i; + jack_port_t *port = (jack_port_t *) node->data; + + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + const char *portname = jack_port_type (port); + + if (strncmp (portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + { +#if HAVE_SAMPLERATE + // audio port, resample if necessary + 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 +#endif + for (i = 0; i < net_period_up; i++) + packet_bufX[i] = buf[i] * 127.0; + } + else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + { + // encode midi events from port to packet + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_up / 4; + uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; + encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); + } + packet_bufX = (packet_bufX + net_period_up); + node = jack_slist_next (node); + chn++; + } +} + +#if HAVE_CELT +// render functions for celt. +void +render_payload_to_jack_ports_celt (void *packet_payload, jack_nframes_t net_period_down, JSList *capture_ports, JSList *capture_srcs, jack_nframes_t nframes) +{ + int chn = 0; + JSList *node = capture_ports; + JSList *src_node = capture_srcs; + + unsigned char *packet_bufX = (unsigned char *)packet_payload; + + while (node != NULL) + { + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + + const char *portname = jack_port_type (port); + + if (strncmp(portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + { + // audio port, decode celt data. + + CELTDecoder *decoder = src_node->data; + if( !packet_payload ) + celt_decode_float( decoder, NULL, net_period_down, buf ); + else + celt_decode_float( decoder, packet_bufX, net_period_down, buf ); + + src_node = jack_slist_next (src_node); + } + else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + { + // midi port, decode midi events + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_down / 2; + uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; + if( packet_payload ) + decode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); + } + packet_bufX = (packet_bufX + net_period_down); + node = jack_slist_next (node); + chn++; + } +} + +void +render_jack_ports_to_payload_celt (JSList *playback_ports, JSList *playback_srcs, jack_nframes_t nframes, void *packet_payload, jack_nframes_t net_period_up) +{ + int chn = 0; + JSList *node = playback_ports; + JSList *src_node = playback_srcs; + + unsigned char *packet_bufX = (unsigned char *)packet_payload; + + while (node != NULL) + { + jack_port_t *port = (jack_port_t *) node->data; + jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); + const char *portname = jack_port_type (port); + + if (strncmp (portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + { + // audio port, encode celt data. + + int encoded_bytes; + float *floatbuf = alloca (sizeof(float) * nframes ); + memcpy( floatbuf, buf, nframes*sizeof(float) ); + CELTEncoder *encoder = src_node->data; + encoded_bytes = celt_encode_float( encoder, floatbuf, NULL, packet_bufX, net_period_up ); + if( encoded_bytes != net_period_up ) + printf( "something in celt changed. netjack needs to be changed to handle this.\n" ); + src_node = jack_slist_next( src_node ); + } + else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + { + // encode midi events from port to packet + // convert the data buffer to a standard format (uint32_t based) + unsigned int buffer_size_uint32 = net_period_up / 2; + uint32_t * buffer_uint32 = (uint32_t*) packet_bufX; + encode_midi_buffer (buffer_uint32, buffer_size_uint32, buf); + } + packet_bufX = (packet_bufX + net_period_up); + node = jack_slist_next (node); + chn++; + } +} + +#endif +/* 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, int dont_htonl_floats) +{ + 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); +#if HAVE_CELT + else if (bitdepth == 1000) + render_payload_to_jack_ports_celt (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); +#endif + else + render_payload_to_jack_ports_float (packet_payload, net_period_down, capture_ports, capture_srcs, nframes, dont_htonl_floats); +} + +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, int dont_htonl_floats) +{ + 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); +#if HAVE_CELT + else if (bitdepth == 1000) + render_jack_ports_to_payload_celt (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); +#endif + else + render_jack_ports_to_payload_float (playback_ports, playback_srcs, nframes, packet_payload, net_period_up, dont_htonl_floats); +} diff --git a/common/netjack_packet.h b/common/netjack_packet.h new file mode 100644 index 00000000..7b21b0ee --- /dev/null +++ b/common/netjack_packet.h @@ -0,0 +1,162 @@ + +/* + * NetJack - Packet Handling functions + * + * used by the driver and the jacknet_client + * + * Copyright (C) 2006 Torben Hohn + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: net_driver.c,v 1.16 2006/03/20 19:41:37 torbenh Exp $ + * + */ + +#ifndef __JACK_NET_PACKET_H__ +#define __JACK_NET_PACKET_H__ + +#ifdef __cplusplus + extern "C" + { +#endif + +#include +#include +//#include +#include + +#include + +//#include +// The Packet Header. + +typedef struct _jacknet_packet_header jacknet_packet_header; + +struct _jacknet_packet_header +{ + // General AutoConf Data + jack_nframes_t capture_channels_audio; + jack_nframes_t playback_channels_audio; + jack_nframes_t capture_channels_midi; + jack_nframes_t playback_channels_midi; + 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_time_t recv_timestamp; + jack_nframes_t framecnt; + char * fragment_array; + char * packet_buf; +}; + +typedef struct _packet_cache packet_cache; + +struct _packet_cache +{ + int size; + cache_packet *packets; + int mtu; + struct sockaddr_in master_address; + int master_address_valid; + jack_nframes_t last_framecnt_retreived; + int last_framecnt_retreived_valid; +}; + +extern packet_cache *global_packcache; + +// fragment cache function prototypes +// XXX: Some of these are private. +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); + +void packet_cache_drain_socket( packet_cache *pcache, int sockfd ); +void packet_cache_reset_master_address( packet_cache *pcache ); +float packet_cache_get_fill( packet_cache *pcache, jack_nframes_t expected_framecnt ); +int packet_cache_retreive_packet_pointer( packet_cache *pcache, jack_nframes_t framecnt, char **packet_buf, int pkt_size, jack_time_t *timestamp ); +int packet_cache_release_packet( packet_cache *pcache, jack_nframes_t framecnt ); +int packet_cache_get_next_available_framecnt( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ); +int packet_cache_get_highest_available_framecnt( packet_cache *pcache, jack_nframes_t *framecnt ); +int packet_cache_find_latency( packet_cache *pcache, jack_nframes_t expected_framecnt, jack_nframes_t *framecnt ); +// Function Prototypes + +int netjack_poll_deadline (int sockfd, jack_time_t deadline); + +void netjack_sendto(int sockfd, char *packet_buf, int pkt_size, int flags, struct sockaddr *addr, int addr_size, 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, int dont_htonl_floats ); + +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, int dont_htonl_floats ); + + +// XXX: This is sort of deprecated: +// This one waits forever. an is not using ppoll +int netjack_poll(int sockfd, int timeout); + +// TODO: these are deprecated. +//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); + +void decode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, jack_default_audio_sample_t* buf); +void encode_midi_buffer (uint32_t *buffer_uint32, unsigned int buffer_size_uint32, jack_default_audio_sample_t* buf); +#ifdef __cplusplus + } +#endif +#endif + From a817e1c4f6ff97160a3689593cd856c423fdd57e Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Sun, 25 Oct 2009 20:16:05 +0100 Subject: [PATCH 02/20] add driver building to wscript --- common/wscript | 2 ++ linux/wscript | 7 ++++++- macosx/wscript | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/common/wscript b/common/wscript index 6089ff81..46d7b12b 100644 --- a/common/wscript +++ b/common/wscript @@ -11,6 +11,8 @@ def configure(conf): if conf.is_defined('HAVE_SAMPLERATE'): conf.env['LIB_SAMPLERATE'] = ['samplerate'] + conf.check_cfg(package='celt', atleast_version='0.5.0', args='--cflags --libs') + conf.env['BUILD_ADAPTER'] = conf.is_defined('HAVE_SAMPLERATE') def create_jack_process_obj(bld, target, sources, uselib = None): diff --git a/linux/wscript b/linux/wscript index 8e12b8ad..adc3478b 100644 --- a/linux/wscript +++ b/linux/wscript @@ -15,7 +15,7 @@ def create_jack_driver_obj(bld, target, sources, uselib = None): driver = bld.new_task_gen('cxx', 'shlib') driver.features.append('cc') driver.env['shlib_PATTERN'] = 'jack_%s.so' - driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE'] + driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL'] driver.includes = ['.', '../linux', '../posix', '../common', '../common/jack', '../dbus'] driver.target = target driver.source = sources @@ -64,3 +64,8 @@ def build(bld): create_jack_driver_obj(bld, 'net', '../common/JackNetDriver.cpp') create_jack_driver_obj(bld, 'loopback', '../common/JackLoopbackDriver.cpp') + + create_jack_driver_obj(bld, 'netone', [ '../common/JackNetOneDriver.cpp', + '../common/netjack.c', + '../common/netjack_packet.c' ], "SAMPLERATE CELT" ) + diff --git a/macosx/wscript b/macosx/wscript index 843874bc..25ca9b3d 100644 --- a/macosx/wscript +++ b/macosx/wscript @@ -73,3 +73,6 @@ def build(bld): create_jack_driver_obj(bld, 'net', '../common/JackNetDriver.cpp') + create_jack_driver_obj(bld, 'netone', [ '../common/JackNetOneDriver.cpp', + '../common/netjack.c', + '../common/netjack_packet.c' ], "SAMPLERATE CELT" ) From 923cec4210e40158d308d6da350d85cc81de2372 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Sun, 25 Oct 2009 20:16:42 +0100 Subject: [PATCH 03/20] Pure Add... add netsource.c to example-clients --- example-clients/netsource.c | 692 ++++++++++++++++++++++++++++++++++++ 1 file changed, 692 insertions(+) create mode 100644 example-clients/netsource.c diff --git a/example-clients/netsource.c b/example-clients/netsource.c new file mode 100644 index 00000000..40da134c --- /dev/null +++ b/example-clients/netsource.c @@ -0,0 +1,692 @@ +/* +NetJack Client + +Copyright (C) 2008 Marc-Olivier Barre +Copyright (C) 2008 Pieter Palmers +Copyright (C) 2006 Torben Hohn + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file netsource.c + * + * @brief This client connects a remote slave JACK to a local JACK server assumed to be the master + */ + +//#include "config.h" +#define HAVE_CELT 1 + + +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#include +#else +#include +#include +#include +#endif + +/* These two required by FreeBSD. */ +#include + +#if HAVE_ALLOCA_H +#include +#endif + +#include + +//#include +#include +#if HAVE_SAMPLERATE +#include +#endif + +#if HAVE_CELT +#include +#endif + +#include + +JSList *capture_ports = NULL; +JSList *capture_srcs = NULL; +int capture_channels = 0; +int capture_channels_audio = 2; +int capture_channels_midi = 1; +JSList *playback_ports = NULL; +JSList *playback_srcs = NULL; +int playback_channels = 0; +int playback_channels_audio = 2; +int playback_channels_midi = 1; +int dont_htonl_floats = 0; + +int latency = 5; +jack_nframes_t factor = 1; +int bitdepth = 0; +int mtu = 1400; +int reply_port = 0; +int redundancy = 1; +jack_client_t *client; + +int state_connected = 0; +int state_latency = 0; +int state_netxruns = 0; +int state_currentframe = 0; +int state_recv_packet_queue_time = 0; + + +int outsockfd; +int insockfd; +#ifdef WIN32 +struct sockaddr_in destaddr; +struct sockaddr_in bindaddr; +#else +struct sockaddr destaddr; +struct sockaddr bindaddr; +#endif + +int sync_state; +jack_transport_state_t last_transport_state; + +int framecnt = 0; + +int cont_miss = 0; + +/** + * This Function allocates all the I/O Ports which are added the lists. + */ +void +alloc_ports (int n_capture_audio, int n_playback_audio, int n_capture_midi, int n_playback_midi) +{ + + int port_flags = JackPortIsOutput; + int chn; + jack_port_t *port; + char buf[32]; + + capture_ports = NULL; + /* Allocate audio capture channels */ + for (chn = 0; chn < n_capture_audio; chn++) + { + snprintf (buf, sizeof (buf) - 1, "capture_%u", chn + 1); + port = jack_port_register (client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0); + if (!port) + { + printf( "jack_netsource: cannot register %s port\n", buf); + break; + } + if( bitdepth == 1000 ) { +#if HAVE_CELT + // XXX: memory leak + CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate( client ), 1, jack_get_buffer_size(client), NULL ); + capture_srcs = jack_slist_append(capture_srcs, celt_decoder_create( celt_mode ) ); +#endif + } else { +#if HAVE_SAMPLERATE + capture_srcs = jack_slist_append (capture_srcs, src_new (SRC_LINEAR, 1, NULL)); +#endif + } + capture_ports = jack_slist_append (capture_ports, port); + } + + /* Allocate midi capture channels */ + for (chn = n_capture_audio; chn < n_capture_midi + n_capture_audio; chn++) + { + snprintf (buf, sizeof (buf) - 1, "capture_%u", chn + 1); + port = jack_port_register (client, buf, JACK_DEFAULT_MIDI_TYPE, port_flags, 0); + if (!port) + { + printf ("jack_netsource: cannot register %s port\n", buf); + break; + } + capture_ports = jack_slist_append(capture_ports, port); + } + + /* Allocate audio playback channels */ + port_flags = JackPortIsInput; + playback_ports = NULL; + for (chn = 0; chn < n_playback_audio; chn++) + { + snprintf (buf, sizeof (buf) - 1, "playback_%u", chn + 1); + port = jack_port_register (client, buf, JACK_DEFAULT_AUDIO_TYPE, port_flags, 0); + if (!port) + { + printf ("jack_netsource: cannot register %s port\n", buf); + break; + } + if( bitdepth == 1000 ) { +#if HAVE_CELT + // XXX: memory leak + CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate (client), 1, jack_get_buffer_size(client), NULL ); + playback_srcs = jack_slist_append(playback_srcs, celt_encoder_create( celt_mode ) ); +#endif + } else { +#if HAVE_SAMPLERATE + playback_srcs = jack_slist_append (playback_srcs, src_new (SRC_LINEAR, 1, NULL)); +#endif + } + playback_ports = jack_slist_append (playback_ports, port); + } + + /* Allocate midi playback channels */ + for (chn = n_playback_audio; chn < n_playback_midi + n_playback_audio; chn++) + { + snprintf (buf, sizeof (buf) - 1, "playback_%u", chn + 1); + port = jack_port_register (client, buf, JACK_DEFAULT_MIDI_TYPE, port_flags, 0); + if (!port) + { + printf ("jack_netsource: cannot register %s port\n", buf); + break; + } + playback_ports = jack_slist_append (playback_ports, port); + } +} + +/** + * The Sync callback... sync state is set elsewhere... + * we will see if this is working correctly. + * i dont really believe in it yet. + */ +int +sync_cb (jack_transport_state_t state, jack_position_t *pos, void *arg) +{ + static int latency_count = 0; + int retval = sync_state; + + if (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; +} + + int deadline_goodness=0; +/** + * The process callback for this JACK application. + * It is called by JACK at the appropriate times. + */ +int +process (jack_nframes_t nframes, void *arg) +{ + jack_nframes_t net_period; + int rx_bufsize, tx_bufsize; + + jack_default_audio_sample_t *buf; + jack_port_t *port; + JSList *node; + int chn; + int size, i; + const char *porttype; + int input_fd; + + jack_position_t local_trans_pos; + + uint32_t *packet_buf, *packet_bufX; + uint32_t *rx_packet_ptr; + jack_time_t packet_recv_timestamp; + + if( bitdepth == 1000 ) + net_period = factor; + else + net_period = (float) nframes / (float) factor; + + rx_bufsize = get_sample_size (bitdepth) * capture_channels * net_period + sizeof (jacknet_packet_header); + tx_bufsize = get_sample_size (bitdepth) * playback_channels * net_period + sizeof (jacknet_packet_header); + + + /* Allocate a buffer where both In and Out Buffer will fit */ + packet_buf = alloca ((rx_bufsize > tx_bufsize) ? rx_bufsize : tx_bufsize); + + jacknet_packet_header *pkthdr = (jacknet_packet_header *) packet_buf; + + /* + * ok... SEND code first. + * needed some time to find out why latency=0 + * did not work ;S + * + */ + + /* reset packet_bufX... */ + 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, dont_htonl_floats); + + /* 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; + + /* playback for us is capture on the other side */ + pkthdr->capture_channels_audio = playback_channels_audio; + pkthdr->playback_channels_audio = capture_channels_audio; + pkthdr->capture_channels_midi = playback_channels_midi; + pkthdr->playback_channels_midi = capture_channels_midi; + pkthdr->mtu = mtu; + pkthdr->sync_state = (jack_nframes_t)deadline_goodness; + //printf("goodness=%d\n", deadline_goodness ); + + packet_header_hton (pkthdr); + if (cont_miss < 3*latency+5) { + int r; + for( r=0; r 50+5*latency) + { + state_connected = 0; + packet_cache_reset_master_address( global_packcache ); + //printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss); + cont_miss = 0; + } + + /* + * ok... now the RECEIVE code. + * + */ + + /* reset packet_bufX... */ + packet_bufX = packet_buf + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t); + + if( reply_port ) + input_fd = insockfd; + else + input_fd = outsockfd; + + // for latency == 0 we can poll. + if( latency == 0 ) { + jack_time_t deadline = jack_get_time() + 1000000 * jack_get_buffer_size(client)/jack_get_sample_rate(client); + // Now loop until we get the right packet. + while(1) { + if ( ! netjack_poll_deadline( input_fd, deadline ) ) + break; + + packet_cache_drain_socket(global_packcache, input_fd); + + if (packet_cache_get_next_available_framecnt( global_packcache, framecnt - latency, NULL )) + break; + } + } else { + // normally: + // only drain socket. + packet_cache_drain_socket(global_packcache, input_fd); + } + + size = packet_cache_retreive_packet_pointer( global_packcache, framecnt - latency, (char**)&rx_packet_ptr, rx_bufsize, &packet_recv_timestamp ); + /* First alternative : we received what we expected. Render the data + * to the JACK ports so it can be played. */ + if (size == rx_bufsize) + { + packet_buf = rx_packet_ptr; + pkthdr = (jacknet_packet_header *) packet_buf; + packet_bufX = packet_buf + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t); + // calculate how much time there would have been, if this packet was sent at the deadline. + + int recv_time_offset = (int) (jack_get_time() - packet_recv_timestamp); + packet_header_ntoh (pkthdr); + deadline_goodness = recv_time_offset - (int)pkthdr->latency; + //printf( "deadline goodness = %d ---> off: %d\n", deadline_goodness, recv_time_offset ); + + if (cont_miss) + { + //printf("Frame %d \tRecovered from dropouts\n", framecnt); + cont_miss = 0; + } + render_payload_to_jack_ports (bitdepth, packet_bufX, net_period, + capture_ports, capture_srcs, nframes, dont_htonl_floats); + + state_currentframe = framecnt; + state_recv_packet_queue_time = recv_time_offset; + state_connected = 1; + sync_state = pkthdr->sync_state; + packet_cache_release_packet( global_packcache, framecnt - latency ); + } + /* Second alternative : we've received something that's not + * as big as expected or we missed a packet. We render silence + * to the ouput ports */ + else + { + jack_nframes_t latency_estimate; + if( packet_cache_find_latency( global_packcache, framecnt, &latency_estimate ) ) + //if( (state_latency == 0) || (latency_estimate < state_latency) ) + state_latency = latency_estimate; + + // Set the counters up. + state_currentframe = framecnt; + //state_latency = framecnt - pkthdr->framecnt; + state_netxruns += 1; + + //printf ("Frame %d \tPacket missed or incomplete (expected: %d bytes, got: %d bytes)\n", framecnt, rx_bufsize, size); + //printf ("Frame %d \tPacket missed or incomplete\n", framecnt); + cont_miss += 1; + chn = 0; + node = capture_ports; + while (node != NULL) + { + port = (jack_port_t *) node->data; + buf = jack_port_get_buffer (port, nframes); + porttype = jack_port_type (port); + if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size ()) == 0) + for (i = 0; i < nframes; i++) + buf[i] = 0.0; + else if (strncmp (porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size ()) == 0) + jack_midi_clear_buffer (buf); + node = jack_slist_next (node); + chn++; + } + } + + 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) +{ +printf( "still here... \n" ); +fflush( stdout ); + + name->sin_family = AF_INET ; + name->sin_port = htons (port); + if (hostname) + { + struct hostent *hostinfo = gethostbyname (hostname); + if (hostinfo == NULL) { + fprintf (stderr, "init_sockaddr_in: unknown host: %s.\n", hostname); + fflush( stderr ); + } +#ifdef WIN32 + name->sin_addr.s_addr = inet_addr( hostname ); +#else + name->sin_addr = *(struct in_addr *) hostinfo->h_addr ; +#endif + } + else + name->sin_addr.s_addr = htonl (INADDR_ANY) ; + +} + +void +printUsage () +{ +fprintf (stderr, "usage: jack_netsource -h [options]\n" + "\n" + " -n - Reports a different name to jack\n" + " -s - The name of the local jack server\n" + " -h - Host name of the slave JACK\n" + " -p - UDP port used by the slave JACK\n" + " -P - Number of audio playback channels\n" + " -C - Number of audio capture channels\n" + " -o - Number of midi playback channels\n" + " -i - Number of midi capture channels\n" + " -l - Network latency in number of NetJack frames\n" + " -r - Local UDP port to use\n" + " -f - Downsample data in the wire by this factor\n" + " -b - Set transport to use 16bit or 8bit\n" + " -m - Assume this mtu for the link\n" + " -c - Use Celt and encode per channel and packet.\n" + " -R - Send out packets N times.\n" + "\n"); +} + +int +main (int argc, char *argv[]) +{ + /* Some startup related basics */ + char *client_name, *server_name = NULL, *peer_ip; + int peer_port = 3000; + jack_options_t options = JackNullOption; + jack_status_t status; +#ifdef WIN32 + WSADATA wsa; + int rc = WSAStartup(MAKEWORD(2,0),&wsa); +#endif + /* Torben's famous state variables, aka "the reporting API" ! */ + /* heh ? these are only the copies of them ;) */ + int statecopy_connected, statecopy_latency, statecopy_netxruns; + jack_nframes_t net_period; + /* Argument parsing stuff */ + extern char *optarg; + extern int optind, optopt; + int errflg=0, c; + + if (argc < 3) + { + printUsage (); + return 1; + } + + client_name = (char *) malloc (sizeof (char) * 10); + peer_ip = (char *) malloc (sizeof (char) * 10); + sprintf(client_name, "netsource"); + sprintf(peer_ip, "localhost"); + + while ((c = getopt (argc, argv, ":H:R:n:s:h:p:C:P:i:o:l:r:f:b:m:c:")) != -1) + { + switch (c) + { + case 'n': + free(client_name); + client_name = (char *) malloc (sizeof (char) * strlen (optarg)+1); + strcpy (client_name, optarg); + break; + case 's': + server_name = (char *) malloc (sizeof (char) * strlen (optarg)+1); + strcpy (server_name, optarg); + options |= JackServerName; + break; + case 'h': + free(peer_ip); + peer_ip = (char *) malloc (sizeof (char) * strlen (optarg)+1); + strcpy (peer_ip, optarg); + break; + case 'p': + peer_port = atoi (optarg); + break; + case 'P': + playback_channels_audio = atoi (optarg); + break; + case 'C': + capture_channels_audio = atoi (optarg); + break; + case 'o': + playback_channels_midi = atoi (optarg); + break; + case 'i': + capture_channels_midi = 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 'c': +#if HAVE_CELT + bitdepth = 1000; + factor = atoi (optarg); +#else + printf( "not built with celt supprt\n" ); + exit(10); +#endif + break; + case 'm': + mtu = atoi (optarg); + break; + case 'R': + redundancy = atoi (optarg); + break; + case 'H': + dont_htonl_floats = 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); + } + + capture_channels = capture_channels_audio + capture_channels_midi; + playback_channels = playback_channels_audio + playback_channels_midi; + + outsockfd = socket (AF_INET, SOCK_DGRAM, 0); + insockfd = socket (AF_INET, SOCK_DGRAM, 0); + + if( (outsockfd == -1) || (insockfd == -1) ) { + fprintf (stderr, "cant open sockets\n" ); + return 1; + } + + init_sockaddr_in ((struct sockaddr_in *) &destaddr, peer_ip, peer_port); + if(reply_port) + { + init_sockaddr_in ((struct sockaddr_in *) &bindaddr, NULL, reply_port); + if( bind (insockfd, &bindaddr, sizeof (bindaddr)) ) { + fprintf (stderr, "bind failure\n" ); + } + } + + /* try to become a client of the JACK server */ + client = jack_client_open (client_name, options, &status, server_name); + if (client == NULL) + { + fprintf (stderr, "jack_client_open() failed, status = 0x%2.0x\n" + "Is the JACK server running ?\n", status); + return 1; + } + + /* Set up jack callbacks */ + jack_set_process_callback (client, process, 0); + jack_set_sync_callback (client, sync_cb, 0); + jack_on_shutdown (client, jack_shutdown, 0); + + alloc_ports (capture_channels_audio, playback_channels_audio, capture_channels_midi, playback_channels_midi); + + if( bitdepth == 1000 ) + net_period = factor; + else + net_period = ceilf((float) jack_get_buffer_size (client) / (float) factor); + + int rx_bufsize = get_sample_size (bitdepth) * capture_channels * net_period + sizeof (jacknet_packet_header); + global_packcache = packet_cache_new (latency + 50, rx_bufsize, mtu); + + /* tell the JACK server that we are ready to roll */ + if (jack_activate (client)) + { + fprintf (stderr, "Cannot activate client"); + return 1; + } + + /* Now sleep forever... and evaluate the state_ vars */ + + statecopy_connected = 2; // make it report unconnected on start. + statecopy_latency = state_latency; + statecopy_netxruns = state_netxruns; + + while (1) + { +#ifdef WIN32 + Sleep (1000); +#else + sleep(1); +#endif + if (statecopy_connected != state_connected) + { + statecopy_connected = state_connected; + if (statecopy_connected) + { + state_netxruns = 1; // We want to reset the netxrun count on each new connection + printf ("Connected :-)\n"); + } + else + printf ("Not Connected\n"); + + fflush(stdout); + } + + if (statecopy_connected) + { + if (statecopy_netxruns != state_netxruns) { + statecopy_netxruns = state_netxruns; + printf ("at frame %06d -> total netxruns %d (%d%%) queue time= %d\n", state_currentframe, + statecopy_netxruns, + 100*statecopy_netxruns/state_currentframe, + state_recv_packet_queue_time); + fflush(stdout); + } + } + else + { + if (statecopy_latency != state_latency) + { + statecopy_latency = state_latency; + if (statecopy_latency > 1) + printf ("current latency %d\n", statecopy_latency); + fflush(stdout); + } + } + } + + /* Never reached. Well we will be a GtkApp someday... */ + packet_cache_free (global_packcache); + jack_client_close (client); + exit (0); +} From 91872fa0ad183d309579bb11af83b376d17432e4 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Sun, 25 Oct 2009 20:18:10 +0100 Subject: [PATCH 04/20] add netsource to wscript... maybe still needs some conditional lib deps. --- example-clients/wscript | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/example-clients/wscript b/example-clients/wscript index 3906cfd2..5a17ba6e 100644 --- a/example-clients/wscript +++ b/example-clients/wscript @@ -30,9 +30,16 @@ example_libs = { def configure(conf): e = conf.check_cc(header_name='sndfile.h', define_name="HAVE_SNDFILE") + conf.check_cc(header_name='samplerate.h', define_name="HAVE_SAMPLERATE") + + if conf.is_defined('HAVE_SAMPLERATE'): + conf.env['LIB_SAMPLERATE'] = ['samplerate'] + if conf.is_defined('HAVE_SNDFILE'): conf.env['LIB_SNDFILE'] = ['sndfile'] + conf.check_cfg(package='celt', atleast_version='0.5.0', args='--cflags --libs') + e = conf.check_cc(header_name='ncurses.h', define_name="HAVE_NCURSES") if conf.is_defined('HAVE_NCURSES'): @@ -105,6 +112,14 @@ def build(bld): prog.uselib_local = 'clientlib' prog.target = 'jack_rec' + prog = bld.new_task_gen('cc', 'program') + prog.includes = os_incdir + ['../common/jack', '../common'] + prog.source = ['netsource.c', '../common/netjack_packet.c'] + prog.env.append_value("CCFLAGS", "-DNO_JACK_ERROR") + prog.uselib = 'CELT SAMPLERATE' + + prog.uselib_local = 'clientlib' + prog.target = 'jack_netsource' for example_lib, example_lib_source in example_libs.items(): lib = bld.new_task_gen('cc', 'shlib') lib.env['shlib_PATTERN'] = '%s.so' From e3f55603c2e008f2538cbdc74886175bee0a48fb Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Sun, 25 Oct 2009 20:20:48 +0100 Subject: [PATCH 05/20] Pure Add: alsa_in.c alsa_out.c --- example-clients/alsa_in.c | 757 +++++++++++++++++++++++++++++++++++++ example-clients/alsa_out.c | 755 ++++++++++++++++++++++++++++++++++++ 2 files changed, 1512 insertions(+) create mode 100644 example-clients/alsa_in.c create mode 100644 example-clients/alsa_out.c diff --git a/example-clients/alsa_in.c b/example-clients/alsa_in.c new file mode 100644 index 00000000..24d70c5f --- /dev/null +++ b/example-clients/alsa_in.c @@ -0,0 +1,757 @@ +/** @file simple_client.c + * + * @brief This simple client demonstrates the basic features of JACK + * as they would be used by many applications. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "alsa/asoundlib.h" + +#include + +// Here are the lists of the jack ports... + +JSList *capture_ports = NULL; +JSList *capture_srcs = NULL; +JSList *playback_ports = NULL; +JSList *playback_srcs = NULL; +jack_client_t *client; + +snd_pcm_t *alsa_handle; + +int jack_sample_rate; +int jack_buffer_size; + +int quit = 0; +double resample_mean = 1.0; +double static_resample_factor = 1.0; + +double *offset_array; +double *window_array; +int offset_differential_index = 0; + +double offset_integral = 0; + +// ------------------------------------------------------ commandline parameters + +int sample_rate = 0; /* stream rate */ +int num_channels = 2; /* count of channels */ +int period_size = 1024; +int num_periods = 2; + +int target_delay = 0; /* the delay which the program should try to approach. */ +int max_diff = 0; /* the diff value, when a hard readpointer skip should occur */ +int catch_factor = 100000; +int catch_factor2 = 10000; +double pclamp = 15.0; +double controlquant = 10000.0; +int smooth_size = 256; +int good_window=0; +int verbose = 0; +int instrument = 0; +int samplerate_quality = 2; + +// Debug stuff: + +volatile float output_resampling_factor = 1.0; +volatile int output_new_delay = 0; +volatile float output_offset = 0.0; +volatile float output_integral = 0.0; +volatile float output_diff = 0.0; + +snd_pcm_uframes_t real_buffer_size; +snd_pcm_uframes_t real_period_size; + +// format selection, and corresponding functions from memops in a nice set of structs. + +typedef struct alsa_format { + snd_pcm_format_t format_id; + size_t sample_size; + void (*jack_to_soundcard) (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); + void (*soundcard_to_jack) (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); + const char *name; +} alsa_format_t; + +alsa_format_t formats[] = { + { SND_PCM_FORMAT_S24_3LE, 3, sample_move_d24_sS, sample_move_dS_s24, "24bit - real" }, + { SND_PCM_FORMAT_FLOAT_LE, 4, sample_move_dS_floatLE, sample_move_floatLE_sSs, "float" }, + { SND_PCM_FORMAT_S32, 4, sample_move_d32u24_sS, sample_move_dS_s32u24, "32bit" }, + { SND_PCM_FORMAT_S24, 4, sample_move_d24_sS, sample_move_dS_s24, "24bit" }, + { SND_PCM_FORMAT_S16, 2, sample_move_d16_sS, sample_move_dS_s16, "16bit" } +}; +#define NUMFORMATS (sizeof(formats)/sizeof(formats[0])) +int format=0; + +// Alsa stuff... i dont want to touch this bullshit in the next years.... please... + +static int xrun_recovery(snd_pcm_t *handle, int err) { +// printf( "xrun !!!.... %d\n", err ); + if (err == -EPIPE) { /* under-run */ + err = snd_pcm_prepare(handle); + if (err < 0) + printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); + return 0; + } else if (err == -EAGAIN) { + while ((err = snd_pcm_resume(handle)) == -EAGAIN) + usleep(100); /* wait until the suspend flag is released */ + if (err < 0) { + err = snd_pcm_prepare(handle); + if (err < 0) + printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); + } + return 0; + } + return err; +} + +static int set_hwformat( snd_pcm_t *handle, snd_pcm_hw_params_t *params ) +{ + int i; + int err; + + for( i=0; i (target_delay+max_diff) ) { + char *tmp = alloca( (delay-target_delay) * formats[format].sample_size * num_channels ); + snd_pcm_readi( alsa_handle, tmp, delay-target_delay ); + output_new_delay = (int) delay; + + delay = target_delay; + + // Set the resample_rate... we need to adjust the offset integral, to do this. + // first look at the PI controller, this code is just a special case, which should never execute once + // everything is swung in. + offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; + // Also clear the array. we are beginning a new control cycle. + for( i=0; i 4 ) current_resample_factor = 4; + + // Now Calculate how many samples we need. + rlen = ceil( ((double)nframes) / current_resample_factor )+2; + assert( rlen > 2 ); + + // Calculate resample_mean so we can init ourselves to saner values. + resample_mean = 0.9999 * resample_mean + 0.0001 * current_resample_factor; + /* + * now this should do it... + */ + + outbuf = alloca( rlen * formats[format].sample_size * num_channels ); + + resampbuf = alloca( rlen * sizeof( float ) ); + + // get the data... +again: + err = snd_pcm_readi(alsa_handle, outbuf, rlen); + if( err < 0 ) { + printf( "err = %d\n", err ); + if (xrun_recovery(alsa_handle, err) < 0) { + //printf("Write error: %s\n", snd_strerror(err)); + //exit(EXIT_FAILURE); + } + goto again; + } + if( err != rlen ) { + //printf( "read = %d\n", rlen ); + } + + /* + * render jack ports to the outbuf... + */ + + int chn = 0; + JSList *node = capture_ports; + JSList *src_node = capture_srcs; + SRC_DATA src; + + while ( node != NULL) + { + jack_port_t *port = (jack_port_t *) node->data; + float *buf = jack_port_get_buffer (port, nframes); + + SRC_STATE *src_state = src_node->data; + + formats[format].soundcard_to_jack( resampbuf, outbuf + format[formats].sample_size * chn, rlen, num_channels*format[formats].sample_size ); + + src.data_in = resampbuf; + src.input_frames = rlen; + + src.data_out = buf; + src.output_frames = nframes; + src.end_of_input = 0; + + src.src_ratio = current_resample_factor; + + src_process( src_state, &src ); + + put_back_samples = rlen-src.input_frames_used; + + src_node = jack_slist_next (src_node); + node = jack_slist_next (node); + chn++; + } + + // Put back the samples libsamplerate did not consume. + //printf( "putback = %d\n", put_back_samples ); + snd_pcm_rewind( alsa_handle, put_back_samples ); + + return 0; +} + + +/** + * Allocate the necessary jack ports... + */ + +void alloc_ports( int n_capture, int n_playback ) { + + int port_flags = JackPortIsOutput; + int chn; + jack_port_t *port; + char buf[32]; + + capture_ports = NULL; + for (chn = 0; chn < n_capture; chn++) + { + snprintf (buf, sizeof(buf) - 1, "capture_%u", chn+1); + + port = jack_port_register (client, buf, + JACK_DEFAULT_AUDIO_TYPE, + port_flags, 0); + + if (!port) + { + printf( "jacknet_client: cannot register port for %s", buf); + break; + } + + capture_srcs = jack_slist_append( capture_srcs, src_new( 4-samplerate_quality, 1, NULL ) ); + capture_ports = jack_slist_append (capture_ports, port); + } + + port_flags = JackPortIsInput; + + playback_ports = NULL; + for (chn = 0; chn < n_playback; chn++) + { + snprintf (buf, sizeof(buf) - 1, "playback_%u", chn+1); + + port = jack_port_register (client, buf, + JACK_DEFAULT_AUDIO_TYPE, + port_flags, 0); + + if (!port) + { + printf( "jacknet_client: cannot register port for %s", buf); + break; + } + + playback_srcs = jack_slist_append( playback_srcs, src_new( 4-samplerate_quality, 1, NULL ) ); + playback_ports = jack_slist_append (playback_ports, port); + } +} + +/** + * This is the shutdown callback for this JACK application. + * It is called by JACK if the server ever shuts down or + * decides to disconnect the client. + */ + +void jack_shutdown (void *arg) { + + exit (1); +} + +/** + * be user friendly. + * be user friendly. + * be user friendly. + */ + +void printUsage() { +fprintf(stderr, "usage: alsa_out [options]\n" + "\n" + " -j - client name\n" + " -d \n" + " -c \n" + " -p \n" + " -n \n" + " -r \n" + " -q \n" + " -t \n" + " -i turns on instrumentation\n" + " -v turns on printouts\n" + "\n"); +} + + +/** + * the main function.... + */ + +void +sigterm_handler( int signal ) +{ + quit = 1; +} + + +int main (int argc, char *argv[]) { + char jack_name[30] = "alsa_in"; + char alsa_device[30] = "hw:0"; + + extern char *optarg; + extern int optind, optopt; + int errflg=0; + int c; + + while ((c = getopt(argc, argv, "ivj:r:c:p:n:d:q:m:t:f:F:C:Q:s:")) != -1) { + switch(c) { + case 'j': + strcpy(jack_name,optarg); + break; + case 'r': + sample_rate = atoi(optarg); + break; + case 'c': + num_channels = atoi(optarg); + break; + case 'p': + period_size = atoi(optarg); + break; + case 'n': + num_periods = atoi(optarg); + break; + case 'd': + strcpy(alsa_device,optarg); + break; + case 't': + target_delay = atoi(optarg); + break; + case 'q': + samplerate_quality = atoi(optarg); + break; + case 'm': + max_diff = atoi(optarg); + break; + case 'f': + catch_factor = atoi(optarg); + break; + case 'F': + catch_factor2 = atoi(optarg); + break; + case 'C': + pclamp = (double) atoi(optarg); + break; + case 'Q': + controlquant = (double) atoi(optarg); + break; + case 'v': + verbose = 1; + break; + case 'i': + instrument = 1; + break; + case 's': + smooth_size = atoi(optarg); + break; + case ':': + fprintf(stderr, + "Option -%c requires an operand\n", optopt); + errflg++; + break; + case '?': + fprintf(stderr, + "Unrecognized option: -%c\n", optopt); + errflg++; + } + } + if (errflg) { + printUsage(); + exit(2); + } + + if( (samplerate_quality < 0) || (samplerate_quality > 4) ) { + fprintf (stderr, "invalid samplerate quality\n"); + return 1; + } + if ((client = jack_client_open (jack_name, 0, NULL)) == 0) { + fprintf (stderr, "jack server not running?\n"); + return 1; + } + + /* tell the JACK server to call `process()' whenever + there is work to be done. + */ + + jack_set_process_callback (client, process, 0); + + /* tell the JACK server to call `jack_shutdown()' if + it ever shuts down, either entirely, or if it + just decides to stop calling us. + */ + + jack_on_shutdown (client, jack_shutdown, 0); + + + // get jack sample_rate + + jack_sample_rate = jack_get_sample_rate( client ); + + if( !sample_rate ) + sample_rate = jack_sample_rate; + + // now open the alsa fd... + alsa_handle = open_audiofd( alsa_device, 1, sample_rate, num_channels, period_size, num_periods); + if( alsa_handle == 0 ) + exit(20); + + printf( "selected sample format: %s\n", formats[format].name ); + + static_resample_factor = (double) jack_sample_rate / (double) sample_rate; + resample_mean = static_resample_factor; + + offset_array = malloc( sizeof(double) * smooth_size ); + if( offset_array == NULL ) { + fprintf( stderr, "no memory for offset_array !!!\n" ); + exit(20); + } + window_array = malloc( sizeof(double) * smooth_size ); + if( window_array == NULL ) { + fprintf( stderr, "no memory for window_array !!!\n" ); + exit(20); + } + int i; + for( i=0; i target_delay ) { + fprintf( stderr, "target_delay (%d) cant be smaller than max_diff(%d)\n", target_delay, max_diff ); + exit(20); + } + if( (target_delay+max_diff) > (num_periods*period_size) ) { + fprintf( stderr, "target_delay+max_diff (%d) cant be bigger than buffersize(%d)\n", target_delay+max_diff, num_periods*period_size ); + exit(20); + } + // alloc input ports, which are blasted out to alsa... + alloc_ports( num_channels, 0 ); + + + /* tell the JACK server that we are ready to roll */ + + if (jack_activate (client)) { + fprintf (stderr, "cannot activate client"); + return 1; + } + + signal( SIGTERM, sigterm_handler ); + signal( SIGINT, sigterm_handler ); + + if( verbose ) { + while(!quit) { + usleep(500000); + if( output_new_delay ) { + printf( "delay = %d\n", output_new_delay ); + output_new_delay = 0; + } + printf( "res: %f, \tdiff = %f, \toffset = %f \n", output_resampling_factor, output_diff, output_offset ); + } + } else if( instrument ) { + printf( "# n\tresamp\tdiff\toffseti\tintegral\n"); + int n=0; + while(!quit) { + usleep(1000); + printf( "%d\t%f\t%f\t%f\t%f\n", n++, output_resampling_factor, output_diff, output_offset, output_integral ); + } + } else { + while(!quit) + { + usleep(500000); + if( output_new_delay ) { + printf( "delay = %d\n", output_new_delay ); + output_new_delay = 0; + } + } + } + + jack_deactivate( client ); + jack_client_close (client); + exit (0); +} + diff --git a/example-clients/alsa_out.c b/example-clients/alsa_out.c new file mode 100644 index 00000000..88bb2920 --- /dev/null +++ b/example-clients/alsa_out.c @@ -0,0 +1,755 @@ +/** @file simple_client.c + * + * @brief This simple client demonstrates the basic features of JACK + * as they would be used by many applications. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "alsa/asoundlib.h" + +#include + +// Here are the lists of the jack ports... + +JSList *capture_ports = NULL; +JSList *capture_srcs = NULL; +JSList *playback_ports = NULL; +JSList *playback_srcs = NULL; +jack_client_t *client; + +snd_pcm_t *alsa_handle; + +int jack_sample_rate; +int jack_buffer_size; + +double resample_mean = 1.0; +double static_resample_factor = 1.0; + +double *offset_array; +double *window_array; +int offset_differential_index = 0; + +double offset_integral = 0; +int quit = 0; + +// ------------------------------------------------------ commandline parameters + +int sample_rate = 0; /* stream rate */ +int num_channels = 2; /* count of channels */ +int period_size = 1024; +int num_periods = 2; + +int target_delay = 0; /* the delay which the program should try to approach. */ +int max_diff = 0; /* the diff value, when a hard readpointer skip should occur */ +int catch_factor = 100000; +int catch_factor2 = 10000; +double pclamp = 15.0; +double controlquant = 10000.0; +int smooth_size = 256; +int good_window=0; +int verbose = 0; +int instrument = 0; +int samplerate_quality = 2; + +// Debug stuff: + +volatile float output_resampling_factor = 1.0; +volatile int output_new_delay = 0; +volatile float output_offset = 0.0; +volatile float output_integral = 0.0; +volatile float output_diff = 0.0; + +snd_pcm_uframes_t real_buffer_size; +snd_pcm_uframes_t real_period_size; + +// format selection, and corresponding functions from memops in a nice set of structs. + +typedef struct alsa_format { + snd_pcm_format_t format_id; + size_t sample_size; + void (*jack_to_soundcard) (char *dst, jack_default_audio_sample_t *src, unsigned long nsamples, unsigned long dst_skip, dither_state_t *state); + void (*soundcard_to_jack) (jack_default_audio_sample_t *dst, char *src, unsigned long nsamples, unsigned long src_skip); + const char *name; +} alsa_format_t; + +alsa_format_t formats[] = { + { SND_PCM_FORMAT_FLOAT_LE, 4, sample_move_dS_floatLE, sample_move_floatLE_sSs, "float" }, + { SND_PCM_FORMAT_S32, 4, sample_move_d32u24_sS, sample_move_dS_s32u24, "32bit" }, + { SND_PCM_FORMAT_S24, 4, sample_move_d24_sS, sample_move_dS_s24, "24bit" }, + { SND_PCM_FORMAT_S16, 2, sample_move_d16_sS, sample_move_dS_s16, "16bit" } +}; +#define NUMFORMATS (sizeof(formats)/sizeof(formats[0])) +int format=0; + +// Alsa stuff... i dont want to touch this bullshit in the next years.... please... + +static int xrun_recovery(snd_pcm_t *handle, int err) { +// printf( "xrun !!!.... %d\n", err ); + if (err == -EPIPE) { /* under-run */ + err = snd_pcm_prepare(handle); + if (err < 0) + printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); + return 0; + } else if (err == -EAGAIN) { + while ((err = snd_pcm_resume(handle)) == -EAGAIN) + usleep(100); /* wait until the suspend flag is released */ + if (err < 0) { + err = snd_pcm_prepare(handle); + if (err < 0) + printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); + } + return 0; + } + return err; +} + +static int set_hwformat( snd_pcm_t *handle, snd_pcm_hw_params_t *params ) +{ + int i; + int err; + + for( i=0; i (target_delay+max_diff) ) { + snd_pcm_rewind( alsa_handle, delay - target_delay ); + output_new_delay = (int) delay; + + delay = target_delay; + + // Set the resample_rate... we need to adjust the offset integral, to do this. + // first look at the PI controller, this code is just a special case, which should never execute once + // everything is swung in. + offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; + // Also clear the array. we are beginning a new control cycle. + for( i=0; i 4 ) current_resample_factor = 4; + + // Now Calculate how many samples we need. + rlen = ceil( ((double)nframes) * current_resample_factor )+2; + assert( rlen > 2 ); + + // Calculate resample_mean so we can init ourselves to saner values. + resample_mean = 0.9999 * resample_mean + 0.0001 * current_resample_factor; + /* + * now this should do it... + */ + + outbuf = alloca( rlen * formats[format].sample_size * num_channels ); + + resampbuf = alloca( rlen * sizeof( float ) ); + /* + * render jack ports to the outbuf... + */ + + int chn = 0; + JSList *node = playback_ports; + JSList *src_node = playback_srcs; + SRC_DATA src; + + while ( node != NULL) + { + jack_port_t *port = (jack_port_t *) node->data; + float *buf = jack_port_get_buffer (port, nframes); + + SRC_STATE *src_state = src_node->data; + + src.data_in = buf; + src.input_frames = nframes; + + src.data_out = resampbuf; + src.output_frames = rlen; + src.end_of_input = 0; + + src.src_ratio = current_resample_factor; + + src_process( src_state, &src ); + + formats[format].jack_to_soundcard( outbuf + format[formats].sample_size * chn, resampbuf, src.output_frames_gen, num_channels*format[formats].sample_size, NULL); + + src_node = jack_slist_next (src_node); + node = jack_slist_next (node); + chn++; + } + + // now write the output... +again: + err = snd_pcm_writei(alsa_handle, outbuf, src.output_frames_gen); + //err = snd_pcm_writei(alsa_handle, outbuf, src.output_frames_gen); + if( err < 0 ) { + printf( "err = %d\n", err ); + if (xrun_recovery(alsa_handle, err) < 0) { + printf("Write error: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + goto again; + } + + return 0; +} + + +/** + * Allocate the necessary jack ports... + */ + +void alloc_ports( int n_capture, int n_playback ) { + + int port_flags = JackPortIsOutput | JackPortIsPhysical | JackPortIsTerminal; + int chn; + jack_port_t *port; + char buf[32]; + + capture_ports = NULL; + for (chn = 0; chn < n_capture; chn++) + { + snprintf (buf, sizeof(buf) - 1, "capture_%u", chn+1); + + port = jack_port_register (client, buf, + JACK_DEFAULT_AUDIO_TYPE, + port_flags, 0); + + if (!port) + { + printf( "jacknet_client: cannot register port for %s", buf); + break; + } + + capture_srcs = jack_slist_append( capture_srcs, src_new( 4-samplerate_quality, 1, NULL ) ); + capture_ports = jack_slist_append (capture_ports, port); + } + + port_flags = JackPortIsInput; + + playback_ports = NULL; + for (chn = 0; chn < n_playback; chn++) + { + snprintf (buf, sizeof(buf) - 1, "playback_%u", chn+1); + + port = jack_port_register (client, buf, + JACK_DEFAULT_AUDIO_TYPE, + port_flags, 0); + + if (!port) + { + printf( "jacknet_client: cannot register port for %s", buf); + break; + } + + playback_srcs = jack_slist_append( playback_srcs, src_new( 4-samplerate_quality, 1, NULL ) ); + playback_ports = jack_slist_append (playback_ports, port); + } +} + +/** + * This is the shutdown callback for this JACK application. + * It is called by JACK if the server ever shuts down or + * decides to disconnect the client. + */ + +void jack_shutdown (void *arg) { + + exit (1); +} + +/** + * be user friendly. + * be user friendly. + * be user friendly. + */ + +void printUsage() { +fprintf(stderr, "usage: alsa_out [options]\n" + "\n" + " -j - client name\n" + " -d \n" + " -c \n" + " -p \n" + " -n \n" + " -r \n" + " -q \n" + " -t \n" + " -i turns on instrumentation\n" + " -v turns on printouts\n" + "\n"); +} + + +/** + * the main function.... + */ + +void +sigterm_handler( int signal ) +{ + quit = 1; +} + + +int main (int argc, char *argv[]) { + char jack_name[30] = "alsa_out"; + char alsa_device[30] = "hw:0"; + + extern char *optarg; + extern int optind, optopt; + int errflg=0; + int c; + + while ((c = getopt(argc, argv, "ivj:r:c:p:n:d:q:m:t:f:F:C:Q:s:")) != -1) { + switch(c) { + case 'j': + strcpy(jack_name,optarg); + break; + case 'r': + sample_rate = atoi(optarg); + break; + case 'c': + num_channels = atoi(optarg); + break; + case 'p': + period_size = atoi(optarg); + break; + case 'n': + num_periods = atoi(optarg); + break; + case 'd': + strcpy(alsa_device,optarg); + break; + case 't': + target_delay = atoi(optarg); + break; + case 'q': + samplerate_quality = atoi(optarg); + break; + case 'm': + max_diff = atoi(optarg); + break; + case 'f': + catch_factor = atoi(optarg); + break; + case 'F': + catch_factor2 = atoi(optarg); + break; + case 'C': + pclamp = (double) atoi(optarg); + break; + case 'Q': + controlquant = (double) atoi(optarg); + break; + case 'v': + verbose = 1; + break; + case 'i': + instrument = 1; + break; + case 's': + smooth_size = atoi(optarg); + break; + case ':': + fprintf(stderr, + "Option -%c requires an operand\n", optopt); + errflg++; + break; + case '?': + fprintf(stderr, + "Unrecognized option: -%c\n", optopt); + errflg++; + } + } + if (errflg) { + printUsage(); + exit(2); + } + + if( (samplerate_quality < 0) || (samplerate_quality > 4) ) { + fprintf (stderr, "invalid samplerate quality\n"); + return 1; + } + if ((client = jack_client_open (jack_name, 0, NULL)) == 0) { + fprintf (stderr, "jack server not running?\n"); + return 1; + } + + /* tell the JACK server to call `process()' whenever + there is work to be done. + */ + + jack_set_process_callback (client, process, 0); + + /* tell the JACK server to call `jack_shutdown()' if + it ever shuts down, either entirely, or if it + just decides to stop calling us. + */ + + jack_on_shutdown (client, jack_shutdown, 0); + + + // get jack sample_rate + + jack_sample_rate = jack_get_sample_rate( client ); + + if( !sample_rate ) + sample_rate = jack_sample_rate; + + static_resample_factor = (double) sample_rate / (double) jack_sample_rate; + resample_mean = static_resample_factor; + + offset_array = malloc( sizeof(double) * smooth_size ); + if( offset_array == NULL ) { + fprintf( stderr, "no memory for offset_array !!!\n" ); + exit(20); + } + window_array = malloc( sizeof(double) * smooth_size ); + if( window_array == NULL ) { + fprintf( stderr, "no memory for window_array !!!\n" ); + exit(20); + } + int i; + for( i=0; i target_delay ) { + fprintf( stderr, "target_delay (%d) cant be smaller than max_diff(%d)\n", target_delay, max_diff ); + exit(20); + } + if( (target_delay+max_diff) > (num_periods*period_size) ) { + fprintf( stderr, "target_delay+max_diff (%d) cant be bigger than buffersize(%d)\n", target_delay+max_diff, num_periods*period_size ); + exit(20); + } + // now open the alsa fd... + alsa_handle = open_audiofd( alsa_device, 0, sample_rate, num_channels, period_size, num_periods); + if( alsa_handle == 0 ) + exit(20); + + printf( "selected sample format: %s\n", formats[format].name ); + + // alloc input ports, which are blasted out to alsa... + alloc_ports( 0, num_channels ); + + + /* tell the JACK server that we are ready to roll */ + + if (jack_activate (client)) { + fprintf (stderr, "cannot activate client"); + return 1; + } + + signal( SIGTERM, sigterm_handler ); + signal( SIGINT, sigterm_handler ); + + if( verbose ) { + while(!quit) { + usleep(500000); + if( output_new_delay ) { + printf( "delay = %d\n", output_new_delay ); + output_new_delay = 0; + } + printf( "res: %f, \tdiff = %f, \toffset = %f \n", output_resampling_factor, output_diff, output_offset ); + } + } else if( instrument ) { + printf( "# n\tresamp\tdiff\toffseti\tintegral\n"); + int n=0; + while(!quit) { + usleep(1000); + printf( "%d\t%f\t%f\t%f\t%f\n", n++, output_resampling_factor, output_diff, output_offset, output_integral ); + } + } else { + while(!quit) + { + usleep(500000); + if( output_new_delay ) { + printf( "delay = %d\n", output_new_delay ); + output_new_delay = 0; + } + } + } + + jack_deactivate( client ); + jack_client_close (client); + exit (0); +} + From f5af54fe4fcc7cdca91a4cab16fdd03ae5f70a07 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Sun, 25 Oct 2009 20:22:23 +0100 Subject: [PATCH 06/20] Add alsa_io to wscript --- example-clients/wscript | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/example-clients/wscript b/example-clients/wscript index 5a17ba6e..6fda348a 100644 --- a/example-clients/wscript +++ b/example-clients/wscript @@ -120,6 +120,24 @@ def build(bld): prog.uselib_local = 'clientlib' prog.target = 'jack_netsource' + + if bld.env['IS_LINUX']: + prog = bld.new_task_gen('cc', 'program') + prog.includes = os_incdir + ['../common/jack', '../common'] + prog.source = ['alsa_in.c', '../common/memops.c'] + prog.env.append_value("CCFLAGS", "-DNO_JACK_ERROR") + prog.uselib = 'ALSA SAMPLERATE' + prog.uselib_local = 'clientlib' + prog.target = 'alsa_in' + + prog = bld.new_task_gen('cc', 'program') + prog.includes = os_incdir + ['../common/jack', '../common'] + prog.source = ['alsa_out.c', '../common/memops.c'] + prog.env.append_value("CCFLAGS", "-DNO_JACK_ERROR") + prog.uselib = 'ALSA SAMPLERATE' + prog.uselib_local = 'clientlib' + prog.target = 'alsa_out' + for example_lib, example_lib_source in example_libs.items(): lib = bld.new_task_gen('cc', 'shlib') lib.env['shlib_PATTERN'] = '%s.so' From be8d8f8d1fb90a2d12be16cba57d5b6febea652e Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Sun, 25 Oct 2009 21:45:10 +0100 Subject: [PATCH 07/20] increase sync timeout to 10seconds, because this can happen with netjack1 --- common/JackTransportEngine.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/JackTransportEngine.cpp b/common/JackTransportEngine.cpp index 418d1180..19168eea 100644 --- a/common/JackTransportEngine.cpp +++ b/common/JackTransportEngine.cpp @@ -38,7 +38,8 @@ JackTransportEngine::JackTransportEngine(): JackAtomicArrayState Date: Thu, 29 Oct 2009 00:58:17 +0100 Subject: [PATCH 08/20] inversion of deadline control feedback. this should have been fixed --- common/netjack.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/netjack.c b/common/netjack.c index 83436d7a..e21005ad 100644 --- a/common/netjack.c +++ b/common/netjack.c @@ -171,11 +171,11 @@ void netjack_wait( netjack_driver_state_t *netj ) */ if( netj->deadline_goodness < (netj->period_usecs/4+10*(int)netj->period_usecs*netj->latency/100) ) { - netj->next_deadline += netj->period_usecs/100; + netj->next_deadline -= netj->period_usecs/100; //jack_log( "goodness: %d, Adjust deadline: --- %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); } if( netj->deadline_goodness > (netj->period_usecs/4+10*(int)netj->period_usecs*netj->latency/100) ) { - netj->next_deadline -= netj->period_usecs/100; + netj->next_deadline += netj->period_usecs/100; //jack_log( "goodness: %d, Adjust deadline: +++ %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); } } else { From fe251673a95bd84751b089c12342da2bacf90294 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 29 Oct 2009 00:24:54 +0100 Subject: [PATCH 09/20] make netxrun an error --- common/netjack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/netjack.c b/common/netjack.c index e21005ad..ccca8b38 100644 --- a/common/netjack.c +++ b/common/netjack.c @@ -149,7 +149,7 @@ void netjack_wait( netjack_driver_state_t *netj ) netj->running_free = 0; if( !we_have_the_expected_frame ) - jack_log( "xrun... %d", netj->expected_framecnt ); + jack_error( "netxrun... %d", netj->expected_framecnt ); if( we_have_the_expected_frame ) { netj->time_to_deadline = netj->next_deadline - jack_get_time() - netj->period_usecs; From 2ac9617a97dcd81fbe45f6eac3b396bd1be43abd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Nettingsmeier?= Date: Thu, 29 Oct 2009 19:18:40 +0100 Subject: [PATCH 10/20] convert bitdepth magic number into global constant for CELT_MODE, no functional changes --- common/netjack.c | 8 ++++---- common/netjack_packet.c | 8 +++++--- common/netjack_packet.h | 2 ++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/common/netjack.c b/common/netjack.c index ccca8b38..16ae7f4f 100644 --- a/common/netjack.c +++ b/common/netjack.c @@ -364,7 +364,7 @@ void netjack_attach( netjack_driver_state_t *netj ) netj->capture_ports = jack_slist_append (netj->capture_ports, port); - if( netj->bitdepth == 1000 ) { + if( netj->bitdepth == CELT_MODE ) { #if HAVE_CELT celt_int32_t lookahead; // XXX: memory leak @@ -411,7 +411,7 @@ void netjack_attach( netjack_driver_state_t *netj ) netj->playback_ports = jack_slist_append (netj->playback_ports, port); - if( netj->bitdepth == 1000 ) { + if( netj->bitdepth == CELT_MODE ) { #if HAVE_CELT // XXX: memory leak CELTMode *celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL ); @@ -517,7 +517,7 @@ netjack_driver_state_t *netjack_init (netjack_driver_state_t *netj, netj->client = client; - if ((bitdepth != 0) && (bitdepth != 8) && (bitdepth != 16) && (bitdepth != 1000)) + if ((bitdepth != 0) && (bitdepth != 8) && (bitdepth != 16) && (bitdepth != CELT_MODE)) { jack_info ("Invalid bitdepth: %d (8, 16 or 0 for float) !!!", bitdepth); return NULL; @@ -655,7 +655,7 @@ netjack_startup( netjack_driver_state_t *netj ) (jack_time_t) floor ((((float) netj->period_size) / (float)netj->sample_rate) * 1000000.0f); - if( netj->bitdepth == 1000 ) { + if( netj->bitdepth == CELT_MODE ) { // celt mode. // TODO: this is a hack. But i dont want to change the packet header. netj->net_period_down = netj->resample_factor; diff --git a/common/netjack_packet.c b/common/netjack_packet.c index cd51dca9..66adfe28 100644 --- a/common/netjack_packet.c +++ b/common/netjack_packet.c @@ -132,7 +132,9 @@ int get_sample_size (int bitdepth) return sizeof (int8_t); if (bitdepth == 16) return sizeof (int16_t); - if( bitdepth == 1000 ) + //JN: why? is this for buffer sizes before or after encoding? + //JN: if the former, why not int16_t, if the latter, shouldn't it depend on -c N? + if( bitdepth == CELT_MODE ) return sizeof( unsigned char ); return sizeof (int32_t); } @@ -1504,7 +1506,7 @@ render_payload_to_jack_ports (int bitdepth, void *packet_payload, jack_nframes_t else if (bitdepth == 16) render_payload_to_jack_ports_16bit (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); #if HAVE_CELT - else if (bitdepth == 1000) + else if (bitdepth == CELT_MODE) render_payload_to_jack_ports_celt (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); #endif else @@ -1519,7 +1521,7 @@ render_jack_ports_to_payload (int bitdepth, JSList *playback_ports, JSList *play else if (bitdepth == 16) render_jack_ports_to_payload_16bit (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); #if HAVE_CELT - else if (bitdepth == 1000) + else if (bitdepth == CELT_MODE) render_jack_ports_to_payload_celt (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); #endif else diff --git a/common/netjack_packet.h b/common/netjack_packet.h index 7b21b0ee..ad752b26 100644 --- a/common/netjack_packet.h +++ b/common/netjack_packet.h @@ -42,6 +42,8 @@ //#include // The Packet Header. +#define CELT_MODE 1000 // Magic bitdepth value that indicates CELT compression + typedef struct _jacknet_packet_header jacknet_packet_header; struct _jacknet_packet_header From 85a981e76017fda0b224de008524f29297f5d035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Nettingsmeier?= Date: Thu, 29 Oct 2009 19:23:07 +0100 Subject: [PATCH 11/20] CELT_MODE, part2 --- common/JackNetOneDriver.cpp | 12 ++++++------ common/JackNetOneDriver.h | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/common/JackNetOneDriver.cpp b/common/JackNetOneDriver.cpp index b9de94de..ecc1961b 100644 --- a/common/JackNetOneDriver.cpp +++ b/common/JackNetOneDriver.cpp @@ -161,7 +161,7 @@ namespace Jack netj.capture_ports = jack_slist_append (netj.capture_ports, (void *)(intptr_t)port_id); - if( netj.bitdepth == 1000 ) { + if( netj.bitdepth == CELT_MODE ) { #if HAVE_CELT celt_int32_t lookahead; // XXX: memory leak @@ -208,7 +208,7 @@ namespace Jack netj.playback_ports = jack_slist_append (netj.playback_ports, (void *)(intptr_t)port_id); - if( netj.bitdepth == 1000 ) { + if( netj.bitdepth == CELT_MODE ) { #if HAVE_CELT // XXX: memory leak CELTMode *celt_mode = celt_mode_create( netj.sample_rate, 1, netj.period_size, NULL ); @@ -458,7 +458,7 @@ JackNetOneDriver::FreePorts () } netj.playback_ports = NULL; - if( netj.bitdepth == 1000 ) { + if( netj.bitdepth == CELT_MODE ) { #if HAVE_CELT node = netj.playback_srcs; while( node != NULL ) { @@ -778,7 +778,7 @@ void JackNetOneDriver::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, int dont_htonl_floats) { #if HAVE_CELT - if (bitdepth == 1000) + if (bitdepth == CELT_MODE) render_payload_to_jack_ports_celt (packet_payload, net_period_down, capture_ports, capture_srcs, nframes); else #endif @@ -789,7 +789,7 @@ void JackNetOneDriver::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, int dont_htonl_floats) { #if HAVE_CELT - if (bitdepth == 1000) + if (bitdepth == CELT_MODE) render_jack_ports_to_payload_celt (playback_ports, playback_srcs, nframes, packet_payload, net_period_up); else #endif @@ -1048,7 +1048,7 @@ JackNetOneDriver::render_jack_ports_to_payload (int bitdepth, JSList *playback_p case 'c': #if HAVE_CELT - bitdepth = 1000; + bitdepth = CELT_MODE; resample_factor = param->value.ui; #else jack_error( "not built with celt support\n" ); diff --git a/common/JackNetOneDriver.h b/common/JackNetOneDriver.h index ce30e06e..5cad7667 100644 --- a/common/JackNetOneDriver.h +++ b/common/JackNetOneDriver.h @@ -23,6 +23,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #include "JackAudioDriver.h" #include "netjack.h" +#include "netjack_packet.h" namespace Jack { From 114a9a72c6a4c5a8956243881d3cce7b9a805b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Nettingsmeier?= Date: Thu, 29 Oct 2009 21:05:01 +0100 Subject: [PATCH 12/20] semi-pointless finger exercise: port type conditional factored out --- common/netjack_packet.c | 55 ++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/common/netjack_packet.c b/common/netjack_packet.c index 66adfe28..cae417e7 100644 --- a/common/netjack_packet.c +++ b/common/netjack_packet.c @@ -139,6 +139,17 @@ int get_sample_size (int bitdepth) return sizeof (int32_t); } +int jack_port_is_audio(const char *porttype) +{ + return (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0); +} + +int jack_port_is_midi(const char *porttype) +{ + return (strncmp (porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0); +} + + // fragment management functions. packet_cache @@ -985,7 +996,7 @@ render_payload_to_jack_ports_float ( void *packet_payload, jack_nframes_t net_pe const char *porttype = jack_port_type (port); - if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + if (jack_port_is_audio (porttype)) { #if HAVE_SAMPLERATE // audio port, resample if necessary @@ -1028,7 +1039,7 @@ render_payload_to_jack_ports_float ( void *packet_payload, jack_nframes_t net_pe } } } - else if (strncmp (porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + else if (jack_port_is_midi (porttype)) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) @@ -1065,7 +1076,7 @@ render_jack_ports_to_payload_float (JSList *playback_ports, JSList *playback_src const char *porttype = jack_port_type (port); - if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + if (jack_port_is_audio (porttype)) { // audio port, resample if necessary @@ -1108,7 +1119,7 @@ render_jack_ports_to_payload_float (JSList *playback_ports, JSList *playback_src } } } - else if (strncmp(porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + else if (jack_port_is_midi (porttype)) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) @@ -1151,9 +1162,9 @@ render_payload_to_jack_ports_16bit (void *packet_payload, jack_nframes_t net_per #if HAVE_SAMPLERATE float *floatbuf = alloca (sizeof(float) * net_period_down); #endif - const char *portname = jack_port_type (port); + const char *porttype = jack_port_type (port); - if (strncmp(portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + if (jack_port_is_audio (porttype)) { // audio port, resample if necessary @@ -1184,7 +1195,7 @@ render_payload_to_jack_ports_16bit (void *packet_payload, jack_nframes_t net_per for (i = 0; i < net_period_down; i++) buf[i] = ((float) ntohs (packet_bufX[i])) / 32768.0 - 1.0; } - else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + else if (jack_port_is_midi (porttype)) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) @@ -1217,9 +1228,9 @@ render_jack_ports_to_payload_16bit (JSList *playback_ports, JSList *playback_src int i; jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); - const char *portname = jack_port_type (port); + const char *porttype = jack_port_type (port); - if (strncmp (portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + if (jack_port_is_audio (porttype)) { // audio port, resample if necessary @@ -1253,7 +1264,7 @@ render_jack_ports_to_payload_16bit (JSList *playback_ports, JSList *playback_src for (i = 0; i < net_period_up; i++) packet_bufX[i] = htons(((uint16_t)((buf[i] + 1.0) * 32767.0))); } - else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + else if (jack_port_is_midi (porttype)) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) @@ -1297,9 +1308,9 @@ render_payload_to_jack_ports_8bit (void *packet_payload, jack_nframes_t net_peri #if HAVE_SAMPLERATE float *floatbuf = alloca (sizeof (float) * net_period_down); #endif - const char *portname = jack_port_type (port); + const char *porttype = jack_port_type (port); - if (strncmp (portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + if (jack_port_is_audio(porttype)) { #if HAVE_SAMPLERATE // audio port, resample if necessary @@ -1327,7 +1338,7 @@ render_payload_to_jack_ports_8bit (void *packet_payload, jack_nframes_t net_peri for (i = 0; i < net_period_down; i++) buf[i] = ((float) packet_bufX[i]) / 127.0; } - else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + else if (jack_port_is_midi (porttype)) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) @@ -1361,9 +1372,9 @@ render_jack_ports_to_payload_8bit (JSList *playback_ports, JSList *playback_srcs jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); - const char *portname = jack_port_type (port); + const char *porttype = jack_port_type (port); - if (strncmp (portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + if (jack_port_is_audio (porttype)) { #if HAVE_SAMPLERATE // audio port, resample if necessary @@ -1395,7 +1406,7 @@ render_jack_ports_to_payload_8bit (JSList *playback_ports, JSList *playback_srcs for (i = 0; i < net_period_up; i++) packet_bufX[i] = buf[i] * 127.0; } - else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + else if (jack_port_is_midi (porttype)) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) @@ -1425,9 +1436,9 @@ render_payload_to_jack_ports_celt (void *packet_payload, jack_nframes_t net_peri jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); - const char *portname = jack_port_type (port); + const char *porttype = jack_port_type (port); - if (strncmp(portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + if (jack_port_is_audio (porttype)) { // audio port, decode celt data. @@ -1439,7 +1450,7 @@ render_payload_to_jack_ports_celt (void *packet_payload, jack_nframes_t net_peri src_node = jack_slist_next (src_node); } - else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + else if (jack_port_is_midi (porttype)) { // midi port, decode midi events // convert the data buffer to a standard format (uint32_t based) @@ -1467,9 +1478,9 @@ render_jack_ports_to_payload_celt (JSList *playback_ports, JSList *playback_srcs { jack_port_t *port = (jack_port_t *) node->data; jack_default_audio_sample_t* buf = jack_port_get_buffer (port, nframes); - const char *portname = jack_port_type (port); + const char *porttype = jack_port_type (port); - if (strncmp (portname, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size()) == 0) + if (jack_port_is_audio (porttype)) { // audio port, encode celt data. @@ -1482,7 +1493,7 @@ render_jack_ports_to_payload_celt (JSList *playback_ports, JSList *playback_srcs printf( "something in celt changed. netjack needs to be changed to handle this.\n" ); src_node = jack_slist_next( src_node ); } - else if (strncmp(portname, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size()) == 0) + else if (jack_port_is_midi (porttype)) { // encode midi events from port to packet // convert the data buffer to a standard format (uint32_t based) From a568aa419df3db6a1fd44302729d75c8895605b6 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 29 Oct 2009 03:35:47 +0100 Subject: [PATCH 13/20] add jack_name to the xrun message --- example-clients/netsource.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/example-clients/netsource.c b/example-clients/netsource.c index 40da134c..6af47d80 100644 --- a/example-clients/netsource.c +++ b/example-clients/netsource.c @@ -666,10 +666,13 @@ main (int argc, char *argv[]) { if (statecopy_netxruns != state_netxruns) { statecopy_netxruns = state_netxruns; - printf ("at frame %06d -> total netxruns %d (%d%%) queue time= %d\n", state_currentframe, - statecopy_netxruns, - 100*statecopy_netxruns/state_currentframe, - state_recv_packet_queue_time); + printf ("%s: at frame %06d -> total netxruns %d (%d%%) queue time= %d\n", + client_name, + state_currentframe, + statecopy_netxruns, + 100*statecopy_netxruns/state_currentframe, + state_recv_packet_queue_time); + fflush(stdout); } } From cecab033b509e34b3ff6db08bf9309c2bfde8a47 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Wed, 4 Nov 2009 15:06:30 +0100 Subject: [PATCH 14/20] merge jack1 and jacknone netsource --- example-clients/netsource.c | 127 ++++++++++++++++++++++++------------ 1 file changed, 84 insertions(+), 43 deletions(-) diff --git a/example-clients/netsource.c b/example-clients/netsource.c index 6af47d80..2a7c6376 100644 --- a/example-clients/netsource.c +++ b/example-clients/netsource.c @@ -266,50 +266,51 @@ process (jack_nframes_t nframes, void *arg) jacknet_packet_header *pkthdr = (jacknet_packet_header *) packet_buf; /* - * ok... SEND code first. - * needed some time to find out why latency=0 - * did not work ;S + * for latency==0 we need to send out the packet before we wait on the reply. + * but this introduces a cycle of latency, when netsource is connected to itself. + * so we send out before read only in zero latency mode. * */ - /* reset packet_bufX... */ - 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, dont_htonl_floats); - - /* 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; - - /* playback for us is capture on the other side */ - pkthdr->capture_channels_audio = playback_channels_audio; - pkthdr->playback_channels_audio = capture_channels_audio; - pkthdr->capture_channels_midi = playback_channels_midi; - pkthdr->playback_channels_midi = capture_channels_midi; - pkthdr->mtu = mtu; - pkthdr->sync_state = (jack_nframes_t)deadline_goodness; - //printf("goodness=%d\n", deadline_goodness ); - - packet_header_hton (pkthdr); - if (cont_miss < 3*latency+5) { - int r; - for( r=0; r 50+5*latency) - { - state_connected = 0; - packet_cache_reset_master_address( global_packcache ); - //printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss); - cont_miss = 0; + if( latency == 0 ) { + /* reset packet_bufX... */ + 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, dont_htonl_floats); + + /* 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; + + /* playback for us is capture on the other side */ + pkthdr->capture_channels_audio = playback_channels_audio; + pkthdr->playback_channels_audio = capture_channels_audio; + pkthdr->capture_channels_midi = playback_channels_midi; + pkthdr->playback_channels_midi = capture_channels_midi; + pkthdr->mtu = mtu; + pkthdr->sync_state = (jack_nframes_t)deadline_goodness; + //printf("goodness=%d\n", deadline_goodness ); + + packet_header_hton (pkthdr); + if (cont_miss < 3*latency+5) { + int r; + for( r=0; r 50+5*latency) + { + state_connected = 0; + packet_cache_reset_master_address( global_packcache ); + //printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss); + cont_miss = 0; + } } /* @@ -407,6 +408,46 @@ process (jack_nframes_t nframes, void *arg) chn++; } } + if( latency != 0 ) { + /* reset packet_bufX... */ + 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, dont_htonl_floats); + + /* 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; + + /* playback for us is capture on the other side */ + pkthdr->capture_channels_audio = playback_channels_audio; + pkthdr->playback_channels_audio = capture_channels_audio; + pkthdr->capture_channels_midi = playback_channels_midi; + pkthdr->playback_channels_midi = capture_channels_midi; + pkthdr->mtu = mtu; + pkthdr->sync_state = (jack_nframes_t)deadline_goodness; + //printf("goodness=%d\n", deadline_goodness ); + + packet_header_hton (pkthdr); + if (cont_miss < 3*latency+5) { + int r; + for( r=0; r 50+5*latency) + { + state_connected = 0; + packet_cache_reset_master_address( global_packcache ); + //printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss); + cont_miss = 0; + } + } framecnt++; return 0; @@ -437,8 +478,8 @@ fflush( stdout ); struct hostent *hostinfo = gethostbyname (hostname); if (hostinfo == NULL) { fprintf (stderr, "init_sockaddr_in: unknown host: %s.\n", hostname); - fflush( stderr ); - } + fflush( stderr ); + } #ifdef WIN32 name->sin_addr.s_addr = inet_addr( hostname ); #else From 3d88e94c93d200a32d5f9a7fd655c4c1708c7578 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 5 Nov 2009 14:48:56 +0100 Subject: [PATCH 15/20] rework of deadline machinery and support for freewheeling Conflicts: common/netjack_packet.h --- common/netjack.c | 42 ++++++++++++++++++------------------- common/netjack.h | 1 + common/netjack_packet.h | 1 + example-clients/netsource.c | 27 +++++++++++++++++++----- 4 files changed, 44 insertions(+), 27 deletions(-) diff --git a/common/netjack.c b/common/netjack.c index 16ae7f4f..274e63eb 100644 --- a/common/netjack.c +++ b/common/netjack.c @@ -93,22 +93,8 @@ void netjack_wait( netjack_driver_state_t *netj ) jacknet_packet_header *pkthdr; if( !netj->next_deadline_valid ) { - if( netj->latency == 0 ) - // for full sync mode... always wait for packet. - netj->next_deadline = jack_get_time() + 50*netj->period_usecs; - else if( netj->latency == 1 ) - // for normal 1 period latency mode, only 1 period for dealine. - netj->next_deadline = jack_get_time() + 110 * netj->period_usecs /100; - else - // looks like waiting 1 period always is correct. - // not 100% sure yet. with the improved resync, it might be better, - // to have more than one period headroom for high latency. - //netj->next_deadline = jack_get_time() + 5*netj->latency*netj->period_usecs/4; - netj->next_deadline = jack_get_time() + netj->period_usecs + 10*netj->latency*netj->period_usecs/100; - + netj->next_deadline = jack_get_time() + netj->deadline_offset; netj->next_deadline_valid = 1; - } else { - netj->next_deadline += netj->period_usecs; } // Increment expected frame here. @@ -170,16 +156,23 @@ void netjack_wait( netjack_driver_state_t *netj ) netj->next_deadline += netj->period_usecs/1000; */ - if( netj->deadline_goodness < (netj->period_usecs/4+10*(int)netj->period_usecs*netj->latency/100) ) { - netj->next_deadline -= netj->period_usecs/100; - //jack_log( "goodness: %d, Adjust deadline: --- %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); - } - if( netj->deadline_goodness > (netj->period_usecs/4+10*(int)netj->period_usecs*netj->latency/100) ) { - netj->next_deadline += netj->period_usecs/100; - //jack_log( "goodness: %d, Adjust deadline: +++ %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); + if( netj->deadline_goodness != MASTER_FREEWHEELS ) { + if( netj->deadline_goodness < (netj->period_usecs/4+10*(int)netj->period_usecs*netj->latency/100) ) { + netj->deadline_offset -= netj->period_usecs/100; + //jack_log( "goodness: %d, Adjust deadline: --- %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); + } + if( netj->deadline_goodness > (netj->period_usecs/4+10*(int)netj->period_usecs*netj->latency/100) ) { + netj->deadline_offset += netj->period_usecs/100; + //jack_log( "goodness: %d, Adjust deadline: +++ %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); + } } + + jack_log( "deadline_offset: %d", (int) netj->deadline_offset ); + + netj->next_deadline = jack_get_time() + netj->deadline_offset; } else { netj->time_to_deadline = 0; + netj->next_deadline = jack_get_time() + netj->deadline_offset; // bah... the packet is not there. // either // - it got lost. @@ -654,6 +647,11 @@ netjack_startup( netjack_driver_state_t *netj ) netj->period_usecs = (jack_time_t) floor ((((float) netj->period_size) / (float)netj->sample_rate) * 1000000.0f); + + if( netj->latency == 0 ) + netj->deadline_offset = 50*netj->period_usecs; + else + netj->deadline_offset = netj->period_usecs + 10*netj->latency*netj->period_usecs/100; if( netj->bitdepth == CELT_MODE ) { // celt mode. diff --git a/common/netjack.h b/common/netjack.h index db30377a..761a664a 100644 --- a/common/netjack.h +++ b/common/netjack.h @@ -96,6 +96,7 @@ struct _netjack_driver_state { int expected_framecnt_valid; unsigned int num_lost_packets; jack_time_t next_deadline; + jack_time_t deadline_offset; int next_deadline_valid; int packet_data_valid; int resync_threshold; diff --git a/common/netjack_packet.h b/common/netjack_packet.h index ad752b26..4617310e 100644 --- a/common/netjack_packet.h +++ b/common/netjack_packet.h @@ -43,6 +43,7 @@ // The Packet Header. #define CELT_MODE 1000 // Magic bitdepth value that indicates CELT compression +#define MASTER_FREEWHEELS 0x80000000 typedef struct _jacknet_packet_header jacknet_packet_header; diff --git a/example-clients/netsource.c b/example-clients/netsource.c index 2a7c6376..afc29ff2 100644 --- a/example-clients/netsource.c +++ b/example-clients/netsource.c @@ -110,6 +110,8 @@ int framecnt = 0; int cont_miss = 0; +int freewheeling = 0; + /** * This Function allocates all the I/O Ports which are added the lists. */ @@ -226,6 +228,12 @@ sync_cb (jack_transport_state_t state, jack_position_t *pos, void *arg) return retval; } +void +freewheel_cb (int starting, void *arg) +{ + freewheeling = starting; +} + int deadline_goodness=0; /** * The process callback for this JACK application. @@ -295,7 +303,10 @@ process (jack_nframes_t nframes, void *arg) pkthdr->capture_channels_midi = playback_channels_midi; pkthdr->playback_channels_midi = capture_channels_midi; pkthdr->mtu = mtu; - pkthdr->sync_state = (jack_nframes_t)deadline_goodness; + if( freewheeling!= 0 ) + pkthdr->sync_state = (jack_nframes_t)MASTER_FREEWHEELS; + else + pkthdr->sync_state = (jack_nframes_t)deadline_goodness; //printf("goodness=%d\n", deadline_goodness ); packet_header_hton (pkthdr); @@ -327,17 +338,19 @@ process (jack_nframes_t nframes, void *arg) input_fd = outsockfd; // for latency == 0 we can poll. - if( latency == 0 ) { + if( (latency == 0) || (freewheeling!=0) ) { jack_time_t deadline = jack_get_time() + 1000000 * jack_get_buffer_size(client)/jack_get_sample_rate(client); // Now loop until we get the right packet. while(1) { + jack_nframes_t got_frame; if ( ! netjack_poll_deadline( input_fd, deadline ) ) break; packet_cache_drain_socket(global_packcache, input_fd); - if (packet_cache_get_next_available_framecnt( global_packcache, framecnt - latency, NULL )) - break; + if (packet_cache_get_next_available_framecnt( global_packcache, framecnt - latency, &got_frame )) + if( got_frame == (framecnt - latency) ) + break; } } else { // normally: @@ -431,7 +444,10 @@ process (jack_nframes_t nframes, void *arg) pkthdr->capture_channels_midi = playback_channels_midi; pkthdr->playback_channels_midi = capture_channels_midi; pkthdr->mtu = mtu; - pkthdr->sync_state = (jack_nframes_t)deadline_goodness; + if( freewheeling!= 0 ) + pkthdr->sync_state = (jack_nframes_t)MASTER_FREEWHEELS; + else + pkthdr->sync_state = (jack_nframes_t)deadline_goodness; //printf("goodness=%d\n", deadline_goodness ); packet_header_hton (pkthdr); @@ -657,6 +673,7 @@ main (int argc, char *argv[]) /* Set up jack callbacks */ jack_set_process_callback (client, process, 0); jack_set_sync_callback (client, sync_cb, 0); + jack_set_freewheel_callback (client, freewheel_cb, 0); jack_on_shutdown (client, jack_shutdown, 0); alloc_ports (capture_channels_audio, playback_channels_audio, capture_channels_midi, playback_channels_midi); From ac1853a42284148153a2d8c7f14c382c66820898 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Wed, 4 Nov 2009 22:55:32 +0100 Subject: [PATCH 16/20] prevent deadlines which are a second in the future. unlikely to happen with the new code... but still. --- common/netjack_packet.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/netjack_packet.c b/common/netjack_packet.c index cae417e7..0af43d58 100644 --- a/common/netjack_packet.c +++ b/common/netjack_packet.c @@ -387,6 +387,10 @@ netjack_poll_deadline (int sockfd, jack_time_t deadline) if( now >= deadline ) return 0; + if( (deadline-now) >= 1000000 ) { + jack_error( "deadline more than 1 second in the future, trimming it." ); + deadline = now+500000; + } #if HAVE_PPOLL timeout_spec.tv_nsec = (deadline - now) * 1000; #else @@ -529,6 +533,7 @@ netjack_poll_deadline (int sockfd, jack_time_t deadline) //jack_error( "timeout = %d", timeout_usecs ); timeout.tv_sec = 0; timeout.tv_usec = (timeout_usecs < 500) ? 500 : timeout_usecs; + timeout.tv_usec = (timeout_usecs > 1000000) ? 500000 : timeout_usecs; int poll_err = select (0, &fds, NULL, NULL, &timeout); if( poll_err != 0 ) From 49fdfa6d7ea86c990d9fc84a47692a92040b81f9 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 5 Nov 2009 00:48:07 +0100 Subject: [PATCH 17/20] remove deadline_offset logging. --- common/netjack.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/common/netjack.c b/common/netjack.c index 274e63eb..6cb48d2e 100644 --- a/common/netjack.c +++ b/common/netjack.c @@ -167,8 +167,6 @@ void netjack_wait( netjack_driver_state_t *netj ) } } - jack_log( "deadline_offset: %d", (int) netj->deadline_offset ); - netj->next_deadline = jack_get_time() + netj->deadline_offset; } else { netj->time_to_deadline = 0; From 698a5d28f3f8b9357a154cca193fa647fb99eb52 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 5 Nov 2009 14:51:03 +0100 Subject: [PATCH 18/20] make celt api version flexible. Conflicts: example-clients/wscript --- common/netjack.c | 18 ++++++++++++------ common/wscript | 2 -- example-clients/netsource.c | 15 ++++++++++----- example-clients/wscript | 5 +++++ linux/wscript | 5 +++++ wscript | 9 +++++++++ 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/common/netjack.c b/common/netjack.c index 6cb48d2e..6bee2c4c 100644 --- a/common/netjack.c +++ b/common/netjack.c @@ -25,8 +25,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. $Id: net_driver.c,v 1.17 2006/04/16 20:16:10 torbenh Exp $ */ -#define HAVE_CELT 1 - #include #include @@ -357,13 +355,17 @@ void netjack_attach( netjack_driver_state_t *netj ) if( netj->bitdepth == CELT_MODE ) { #if HAVE_CELT +#if HAVE_CELT_API_0_7 + celt_int32 lookahead; + CELTMode *celt_mode = celt_mode_create( netj->sample_rate, netj->period_size, NULL ); + netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( celt_mode, 1, NULL ) ); +#else celt_int32_t lookahead; - // XXX: memory leak CELTMode *celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL ); + netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( celt_mode ) ); +#endif celt_mode_info( celt_mode, CELT_GET_LOOKAHEAD, &lookahead ); netj->codec_latency = 2*lookahead; - - netj->capture_srcs = jack_slist_append(netj->capture_srcs, celt_decoder_create( celt_mode ) ); #endif } else { #if HAVE_SAMPLERATE @@ -404,9 +406,13 @@ void netjack_attach( netjack_driver_state_t *netj ) jack_slist_append (netj->playback_ports, port); if( netj->bitdepth == CELT_MODE ) { #if HAVE_CELT - // XXX: memory leak +#if HAVE_CELT_API_0_7 + CELTMode *celt_mode = celt_mode_create( netj->sample_rate, netj->period_size, NULL ); + netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_encoder_create( celt_mode, 1, NULL ) ); +#else CELTMode *celt_mode = celt_mode_create( netj->sample_rate, 1, netj->period_size, NULL ); netj->playback_srcs = jack_slist_append(netj->playback_srcs, celt_encoder_create( celt_mode ) ); +#endif #endif } else { #if HAVE_SAMPLERATE diff --git a/common/wscript b/common/wscript index 46d7b12b..6089ff81 100644 --- a/common/wscript +++ b/common/wscript @@ -11,8 +11,6 @@ def configure(conf): if conf.is_defined('HAVE_SAMPLERATE'): conf.env['LIB_SAMPLERATE'] = ['samplerate'] - conf.check_cfg(package='celt', atleast_version='0.5.0', args='--cflags --libs') - conf.env['BUILD_ADAPTER'] = conf.is_defined('HAVE_SAMPLERATE') def create_jack_process_obj(bld, target, sources, uselib = None): diff --git a/example-clients/netsource.c b/example-clients/netsource.c index afc29ff2..f79ac8da 100644 --- a/example-clients/netsource.c +++ b/example-clients/netsource.c @@ -26,9 +26,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * @brief This client connects a remote slave JACK to a local JACK server assumed to be the master */ -//#include "config.h" -#define HAVE_CELT 1 - #include #include @@ -137,9 +134,13 @@ alloc_ports (int n_capture_audio, int n_playback_audio, int n_capture_midi, int } if( bitdepth == 1000 ) { #if HAVE_CELT - // XXX: memory leak +#if HAVE_CELT_API_0_7 + CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate( client ), jack_get_buffer_size(client), NULL ); + capture_srcs = jack_slist_append(capture_srcs, celt_decoder_create( celt_mode, 1, NULL ) ); +#else CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate( client ), 1, jack_get_buffer_size(client), NULL ); capture_srcs = jack_slist_append(capture_srcs, celt_decoder_create( celt_mode ) ); +#endif #endif } else { #if HAVE_SAMPLERATE @@ -176,9 +177,13 @@ alloc_ports (int n_capture_audio, int n_playback_audio, int n_capture_midi, int } if( bitdepth == 1000 ) { #if HAVE_CELT - // XXX: memory leak +#if HAVE_CELT_API_0_7 + CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate (client), jack_get_buffer_size(client), NULL ); + playback_srcs = jack_slist_append(playback_srcs, celt_encoder_create( celt_mode, 1, NULL ) ); +#else CELTMode *celt_mode = celt_mode_create( jack_get_sample_rate (client), 1, jack_get_buffer_size(client), NULL ); playback_srcs = jack_slist_append(playback_srcs, celt_encoder_create( celt_mode ) ); +#endif #endif } else { #if HAVE_SAMPLERATE diff --git a/example-clients/wscript b/example-clients/wscript index 6fda348a..34e750ad 100644 --- a/example-clients/wscript +++ b/example-clients/wscript @@ -116,6 +116,11 @@ def build(bld): prog.includes = os_incdir + ['../common/jack', '../common'] prog.source = ['netsource.c', '../common/netjack_packet.c'] prog.env.append_value("CCFLAGS", "-DNO_JACK_ERROR") + if bld.env['HAVE_CELT']: + if bld.env['HAVE_CELT_API_0_5']: + prog.defines = ['HAVE_CELT', 'HAVE_CELT_API_0_5'] + elif bld.env['HAVE_CELT_API_0_7']: + prog.defines = ['HAVE_CELT', 'HAVE_CELT_API_0_7'] prog.uselib = 'CELT SAMPLERATE' prog.uselib_local = 'clientlib' diff --git a/linux/wscript b/linux/wscript index adc3478b..fc06fd3f 100644 --- a/linux/wscript +++ b/linux/wscript @@ -16,6 +16,11 @@ def create_jack_driver_obj(bld, target, sources, uselib = None): driver.features.append('cc') driver.env['shlib_PATTERN'] = 'jack_%s.so' driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE', 'HAVE_PPOLL'] + if bld.env['HAVE_CELT']: + if bld.env['HAVE_CELT_API_0_5']: + driver.defines += ['HAVE_CELT', 'HAVE_CELT_API_0_5'] + elif bld.env['HAVE_CELT_API_0_7']: + driver.defines += ['HAVE_CELT', 'HAVE_CELT_API_0_7'] driver.includes = ['.', '../linux', '../posix', '../common', '../common/jack', '../dbus'] driver.target = target driver.source = sources diff --git a/wscript b/wscript index 6fb5c88c..eec01dc3 100644 --- a/wscript +++ b/wscript @@ -224,6 +224,15 @@ def configure(conf): else: conf.env['LIBDIR'] = conf.env['PREFIX'] + '/lib32' + if conf.check_cfg(package='celt', atleast_version='0.7.0', args='--cflags --libs'): + conf.env['HAVE_CELT'] = True + conf.env['HAVE_CELT_API_0_7'] = True + elif conf.check_cfg(package='celt', atleast_version='0.5.0', args='--cflags --libs', required=True): + conf.env['HAVE_CELT'] = True + conf.env['HAVE_CELT_API_0_5'] = True + else: + conf.env['HAVE_CELT'] = False + def build(bld): print ("make[1]: Entering directory `" + os.getcwd() + "/" + blddir + "'" ) if not os.access('svnversion.h', os.R_OK): From c5136fcf7a7d45fcbc23c3a128d409bed7b23778 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 5 Nov 2009 01:53:33 +0100 Subject: [PATCH 19/20] hope this works for osx --- macosx/wscript | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/macosx/wscript b/macosx/wscript index 25ca9b3d..1a1c9615 100644 --- a/macosx/wscript +++ b/macosx/wscript @@ -6,6 +6,11 @@ def create_jack_driver_obj(bld, target, sources, uselib = None): driver.features.append('cc') driver.env['shlib_PATTERN'] = 'jack_%s.so' driver.defines = ['HAVE_CONFIG_H','SERVER_SIDE'] + if bld.env['HAVE_CELT']: + if bld.env['HAVE_CELT_API_0_5']: + driver.defines += ['HAVE_CELT', 'HAVE_CELT_API_0_5'] + elif bld.env['HAVE_CELT_API_0_7']: + driver.defines += ['HAVE_CELT', 'HAVE_CELT_API_0_7'] driver.includes = ['.', '../macosx', '../posix', '../common', '../common/jack'] driver.target = target driver.source = sources From 91c17b259a88b87a9042a5c15c5abe0cabea9d98 Mon Sep 17 00:00:00 2001 From: Torben Hohn Date: Thu, 5 Nov 2009 10:35:18 +0100 Subject: [PATCH 20/20] forgot some occurences of celt initilisation... doh. --- common/JackNetOneDriver.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/common/JackNetOneDriver.cpp b/common/JackNetOneDriver.cpp index ecc1961b..b6703432 100644 --- a/common/JackNetOneDriver.cpp +++ b/common/JackNetOneDriver.cpp @@ -163,13 +163,17 @@ namespace Jack if( netj.bitdepth == CELT_MODE ) { #if HAVE_CELT - celt_int32_t lookahead; - // XXX: memory leak - CELTMode *celt_mode = celt_mode_create( netj.sample_rate, 1, netj.period_size, NULL ); - celt_mode_info( celt_mode, CELT_GET_LOOKAHEAD, &lookahead ); - netj.codec_latency = 2*lookahead; - - netj.capture_srcs = jack_slist_append(netj.capture_srcs, (void *)celt_decoder_create( celt_mode ) ); +#if HAVE_CELT_API_0_7 + celt_int32 lookahead; + CELTMode *celt_mode = celt_mode_create( netj.sample_rate, netj.period_size, NULL ); + netj.capture_srcs = jack_slist_append(netj.capture_srcs, celt_decoder_create( celt_mode, 1, NULL ) ); +#else + celt_int32_t lookahead; + CELTMode *celt_mode = celt_mode_create( netj.sample_rate, 1, netj.period_size, NULL ); + netj.capture_srcs = jack_slist_append(netj.capture_srcs, celt_decoder_create( celt_mode ) ); +#endif + celt_mode_info( celt_mode, CELT_GET_LOOKAHEAD, &lookahead ); + netj.codec_latency = 2*lookahead; #endif } else { #if HAVE_SAMPLERATE @@ -210,9 +214,13 @@ namespace Jack if( netj.bitdepth == CELT_MODE ) { #if HAVE_CELT - // XXX: memory leak - CELTMode *celt_mode = celt_mode_create( netj.sample_rate, 1, netj.period_size, NULL ); - netj.playback_srcs = jack_slist_append(netj.playback_srcs, (void *)celt_encoder_create( celt_mode ) ); +#if HAVE_CELT_API_0_7 + CELTMode *celt_mode = celt_mode_create( netj.sample_rate, netj.period_size, NULL ); + netj.playback_srcs = jack_slist_append(netj.playback_srcs, celt_encoder_create( celt_mode, 1, NULL ) ); +#else + CELTMode *celt_mode = celt_mode_create( netj.sample_rate, 1, netj.period_size, NULL ); + netj.playback_srcs = jack_slist_append(netj.playback_srcs, celt_encoder_create( celt_mode ) ); +#endif #endif } else { #if HAVE_SAMPLERATE