diff options
author | torben <torben@0c269be4-1314-0410-8aa9-9f06e86f4224> | 2009-11-06 11:14:15 +0000 |
---|---|---|
committer | torben <torben@0c269be4-1314-0410-8aa9-9f06e86f4224> | 2009-11-06 11:14:15 +0000 |
commit | 73b23245a9f29ac479ec7d7313a97a0fb60b25e0 (patch) | |
tree | fb186e91ba86c74b0adcbce240f0357784b74df8 | |
parent | bc1de2953eb7b0c764088176478a7fb7122e7697 (diff) | |
download | jack1-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.am | 2 | ||||
-rw-r--r-- | drivers/netjack/net_driver.c | 686 | ||||
-rw-r--r-- | drivers/netjack/netjack.c | 732 | ||||
-rw-r--r-- | drivers/netjack/netjack.h | 28 |
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 |