Add no-sync option, don't try to sync if delay() returns -1, check and comment some integer and sign promotion, update stats printing, (but more to do there).

This commit is contained in:
Mike Brady
2016-03-26 21:40:59 +00:00
parent 548d07716a
commit cca403c260
+118 -78
View File
@@ -168,8 +168,8 @@ static inline seq_t PREDECESSOR(seq_t x) {
// anything with ORDINATE in it must be proctected by the ab_mutex
static inline int32_t ORDINATE(seq_t x) {
int32_t p = x & 0xffff;
int32_t q = ab_read & 0x0ffff;
int32_t p = x; // int32_t from seq_t, i.e. uint16_t, so okay
int32_t q = ab_read; // int32_t from seq_t, i.e. uint16_t, so okay
int32_t t = (p + 0x10000 - q) & 0xffff;
// we definitely will get a positive number in t at this point, but it might be a
// positive alias of a negative number, i.e. x might actually be "before" ab_read
@@ -935,9 +935,9 @@ static void *player_thread_func(void *arg) {
int64_t tsum_of_sync_errors, tsum_of_corrections, tsum_of_insertions_and_deletions,
tsum_of_drifts;
int64_t previous_sync_error, previous_correction;
int64_t minimum_dac_queue_size = 1000000;
int32_t minimum_buffer_occupancy = BUFFER_FRAMES;
int32_t maximum_buffer_occupancy = 0;
int64_t minimum_dac_queue_size = INT64_MAX;
int32_t minimum_buffer_occupancy = INT32_MAX;
int32_t maximum_buffer_occupancy = INT32_MIN;
buffer_occupancy = 0;
@@ -992,25 +992,28 @@ static void *player_thread_func(void *arg) {
uint32_t reference_timestamp;
uint64_t reference_timestamp_time,remote_reference_timestamp_time;
get_reference_timestamp_stuff(&reference_timestamp, &reference_timestamp_time, &remote_reference_timestamp_time);
get_reference_timestamp_stuff(&reference_timestamp, &reference_timestamp_time, &remote_reference_timestamp_time); // types okay
int64_t rt, nt;
rt = reference_timestamp;
nt = inframe->timestamp;
rt = reference_timestamp; // uint32_t to int64_t
nt = inframe->timestamp; // uint32_t to int64_t
uint64_t local_time_now = get_absolute_time_in_fp();
uint64_t local_time_now = get_absolute_time_in_fp(); // types okay
// struct timespec tn;
// clock_gettime(CLOCK_MONOTONIC,&tn);
// uint64_t
// local_time_now=((uint64_t)tn.tv_sec<<32)+((uint64_t)tn.tv_nsec<<32)/1000000000;
int64_t td_in_frames;
int64_t td = local_time_now - reference_timestamp_time;
int64_t td = 0;
int64_t td_in_frames = 0;
if (local_time_now >= reference_timestamp_time) {
// debug(1,"td is %lld.",td);
if (td >= 0) {
td = local_time_now - reference_timestamp_time; // this is the positive value. Conversion is positive uint64_t to int64_t, thus okay
td_in_frames = (td * 44100) >> 32;
} else {
td_in_frames = -((-td * 44100) >> 32);
td = reference_timestamp_time - local_time_now; // this is the absolute value, which should be negated. Conversion is positive uint64_t to int64_t, thus okay.
td_in_frames = -((td * 44100) >> 32); //use the absolute td value for the present. Types okay
td = -td; // should be okay, as the range of values should be very small w.r.t 64 bits
}
// This is the timing error for the next audio frame in the DAC, if applicable
@@ -1020,10 +1023,10 @@ static void *player_thread_func(void *arg) {
// check sequencing
if (last_seqno_read == -1)
last_seqno_read = inframe->sequence_number;
last_seqno_read = inframe->sequence_number; //int32_t from seq_t, i.e. uint16_t, so okay.
else {
last_seqno_read = (SUCCESSOR(last_seqno_read) & 0xffff);
if (inframe->sequence_number != last_seqno_read) {
last_seqno_read = SUCCESSOR(last_seqno_read); // int32_t from seq_t, i.e. uint16_t, so okay.
if (inframe->sequence_number != last_seqno_read) { //seq_t, ei.e. uint16_t and int32_t, so okay
debug(
1,
"Player: packets out of sequence: expected: %d, got: %d, sync error: %d frames.",
@@ -1032,7 +1035,7 @@ static void *player_thread_func(void *arg) {
}
}
buffer_occupancy = seq_diff(ab_read, ab_write);
buffer_occupancy = seq_diff(ab_read, ab_write); // int32_t from int32
if (buffer_occupancy < minimum_buffer_occupancy)
minimum_buffer_occupancy = buffer_occupancy;
@@ -1040,26 +1043,39 @@ static void *player_thread_func(void *arg) {
if (buffer_occupancy > maximum_buffer_occupancy)
maximum_buffer_occupancy = buffer_occupancy;
if (config.output->delay) {
current_delay = config.output->delay();
if (current_delay == -1) {
// here, we want to check (a) if we are meant to do synchronisation, (b) if we have a delay procedure, (b) if we can get the delay.
// If any of these are false, we don't do any synchronisation stuff
current_delay = -1; // use this as a failure flag
if (config.output->delay) {
current_delay = config.output->delay(); // int64_t from int32_t, so okay;
if (current_delay >= 0) {
if (current_delay < minimum_dac_queue_size) {
minimum_dac_queue_size = current_delay; // update for display later
}
} else {
debug(1, "Delay error when checking running latency.");
current_delay = 0;
}
if (current_delay < minimum_dac_queue_size)
minimum_dac_queue_size = current_delay;
}
if (current_delay >= 0) {
// this is the actual delay, including the latency we actually want, which will
// fluctuate a good bit about a potentially rising or falling trend.
int64_t delay = td_in_frames + rt - (nt - current_delay);
int64_t delay = td_in_frames + rt - (nt - current_delay); // all int64_t
// This is the timing error for the next audio frame in the DAC.
sync_error = delay - config.latency;
sync_error = delay - config.latency; // int64_t from int64_t - int32_t, so okay
// if (llabs(sync_error)>352*512)
// debug(1,"Very large sync error: %lld",sync_error);
// before we finally commit to this frame, check its sequencing and timing
// require a certain error before bothering to fix it...
if (sync_error > config.tolerance) {
if (sync_error > config.tolerance) { // int64_t > int, okay
amount_to_stuff = -1;
}
if (sync_error < -config.tolerance) {
@@ -1079,7 +1095,7 @@ static void *player_thread_func(void *arg) {
if (amount_to_stuff) {
if ((local_time_now) && (first_packet_time_to_play) && (local_time_now >= first_packet_time_to_play)) {
int64_t tp = (local_time_now - first_packet_time_to_play)>>32; // seconds
int64_t tp = (local_time_now - first_packet_time_to_play)>>32; // seconds int64_t from uint64_t which is always positive, so ok
if (tp<5)
amount_to_stuff = 0; // wait at least five seconds
@@ -1089,6 +1105,9 @@ static void *player_thread_func(void *arg) {
}
}
}
if (config.no_sync!=0)
amount_to_stuff = 0 ; // no stuffing if it's been disabled
if ((amount_to_stuff == 0) && (fix_volume == 0x10000)) {
// if no stuffing needed and no volume adjustment, then
@@ -1132,8 +1151,14 @@ static void *player_thread_func(void *arg) {
// check for loss of sync
// timestamp of zero means an inserted silent frame in place of a missing frame
if ((inframe->timestamp != 0) && (!please_stop) && (config.resyncthreshold != 0) &&
(abs(sync_error) > config.resyncthreshold)) {
// not too sure if abs() is implemented for int64_t, so we'll do it manually
int64_t abs_sync_error = sync_error;
if (abs_sync_error<0)
abs_sync_error = -abs_sync_error;
if ((config.no_sync==0) && (inframe->timestamp != 0) && (!please_stop) && (config.resyncthreshold != 0) &&
(abs_sync_error > config.resyncthreshold)) {
sync_error_out_of_bounds++;
// debug(1,"Sync error out of bounds: Error: %lld; previous error: %lld; DAC: %lld;
// timestamp: %llx, time now
@@ -1149,11 +1174,12 @@ static void *player_thread_func(void *arg) {
sync_error_out_of_bounds = 0;
}
} else {
// if there is no delay procedure, there can be no synchronising
// if there is no delay procedure, or it's not working or not allowed, there can be no synchronising
if (fix_volume == 0x10000)
config.output->play(inbuf, frame_size);
else {
play_samples = stuff_buffer_basic(inbuf, outbuf, 0);
play_samples = stuff_buffer_basic(inbuf, outbuf, 0); // no stuffing, but volume adjustment
config.output->play(outbuf, frame_size);
}
}
@@ -1166,45 +1192,49 @@ static void *player_thread_func(void *arg) {
// new stats calculation. We want a running average of sync error, drift, adjustment,
// number of additions+subtractions
// this is a misleading hack -- the statistics should include some data on the number of valid samples and the number of times sync wasn't checked due to non availability of a delay figure.
// for the present, stats are only updated when sync has been checked
if (sync_error!=-1) {
if (number_of_statistics == trend_interval) {
// here we remove the oldest statistical data and take it from the summaries as well
tsum_of_sync_errors -= statistics[oldest_statistic].sync_error;
tsum_of_drifts -= statistics[oldest_statistic].drift;
if (statistics[oldest_statistic].correction > 0)
tsum_of_insertions_and_deletions -= statistics[oldest_statistic].correction;
else
tsum_of_insertions_and_deletions += statistics[oldest_statistic].correction;
tsum_of_corrections -= statistics[oldest_statistic].correction;
oldest_statistic = (oldest_statistic + 1) % trend_interval;
number_of_statistics--;
}
if (number_of_statistics == trend_interval) {
// here we remove the oldest statistical data and take it from the summaries as well
tsum_of_sync_errors -= statistics[oldest_statistic].sync_error;
tsum_of_drifts -= statistics[oldest_statistic].drift;
if (statistics[oldest_statistic].correction > 0)
tsum_of_insertions_and_deletions -= statistics[oldest_statistic].correction;
statistics[newest_statistic].sync_error = sync_error;
statistics[newest_statistic].correction = amount_to_stuff;
if (number_of_statistics == 0)
statistics[newest_statistic].drift = 0;
else
tsum_of_insertions_and_deletions += statistics[oldest_statistic].correction;
tsum_of_corrections -= statistics[oldest_statistic].correction;
oldest_statistic = (oldest_statistic + 1) % trend_interval;
number_of_statistics--;
}
statistics[newest_statistic].drift =
sync_error - previous_sync_error - previous_correction;
statistics[newest_statistic].sync_error = sync_error;
statistics[newest_statistic].correction = amount_to_stuff;
previous_sync_error = sync_error;
previous_correction = amount_to_stuff;
if (number_of_statistics == 0)
statistics[newest_statistic].drift = 0;
else
statistics[newest_statistic].drift =
sync_error - previous_sync_error - previous_correction;
previous_sync_error = sync_error;
previous_correction = amount_to_stuff;
tsum_of_sync_errors += sync_error;
tsum_of_drifts += statistics[newest_statistic].drift;
if (amount_to_stuff > 0) {
tsum_of_insertions_and_deletions += amount_to_stuff;
} else {
tsum_of_insertions_and_deletions -= amount_to_stuff;
}
tsum_of_corrections += amount_to_stuff;
session_corrections += amount_to_stuff;
tsum_of_sync_errors += sync_error;
tsum_of_drifts += statistics[newest_statistic].drift;
if (amount_to_stuff > 0) {
tsum_of_insertions_and_deletions += amount_to_stuff;
} else {
tsum_of_insertions_and_deletions -= amount_to_stuff;
}
tsum_of_corrections += amount_to_stuff;
session_corrections += amount_to_stuff;
newest_statistic = (newest_statistic + 1) % trend_interval;
number_of_statistics++;
newest_statistic = (newest_statistic + 1) % trend_interval;
number_of_statistics++;
}
}
if (play_number % print_interval == 0) {
// we can now calculate running averages for sync error (frames), corrections (ppm),
@@ -1217,29 +1247,39 @@ static void *player_thread_func(void *arg) {
// if ((play_number/print_interval)%20==0)
if (config.statistics_requested) {
if (at_least_one_frame_seen) {
if (config.output->delay)
inform("Sync error: %.1f (frames); net correction: %.1f (ppm); corrections: %.1f "
"(ppm); missing packets %llu; late packets %llu; too late packets %llu; "
"resend requests %llu; min DAC queue size %lli, min and max buffer occupancy "
"%d and %d.",
moving_average_sync_error, moving_average_correction * 1000000 / 352,
moving_average_insertions_plus_deletions * 1000000 / 352, missing_packets,
late_packets, too_late_packets, resend_requests, minimum_dac_queue_size,
minimum_buffer_occupancy, maximum_buffer_occupancy);
else
if ((config.output->delay)) {
if (config.no_sync==0) {
inform("Sync error: %.1f (frames); net correction: %.1f (ppm); corrections: %.1f "
"(ppm); missing packets %llu; late packets %llu; too late packets %llu; "
"resend requests %llu; min DAC queue size %lli, min and max buffer occupancy "
"%d and %d.",
moving_average_sync_error, moving_average_correction * 1000000 / 352,
moving_average_insertions_plus_deletions * 1000000 / 352, missing_packets,
late_packets, too_late_packets, resend_requests, minimum_dac_queue_size,
minimum_buffer_occupancy, maximum_buffer_occupancy);
} else {
inform("Synchronisation disabled. Sync error: %.1f (frames); missing packets %llu; late packets %llu; too late packets %llu; "
"resend requests %llu; min DAC queue size %lli, min and max buffer occupancy "
"%d and %d.",
moving_average_sync_error, missing_packets,
late_packets, too_late_packets, resend_requests, minimum_dac_queue_size,
minimum_buffer_occupancy, maximum_buffer_occupancy);
}
} else {
inform("Synchronisation disabled. Missing packets %llu; late packets %llu; too late packets %llu; "
"resend requests %llu; min and max buffer occupancy "
"%d and %d.",
missing_packets,
late_packets, too_late_packets, resend_requests,
minimum_buffer_occupancy, maximum_buffer_occupancy);
minimum_buffer_occupancy, maximum_buffer_occupancy);
}
} else {
inform("No frames received in the last sampling interval.");
}
}
minimum_dac_queue_size = 1000000; // hack reset
maximum_buffer_occupancy = 0; // can't be less than this
minimum_buffer_occupancy = BUFFER_FRAMES; // can't be more than this
minimum_dac_queue_size = INT64_MAX; // hack reset
maximum_buffer_occupancy = INT32_MIN; // can't be less than this
minimum_buffer_occupancy = INT32_MAX; // can't be more than this
at_least_one_frame_seen = 0;
}
}