summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortorben <torben@0c269be4-1314-0410-8aa9-9f06e86f4224>2009-11-06 11:14:15 +0000
committertorben <torben@0c269be4-1314-0410-8aa9-9f06e86f4224>2009-11-06 11:14:15 +0000
commit73b23245a9f29ac479ec7d7313a97a0fb60b25e0 (patch)
treefb186e91ba86c74b0adcbce240f0357784b74df8
parentbc1de2953eb7b0c764088176478a7fb7122e7697 (diff)
downloadjack1-73b23245a9f29ac479ec7d7313a97a0fb60b25e0.tar.gz
[netjack] code reuse part2: move non driver specific code to netjack.c
git-svn-id: svn+ssh://jackaudio.org/trunk/jack@3702 0c269be4-1314-0410-8aa9-9f06e86f4224
-rw-r--r--drivers/netjack/Makefile.am2
-rw-r--r--drivers/netjack/net_driver.c686
-rw-r--r--drivers/netjack/netjack.c732
-rw-r--r--drivers/netjack/netjack.h28
4 files changed, 786 insertions, 662 deletions
diff --git a/drivers/netjack/Makefile.am b/drivers/netjack/Makefile.am
index 6e38afa..135a31c 100644
--- a/drivers/netjack/Makefile.am
+++ b/drivers/netjack/Makefile.am
@@ -9,7 +9,7 @@ plugin_LTLIBRARIES = jack_net.la
jack_net_la_LDFLAGS = -module -avoid-version @NETJACK_LIBS@
jack_net_la_CFLAGS = @NETJACK_CFLAGS@
-jack_net_la_SOURCES = net_driver.c netjack_packet.c
+jack_net_la_SOURCES = net_driver.c netjack_packet.c netjack.c
noinst_HEADERS = net_driver.h netjack_packet.h
diff --git a/drivers/netjack/net_driver.c b/drivers/netjack/net_driver.c
index 26b8205..2bac886 100644
--- a/drivers/netjack/net_driver.c
+++ b/drivers/netjack/net_driver.c
@@ -38,255 +38,23 @@ $Id: net_driver.c,v 1.17 2006/04/16 20:16:10 torbenh Exp $
#include <sysdeps/time.h>
#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
#include "config.h"
-#if HAVE_SAMPLERATE
-#include <samplerate.h>
-#endif
-
-#if HAVE_CELT
-#include <celt/celt.h>
-#endif
#include "netjack.h"
-
#include "net_driver.h"
-#include "netjack_packet.h"
#undef DEBUG_WAKEUP
-#define MIN(x,y) ((x)<(y) ? (x) : (y))
-
-static int sync_state = TRUE;
-static jack_transport_state_t last_transport_state;
-
-static int
-net_driver_sync_cb(jack_transport_state_t state, jack_position_t *pos, net_driver_t *driver)
-{
- int retval = sync_state;
- if (state == JackTransportStarting && last_transport_state != JackTransportStarting) {
- retval = 0;
- }
-// if (state == JackTransportStarting)
-// jack_info("Starting sync_state = %d", sync_state);
- last_transport_state = state;
- return retval;
-}
static jack_nframes_t
net_driver_wait (net_driver_t *driver, int extra_fd, int *status, float *delayed_usecs)
{
netjack_driver_state_t *netj = &( driver->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 = (jacknet_packet_header *) netj->rx_buf;
-
- if( !netj->next_deadline_valid ) {
- if( netj->latency == 0 )
- // for full sync mode... always wait for packet.
- netj->next_deadline = jack_get_microseconds() + 500*driver->period_usecs;
- else if( netj->latency == 1 )
- // for normal 1 period latency mode, only 1 period for dealine.
- netj->next_deadline = jack_get_microseconds() + driver->period_usecs;
- 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_microseconds() + 5*netj->latency*netj->period_usecs/4;
- netj->next_deadline = jack_get_microseconds() + 2*driver->period_usecs;
-
- netj->next_deadline_valid = 1;
- } else {
- netj->next_deadline += driver->period_usecs;
- }
-
- // Increment expected frame here.
- netj->expected_framecnt += 1;
-
- // 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;
- 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.
- // TODO: there is still something wrong when trying
- // to send back to another port on localhost.
- // need to use -r on netsource for that.
- 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 ) {
- netj->time_to_deadline = netj->next_deadline - jack_get_microseconds() - driver->period_usecs;
- packet_cache_retreive_packet( global_packcache, netj->expected_framecnt, (char *) netj->rx_buf, netj->rx_bufsize , &packet_recv_time_stamp);
- //int recv_time_offset = (int) (jack_get_microseconds() - packet_recv_time_stamp);
- 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 < 10*(int)driver->period_usecs/100*netj->latency ) {
- netj->next_deadline -= driver->period_usecs/1000;
- //printf( "goodness: %d, Adjust deadline: --- %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 );
- }
- if( netj->deadline_goodness > 10*(int)driver->period_usecs/100*netj->latency ) {
- netj->next_deadline += driver->period_usecs/1000;
- //printf( "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;
-
- //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 -= driver->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( global_packcache, netj->expected_framecnt, (char *) netj->rx_buf, netj->rx_bufsize, NULL );
- packet_header_ntoh(pkthdr);
- //netj->deadline_goodness = 0;
- netj->deadline_goodness = (int)pkthdr->sync_state - (int)driver->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 += driver->period_usecs/8;
- }
- }
- } else if( (netj->num_lost_packets <= 10) ) {
- // 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( global_packcache, netj->expected_framecnt, (char *) netj->rx_buf, netj->rx_bufsize, NULL );
- packet_header_ntoh(pkthdr);
- netj->deadline_goodness = pkthdr->sync_state;
- netj->next_deadline_valid = 0;
- netj->packet_data_valid = 1;
- netj->running_free = 0;
- printf( "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;
- //packet_header_ntoh (pkthdr);
- }
-
+ netjack_wait( netj );
driver->last_wait_ust = jack_get_microseconds ();
driver->engine->transport_cycle_start (driver->engine, driver->last_wait_ust);
@@ -299,7 +67,7 @@ net_driver_wait (net_driver_t *driver, int extra_fd, int *status, float *delayed
return netj->period_size;
}
-static inline int
+static int
net_driver_run_cycle (net_driver_t *driver)
{
jack_engine_t *engine = driver->engine;
@@ -330,41 +98,9 @@ net_driver_null_cycle (net_driver_t* driver, jack_nframes_t nframes)
// do i wait here ?
// just sending out a packet marked with junk ?
- //int rx_size = get_sample_size(driver->bitdepth) * driver->capture_channels * driver->net_period_down + sizeof(jacknet_packet_header);
netjack_driver_state_t *netj = &(driver->netj);
- 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 = (driver->engine->control->sync_remain <= 1);
-
- 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; r<netj->redundancy; r++ )
- netjack_sendto(netj->outsockfd, (char *)packet_buf, tx_size,
- 0, (struct sockaddr*)&(netj->syncsource_address), sizeof(struct sockaddr_in), netj->mtu);
- }
+ int sync_state = (driver->engine->control->sync_remain <= 1);
+ netjack_send_silence( netj, sync_state );
return 0;
}
@@ -383,82 +119,8 @@ static int
net_driver_read (net_driver_t* driver, jack_nframes_t nframes)
{
netjack_driver_state_t *netj = &(driver->netj);
- jack_position_t local_trans_pos;
- jack_transport_state_t local_trans_state;
-
- unsigned int *packet_buf, *packet_bufX;
-
- if( ! netj->packet_data_valid ) {
- render_payload_to_jack_ports (netj->bitdepth, NULL, netj->net_period_down, netj->capture_ports, netj->capture_srcs, nframes, 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) {
- int compensated_tranport_pos = (pkthdr->transport_frame + (pkthdr->latency * nframes) + netj->codec_latency);
-
- // read local transport info....
- local_trans_state = jack_transport_query(netj->client, &local_trans_pos);
-
- // Now check if we have to start or stop local transport to sync to remote...
- switch (pkthdr->transport_state) {
- case JackTransportStarting:
- // the master transport is starting... so we set our reply to the sync_callback;
- if (local_trans_state == JackTransportStopped) {
- jack_transport_start(netj->client);
- last_transport_state = JackTransportStopped;
- sync_state = FALSE;
- jack_info("locally stopped... starting...");
- }
-
- if (local_trans_pos.frame != compensated_tranport_pos)
- {
- jack_transport_locate(netj->client, compensated_tranport_pos);
- last_transport_state = JackTransportRolling;
- sync_state = FALSE;
- jack_info("starting locate to %d", compensated_tranport_pos );
- }
- break;
- case JackTransportStopped:
- sync_state = TRUE;
- if (local_trans_pos.frame != (pkthdr->transport_frame)) {
- 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);
- break;
- case JackTransportRolling:
- sync_state = TRUE;
-// if(local_trans_pos.frame != (pkthdr->transport_frame + (pkthdr->latency) * nframes)) {
-// jack_transport_locate(netj->client, (pkthdr->transport_frame + (pkthdr->latency + 2) * nframes));
-// jack_info("running locate to %d", pkthdr->transport_frame + (pkthdr->latency)*nframes);
-// }
- if (local_trans_state != JackTransportRolling)
- jack_transport_start (netj->client);
- break;
-
- case JackTransportLooping:
- break;
- }
- }
-
- render_payload_to_jack_ports (netj->bitdepth, packet_bufX, netj->net_period_down, netj->capture_ports, netj->capture_srcs, nframes, netj->dont_htonl_floats );
+ netjack_read( netj, nframes );
return 0;
}
@@ -466,48 +128,9 @@ static int
net_driver_write (net_driver_t* driver, jack_nframes_t nframes)
{
netjack_driver_state_t *netj = &(driver->netj);
- 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 = 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 = (driver->engine->control->sync_remain <= 1);;
- 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, nframes, packet_bufX, netj->net_period_up, netj->dont_htonl_floats );
-
- packet_header_hton(pkthdr);
- if (netj->srcaddress_valid)
- {
- int r;
-
-#ifdef __APPLE__
- static const int flag = 0;
-#else
- static const int flag = MSG_CONFIRM;
-#endif
-
- if (netj->reply_port)
- netj->syncsource_address.sin_port = htons(netj->reply_port);
-
- for( r=0; r<netj->redundancy; r++ )
- netjack_sendto(netj->outsockfd, (char *)packet_buf, packet_size,
- flag, (struct sockaddr*)&(netj->syncsource_address), sizeof(struct sockaddr_in), netj->mtu);
- }
+ int sync_state = (driver->engine->control->sync_remain <= 1);;
+ netjack_write( netj, nframes, sync_state );
return 0;
}
@@ -516,110 +139,10 @@ static int
net_driver_attach (net_driver_t *driver)
{
netjack_driver_state_t *netj = &( driver->netj );
- //puts ("net_driver_attach");
- jack_port_t * port;
- char buf[32];
- unsigned int chn;
- int port_flags;
-
driver->engine->set_buffer_size (driver->engine, netj->period_size);
driver->engine->set_sample_rate (driver->engine, netj->sample_rate);
- if (netj->handle_transport_sync)
- jack_set_sync_callback(netj->client, (JackSyncCallback) net_driver_sync_cb, driver);
-
- 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);
+ netjack_attach( netj );
return 0;
}
@@ -627,26 +150,11 @@ static int
net_driver_detach (net_driver_t *driver)
{
netjack_driver_state_t *netj = &( driver->netj );
- JSList * node;
if (driver->engine == 0)
return 0;
-//#if 0
- 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;
-//#endif
-
- 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_detach( netj );
return 0;
}
@@ -678,8 +186,6 @@ net_driver_new (jack_client_t * client,
{
net_driver_t * driver;
netjack_driver_state_t *netj = &(driver->netj);
- int first_pack_len;
- struct sockaddr_in address;
jack_info ("creating net driver ... %s|%" PRIu32 "|%" PRIu32
"|%u|%u|%u|transport_sync:%u", name, sample_rate, period_size, listen_port,
@@ -698,166 +204,28 @@ net_driver_new (jack_client_t * client,
driver->nt_run_cycle = (JackDriverNTRunCycleFunction) net_driver_run_cycle;
driver->last_wait_ust = 0;
- // 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->client = client;
driver->engine = NULL;
- 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;
-
- // Now open the socket, and wait for the first packet to arrive...
- netj->sockfd = socket (PF_INET, SOCK_DGRAM, 0);
- if (netj->sockfd == -1)
- {
- jack_info ("socket error");
- return NULL;
- }
- 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 NULL;
- }
-
- netj->outsockfd = socket (PF_INET, SOCK_DGRAM, 0);
- if (netj->outsockfd == -1)
- {
- jack_info ("socket error");
- return NULL;
- }
- netj->srcaddress_valid = 0;
-
- if (use_autoconfig)
- {
- jacknet_packet_header *first_packet = alloca (sizeof (jacknet_packet_header));
- socklen_t address_size = sizeof (struct sockaddr_in);
-
- 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 !!!");
-
- // XXX: netjack_poll polls forever.
- // thats ok here.
- if (netjack_poll (netj->sockfd, 500))
- first_pack_len = recvfrom (netj->sockfd, first_packet, sizeof (jacknet_packet_header), 0, (struct sockaddr*) & netj->syncsource_address, &address_size);
- else
- first_pack_len = 0;
-
- 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...
- driver->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 = resample_factor;
- netj->net_period_up = resample_factor_up;
- } else {
- netj->net_period_down = (float) netj->period_size / (float) resample_factor;
- netj->net_period_up = (float) netj->period_size / (float) resample_factor_up;
- }
-
- netj->rx_bufsize = sizeof (jacknet_packet_header) + netj->net_period_down * netj->capture_channels * get_sample_size (netj->bitdepth);
- netj->rx_buf = malloc (netj->rx_bufsize);
- netj->pkt_buf = malloc (netj->rx_bufsize);
- global_packcache = packet_cache_new (netj->latency + 5, 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 );
+ netjack_init ( netj,
+ client,
+ name,
+ capture_ports,
+ playback_ports,
+ capture_ports_midi,
+ playback_ports_midi,
+ sample_rate,
+ period_size,
+ listen_port,
+ transport_sync,
+ resample_factor,
+ resample_factor_up,
+ bitdepth,
+ use_autoconfig,
+ latency,
+ redundancy,
+ dont_htonl_floats );
- netj->running_free = 0;
jack_info ("netjack: period : up: %d / dn: %d", netj->net_period_up, netj->net_period_down);
jack_info ("netjack: framerate: %d", netj->sample_rate);
diff --git a/drivers/netjack/netjack.c b/drivers/netjack/netjack.c
index ce39821..e3f1be3 100644
--- a/drivers/netjack/netjack.c
+++ b/drivers/netjack/netjack.c
@@ -24,3 +24,735 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: net_driver.c,v 1.17 2006/04/16 20:16:10 torbenh Exp $
*/
+
+#include <math.h>
+#include <stdio.h>
+#include <memory.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+
+#include <jack/types.h>
+#include <jack/engine.h>
+#include <sysdeps/time.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "netjack.h"
+
+#include "config.h"
+#if HAVE_SAMPLERATE
+#include <samplerate.h>
+#endif
+
+#if HAVE_CELT
+#include <celt/celt.h>
+#endif
+
+#include "netjack.h"
+#include "netjack_packet.h"
+
+#define MIN(x,y) ((x)<(y) ? (x) : (y))
+
+static int sync_state = TRUE;
+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 = (jacknet_packet_header *) netj->rx_buf;
+
+ if( !netj->next_deadline_valid ) {
+ if( netj->latency == 0 )
+ // for full sync mode... always wait for packet.
+ netj->next_deadline = jack_get_microseconds() + 500*netj->period_usecs;
+ else if( netj->latency == 1 )
+ // for normal 1 period latency mode, only 1 period for dealine.
+ netj->next_deadline = jack_get_microseconds() + netj->period_usecs;
+ 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_microseconds() + 5*netj->latency*netj->period_usecs/4;
+ netj->next_deadline = jack_get_microseconds() + 2*netj->period_usecs;
+
+ netj->next_deadline_valid = 1;
+ } else {
+ netj->next_deadline += netj->period_usecs;
+ }
+
+ // Increment expected frame here.
+ netj->expected_framecnt += 1;
+
+ // 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;
+ 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.
+ // TODO: there is still something wrong when trying
+ // to send back to another port on localhost.
+ // need to use -r on netsource for that.
+ 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 ) {
+ netj->time_to_deadline = netj->next_deadline - jack_get_microseconds() - netj->period_usecs;
+ packet_cache_retreive_packet( global_packcache, netj->expected_framecnt, (char *) netj->rx_buf, netj->rx_bufsize , &packet_recv_time_stamp);
+ //int recv_time_offset = (int) (jack_get_microseconds() - packet_recv_time_stamp);
+ 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 < 10*(int)netj->period_usecs/100*netj->latency ) {
+ netj->next_deadline -= netj->period_usecs/1000;
+ //printf( "goodness: %d, Adjust deadline: --- %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 );
+ }
+ if( netj->deadline_goodness > 10*(int)netj->period_usecs/100*netj->latency ) {
+ netj->next_deadline += netj->period_usecs/1000;
+ //printf( "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( global_packcache, netj->expected_framecnt, (char *) netj->rx_buf, netj->rx_bufsize, NULL );
+ 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/8;
+ }
+ }
+ } else if( (netj->num_lost_packets <= 10) ) {
+ // 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( global_packcache, netj->expected_framecnt, (char *) netj->rx_buf, netj->rx_bufsize, NULL );
+ packet_header_ntoh(pkthdr);
+ netj->deadline_goodness = pkthdr->sync_state;
+ netj->next_deadline_valid = 0;
+ netj->packet_data_valid = 1;
+ netj->running_free = 0;
+ printf( "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;
+ //packet_header_ntoh (pkthdr);
+ }
+}
+
+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; r<netj->redundancy; r++ )
+ netjack_sendto(netj->outsockfd, (char *)packet_buf, tx_size,
+ 0, (struct sockaddr*)&(netj->syncsource_address), sizeof(struct sockaddr_in), netj->mtu);
+ }
+}
+
+void netjack_read( netjack_driver_state_t *netj, jack_nframes_t nframes )
+{
+ jack_position_t local_trans_pos;
+ jack_transport_state_t local_trans_state;
+
+ unsigned int *packet_buf, *packet_bufX;
+
+ if( ! netj->packet_data_valid ) {
+ render_payload_to_jack_ports (netj->bitdepth, NULL, netj->net_period_down, netj->capture_ports, netj->capture_srcs, nframes, netj->dont_htonl_floats );
+ return;
+ }
+ 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) {
+ int compensated_tranport_pos = (pkthdr->transport_frame + (pkthdr->latency * nframes) + netj->codec_latency);
+
+ // read local transport info....
+ local_trans_state = jack_transport_query(netj->client, &local_trans_pos);
+
+ // Now check if we have to start or stop local transport to sync to remote...
+ switch (pkthdr->transport_state) {
+ case JackTransportStarting:
+ // the master transport is starting... so we set our reply to the sync_callback;
+ if (local_trans_state == JackTransportStopped) {
+ jack_transport_start(netj->client);
+ last_transport_state = JackTransportStopped;
+ sync_state = FALSE;
+ jack_info("locally stopped... starting...");
+ }
+
+ if (local_trans_pos.frame != compensated_tranport_pos)
+ {
+ jack_transport_locate(netj->client, compensated_tranport_pos);
+ last_transport_state = JackTransportRolling;
+ sync_state = FALSE;
+ jack_info("starting locate to %d", compensated_tranport_pos );
+ }
+ break;
+ case JackTransportStopped:
+ sync_state = TRUE;
+ if (local_trans_pos.frame != (pkthdr->transport_frame)) {
+ 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);
+ break;
+ case JackTransportRolling:
+ sync_state = TRUE;
+// if(local_trans_pos.frame != (pkthdr->transport_frame + (pkthdr->latency) * nframes)) {
+// jack_transport_locate(netj->client, (pkthdr->transport_frame + (pkthdr->latency + 2) * nframes));
+// jack_info("running locate to %d", pkthdr->transport_frame + (pkthdr->latency)*nframes);
+// }
+ if (local_trans_state != JackTransportRolling)
+ jack_transport_start (netj->client);
+ break;
+
+ case JackTransportLooping:
+ break;
+ }
+ }
+
+ render_payload_to_jack_ports (netj->bitdepth, packet_bufX, netj->net_period_down, netj->capture_ports, netj->capture_srcs, nframes, netj->dont_htonl_floats );
+}
+
+void netjack_write( netjack_driver_state_t *netj, jack_nframes_t nframes, int syncstate )
+{
+ 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 = alloca(packet_size);
+ pkthdr = (jacknet_packet_header *)packet_buf;
+
+ if( netj->running_free ) {
+ return;
+ }
+
+ // 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, nframes, packet_bufX, netj->net_period_up, netj->dont_htonl_floats );
+
+ packet_header_hton(pkthdr);
+ if (netj->srcaddress_valid)
+ {
+ int r;
+
+#ifdef __APPLE__
+ static const int flag = 0;
+#else
+ static const int flag = MSG_CONFIRM;
+#endif
+
+ if (netj->reply_port)
+ netj->syncsource_address.sin_port = htons(netj->reply_port);
+
+ for( r=0; r<netj->redundancy; r++ )
+ netjack_sendto(netj->outsockfd, (char *)packet_buf, packet_size,
+ flag, (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,
+ 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)
+{
+
+ // Fill in netj values.
+ // might be subject to autoconfig...
+ // so dont calculate anything with them...
+
+ int first_pack_len;
+ struct sockaddr_in address;
+
+ 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->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;
+
+ // Now open the socket, and wait for the first packet to arrive...
+ netj->sockfd = socket (PF_INET, SOCK_DGRAM, 0);
+ if (netj->sockfd == -1)
+ {
+ jack_info ("socket error");
+ return NULL;
+ }
+ 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 NULL;
+ }
+
+ netj->outsockfd = socket (PF_INET, SOCK_DGRAM, 0);
+ if (netj->outsockfd == -1)
+ {
+ jack_info ("socket error");
+ return NULL;
+ }
+ netj->srcaddress_valid = 0;
+
+ if (use_autoconfig)
+ {
+ jacknet_packet_header *first_packet = alloca (sizeof (jacknet_packet_header));
+ socklen_t address_size = sizeof (struct sockaddr_in);
+
+ 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 !!!");
+
+ // XXX: netjack_poll polls forever.
+ // thats ok here.
+ if (netjack_poll (netj->sockfd, 500))
+ first_pack_len = recvfrom (netj->sockfd, first_packet, sizeof (jacknet_packet_header), 0, (struct sockaddr*) & netj->syncsource_address, &address_size);
+ else
+ first_pack_len = 0;
+
+ 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 = resample_factor;
+ netj->net_period_up = resample_factor_up;
+ } else {
+ netj->net_period_down = (float) netj->period_size / (float) resample_factor;
+ netj->net_period_up = (float) netj->period_size / (float) resample_factor_up;
+ }
+
+ netj->rx_bufsize = sizeof (jacknet_packet_header) + netj->net_period_down * netj->capture_channels * get_sample_size (netj->bitdepth);
+ netj->rx_buf = malloc (netj->rx_bufsize);
+ netj->pkt_buf = malloc (netj->rx_bufsize);
+ global_packcache = packet_cache_new (netj->latency + 5, 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 netj;
+}
diff --git a/drivers/netjack/netjack.h b/drivers/netjack/netjack.h
index 8684f85..e2552ca 100644
--- a/drivers/netjack/netjack.h
+++ b/drivers/netjack/netjack.h
@@ -39,6 +39,7 @@ struct _netjack_driver_state {
jack_nframes_t sample_rate;
jack_nframes_t bitdepth;
jack_nframes_t period_size;
+ jack_time_t period_usecs;
int dont_htonl_floats;
jack_nframes_t codec_latency;
@@ -90,6 +91,29 @@ struct _netjack_driver_state {
jack_time_t time_to_deadline;
};
-
-
+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,
+ 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);
#endif