/** * The process callback for this JACK application. * It is called by JACK at the appropriate times. */ int process (jack_nframes_t nframes, void *arg) { jack_nframes_t net_period; int rx_bufsize, tx_bufsize; jack_default_audio_sample_t *buf; jack_port_t *port; JSList *node; int chn; int size, i; const char *porttype; int input_fd; jack_position_t local_trans_pos; uint32_t *packet_buf_tx, *packet_bufX; uint32_t *rx_packet_ptr; jack_time_t packet_recv_timestamp; if( bitdepth == 1000 || bitdepth == 999) net_period = (factor * jack_get_buffer_size(client) * 1024 / jack_get_sample_rate(client) / 8) & (~1) ; else net_period = (float) nframes / (float) factor; rx_bufsize = get_sample_size (bitdepth) * capture_channels * net_period + sizeof (jacknet_packet_header); tx_bufsize = get_sample_size (bitdepth) * playback_channels * net_period + sizeof (jacknet_packet_header); /* Allocate a buffer where both In and Out Buffer will fit */ packet_buf_tx = alloca (tx_bufsize); jacknet_packet_header *pkthdr_tx = (jacknet_packet_header *) packet_buf_tx; /* * for latency==0 we need to send out the packet before we wait on the reply. * but this introduces a cycle of latency, when netsource is connected to itself. * so we send out before read only in zero latency mode. * */ if( latency == 0 ) { /* reset packet_bufX... */ packet_bufX = packet_buf_tx + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t); /* ---------- Send ---------- */ render_jack_ports_to_payload (bitdepth, playback_ports, playback_srcs, nframes, packet_bufX, net_period, dont_htonl_floats); /* fill in packet hdr */ pkthdr_tx->transport_state = jack_transport_query (client, &local_trans_pos); pkthdr_tx->transport_frame = local_trans_pos.frame; pkthdr_tx->framecnt = framecnt; pkthdr_tx->latency = latency; pkthdr_tx->reply_port = reply_port; pkthdr_tx->sample_rate = jack_get_sample_rate (client); pkthdr_tx->period_size = nframes; /* playback for us is capture on the other side */ pkthdr_tx->capture_channels_audio = playback_channels_audio; pkthdr_tx->playback_channels_audio = capture_channels_audio; pkthdr_tx->capture_channels_midi = playback_channels_midi; pkthdr_tx->playback_channels_midi = capture_channels_midi; pkthdr_tx->mtu = mtu; if( freewheeling != 0 ) pkthdr_tx->sync_state = (jack_nframes_t)MASTER_FREEWHEELS; else pkthdr_tx->sync_state = (jack_nframes_t)deadline_goodness; //printf("goodness=%d\n", deadline_goodness ); packet_header_hton (pkthdr_tx); if (cont_miss < 3 * latency + 5) { int r; for( r = 0; r < redundancy; r++ ) netjack_sendto (outsockfd, (char *) packet_buf_tx, tx_bufsize, 0, &destaddr, sizeof (destaddr), mtu); } else if (cont_miss > 50 + 5 * latency) { state_connected = 0; packet_cache_reset_master_address( packcache ); //printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss); cont_miss = 0; } } /* * ok... now the RECEIVE code. * */ if( reply_port ) input_fd = insockfd; else input_fd = outsockfd; // for latency == 0 we can poll. if( (latency == 0) || (freewheeling != 0) ) { jack_time_t deadline = jack_get_time() + 1000000 * jack_get_buffer_size(client) / jack_get_sample_rate(client); // Now loop until we get the right packet. while(1) { jack_nframes_t got_frame; if ( ! netjack_poll_deadline( input_fd, deadline ) ) break; packet_cache_drain_socket(packcache, input_fd); if (packet_cache_get_next_available_framecnt( packcache, framecnt - latency, &got_frame )) if( got_frame == (framecnt - latency) ) break; } } else { // normally: // only drain socket. packet_cache_drain_socket(packcache, input_fd); } size = packet_cache_retreive_packet_pointer( packcache, framecnt - latency, (char**)&rx_packet_ptr, rx_bufsize, &packet_recv_timestamp ); /* First alternative : we received what we expected. Render the data * to the JACK ports so it can be played. */ if (size == rx_bufsize) { uint32_t *packet_buf_rx = rx_packet_ptr; jacknet_packet_header *pkthdr_rx = (jacknet_packet_header *) packet_buf_rx; packet_bufX = packet_buf_rx + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t); // calculate how much time there would have been, if this packet was sent at the deadline. int recv_time_offset = (int) (jack_get_time() - packet_recv_timestamp); packet_header_ntoh (pkthdr_rx); deadline_goodness = recv_time_offset - (int)pkthdr_rx->latency; //printf( "deadline goodness = %d ---> off: %d\n", deadline_goodness, recv_time_offset ); if (cont_miss) { //printf("Frame %d \tRecovered from dropouts\n", framecnt); cont_miss = 0; } render_payload_to_jack_ports (bitdepth, packet_bufX, net_period, capture_ports, capture_srcs, nframes, dont_htonl_floats); state_currentframe = framecnt; state_recv_packet_queue_time = recv_time_offset; state_connected = 1; sync_state = pkthdr_rx->sync_state; packet_cache_release_packet( packcache, framecnt - latency ); } /* Second alternative : we've received something that's not * as big as expected or we missed a packet. We render silence * to the ouput ports */ else { jack_nframes_t latency_estimate; if( packet_cache_find_latency( packcache, framecnt, &latency_estimate ) ) //if( (state_latency == 0) || (latency_estimate < state_latency) ) state_latency = latency_estimate; // Set the counters up. state_currentframe = framecnt; //state_latency = framecnt - pkthdr->framecnt; state_netxruns += 1; //printf ("Frame %d \tPacket missed or incomplete (expected: %d bytes, got: %d bytes)\n", framecnt, rx_bufsize, size); //printf ("Frame %d \tPacket missed or incomplete\n", framecnt); cont_miss += 1; chn = 0; node = capture_ports; while (node != NULL) { port = (jack_port_t *) node->data; buf = jack_port_get_buffer (port, nframes); porttype = jack_port_type (port); if (strncmp (porttype, JACK_DEFAULT_AUDIO_TYPE, jack_port_type_size ()) == 0) for (i = 0; i < nframes; i++) buf[i] = 0.0; else if (strncmp (porttype, JACK_DEFAULT_MIDI_TYPE, jack_port_type_size ()) == 0) jack_midi_clear_buffer (buf); node = jack_slist_next (node); chn++; } } if (latency != 0) { /* reset packet_bufX... */ packet_bufX = packet_buf_tx + sizeof (jacknet_packet_header) / sizeof (jack_default_audio_sample_t); /* ---------- Send ---------- */ render_jack_ports_to_payload (bitdepth, playback_ports, playback_srcs, nframes, packet_bufX, net_period, dont_htonl_floats); /* fill in packet hdr */ pkthdr_tx->transport_state = jack_transport_query (client, &local_trans_pos); pkthdr_tx->transport_frame = local_trans_pos.frame; pkthdr_tx->framecnt = framecnt; pkthdr_tx->latency = latency; pkthdr_tx->reply_port = reply_port; pkthdr_tx->sample_rate = jack_get_sample_rate (client); pkthdr_tx->period_size = nframes; /* playback for us is capture on the other side */ pkthdr_tx->capture_channels_audio = playback_channels_audio; pkthdr_tx->playback_channels_audio = capture_channels_audio; pkthdr_tx->capture_channels_midi = playback_channels_midi; pkthdr_tx->playback_channels_midi = capture_channels_midi; pkthdr_tx->mtu = mtu; if( freewheeling != 0 ) pkthdr_tx->sync_state = (jack_nframes_t)MASTER_FREEWHEELS; else pkthdr_tx->sync_state = (jack_nframes_t)deadline_goodness; //printf("goodness=%d\n", deadline_goodness ); packet_header_hton (pkthdr_tx); if (cont_miss < 3 * latency + 5) { int r; for( r = 0; r < redundancy; r++ ) netjack_sendto (outsockfd, (char *) packet_buf_tx, tx_bufsize, 0, &destaddr, sizeof (destaddr), mtu); } else if (cont_miss > 50 + 5 * latency) { state_connected = 0; packet_cache_reset_master_address( packcache ); //printf ("Frame %d \tRealy too many packets missed (%d). Let's reset the counter\n", framecnt, cont_miss); cont_miss = 0; } } framecnt++; return 0; }
int netjack_wait( netjack_driver_state_t *netj ) { int we_have_the_expected_frame = 0; jack_nframes_t next_frame_avail; jack_time_t packet_recv_time_stamp; jacknet_packet_header *pkthdr; if( !netj->next_deadline_valid ) { netj->next_deadline = jack_get_time() + netj->period_usecs; netj->next_deadline_valid = 1; } // Increment expected frame here. if( netj->expected_framecnt_valid ) { netj->expected_framecnt += 1; } else { // starting up.... lets look into the packetcache, and fetch the highest packet. packet_cache_drain_socket( netj->packcache, netj->sockfd ); if( packet_cache_get_highest_available_framecnt( netj->packcache, &next_frame_avail ) ) { netj->expected_framecnt = next_frame_avail; netj->expected_framecnt_valid = 1; } else { // no packets there... start normally. netj->expected_framecnt = 0; netj->expected_framecnt_valid = 1; } } //jack_log( "expect %d", netj->expected_framecnt ); // Now check if required packet is already in the cache. // then poll (have deadline calculated) // then drain socket, rinse and repeat. while(1) { if( packet_cache_get_next_available_framecnt( netj->packcache, netj->expected_framecnt, &next_frame_avail) ) { if( next_frame_avail == netj->expected_framecnt ) { we_have_the_expected_frame = 1; if( !netj->always_deadline ) break; } } if( ! netjack_poll_deadline( netj->sockfd, netj->next_deadline ) ) { break; } packet_cache_drain_socket( netj->packcache, netj->sockfd ); } // check if we know who to send our packets too. if (!netj->srcaddress_valid) if( netj->packcache->master_address_valid ) { memcpy (&(netj->syncsource_address), &(netj->packcache->master_address), sizeof( struct sockaddr_in ) ); netj->srcaddress_valid = 1; } // XXX: switching mode unconditionally is stupid. // if we were running free perhaps we like to behave differently // ie. fastforward one packet etc. // well... this is the first packet we see. hmm.... dunno ;S // it works... so... netj->running_free = 0; //if( !we_have_the_expected_frame ) // jack_error( "netxrun... %d", netj->expected_framecnt ); if( we_have_the_expected_frame ) { jack_time_t now = jack_get_time(); if( now < netj->next_deadline ) netj->time_to_deadline = netj->next_deadline - now; else netj->time_to_deadline = 0; packet_cache_retreive_packet_pointer( netj->packcache, netj->expected_framecnt, (char **) &(netj->rx_buf), netj->rx_bufsize , &packet_recv_time_stamp); pkthdr = (jacknet_packet_header *) netj->rx_buf; packet_header_ntoh(pkthdr); netj->deadline_goodness = (int)pkthdr->sync_state; netj->packet_data_valid = 1; int want_deadline; if( netj->jitter_val != 0 ) want_deadline = netj->jitter_val; else if( netj->latency < 4 ) want_deadline = -netj->period_usecs/2; else want_deadline = (netj->period_usecs/4+10*(int)netj->period_usecs*netj->latency/100); if( netj->deadline_goodness != MASTER_FREEWHEELS ) { if( netj->deadline_goodness < want_deadline ) { netj->next_deadline -= netj->period_usecs/100; //jack_log( "goodness: %d, Adjust deadline: --- %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); } if( netj->deadline_goodness > want_deadline ) { netj->next_deadline += netj->period_usecs/100; //jack_log( "goodness: %d, Adjust deadline: +++ %d\n", netj->deadline_goodness, (int) netj->period_usecs*netj->latency/100 ); } } // if( netj->next_deadline < (netj->period_usecs*70/100) ) { // jack_error( "master is forcing deadline_offset to below 70%% of period_usecs... increase latency setting on master" ); // netj->deadline_offset = (netj->period_usecs*90/100); // } netj->next_deadline += netj->period_usecs; } else { netj->time_to_deadline = 0; netj->next_deadline += netj->period_usecs; // 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( netj->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( netj->packcache, netj->expected_framecnt ) > 80.0 ) netj->next_deadline -= netj->period_usecs/2; } else { // the diff is too high. but we have a packet in the future. // lets resync. netj->expected_framecnt = next_frame_avail; packet_cache_retreive_packet_pointer( netj->packcache, netj->expected_framecnt, (char **) &(netj->rx_buf), netj->rx_bufsize, NULL ); pkthdr = (jacknet_packet_header *) netj->rx_buf; packet_header_ntoh(pkthdr); //netj->deadline_goodness = 0; netj->deadline_goodness = (int)pkthdr->sync_state - (int)netj->period_usecs * offset; netj->next_deadline_valid = 0; netj->packet_data_valid = 1; } } else { // no packets in buffer. netj->packet_data_valid = 0; //printf( "frame %d No Packet in queue. num_lost_packets = %d \n", netj->expected_framecnt, netj->num_lost_packets ); if( netj->num_lost_packets < 5 ) { // ok. No Packet in queue. The packet was either lost, // or we are running too fast. // // Adjusting the deadline unconditionally resulted in // too many xruns on master. // But we need to adjust for the case we are running too fast. // So lets check if the last packet is there now. // // It would not be in the queue anymore, if it had been // retrieved. This might break for redundancy, but // i will make the packet cache drop redundant packets, // that have already been retreived. // if( packet_cache_get_highest_available_framecnt( netj->packcache, &next_frame_avail) ) { if( next_frame_avail == (netj->expected_framecnt - 1) ) { // Ok. the last packet is there now. // and it had not been retrieved. // // TODO: We are still dropping 2 packets. // perhaps we can adjust the deadline // when (num_packets lost == 0) // This might still be too much. netj->next_deadline += netj->period_usecs; } } } else if( (netj->num_lost_packets <= 100) ) { // lets try adjusting the deadline harder, for some packets, we might have just ran 2 fast. netj->next_deadline += netj->period_usecs*netj->latency/8; } else { // But now we can check for any new frame available. // if( packet_cache_get_highest_available_framecnt( netj->packcache, &next_frame_avail) ) { netj->expected_framecnt = next_frame_avail; packet_cache_retreive_packet_pointer( netj->packcache, netj->expected_framecnt, (char **) &(netj->rx_buf), netj->rx_bufsize, NULL ); pkthdr = (jacknet_packet_header *) netj->rx_buf; packet_header_ntoh(pkthdr); netj->deadline_goodness = pkthdr->sync_state; netj->next_deadline_valid = 0; netj->packet_data_valid = 1; netj->running_free = 0; jack_info( "resync after freerun... %d", netj->expected_framecnt ); } else { if( netj->num_lost_packets == 101 ) { jack_info( "master seems gone... entering freerun mode", netj->expected_framecnt ); } 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( netj->packcache ); } } } } } int retval = 0; if( !netj->packet_data_valid ) { netj->num_lost_packets += 1; if( netj->num_lost_packets == 1 ) retval = netj->period_usecs; } else { if( (netj->num_lost_packets>1) && !netj->running_free ) retval = (netj->num_lost_packets-1) * netj->period_usecs; netj->num_lost_packets = 0; } return retval; }