njh/rotter — pull request #28 by bergner (2013-12-13 09:46:37+00:00)
Title: Sample accurate output files

Hi,
I've rewritten the callback_jack function and adjusted the rotter_ringbuffer_t struct to keep track of audio samples as a mean to obtain output files that have exactly the correct duration. Previously the file switching involved a certain degree of randomness due to the timestamp handling. The behaviour I've observed is that the output files tended to be slightly shorter than the designated archive period.

The two new rotter_ringbuffer_t struct members are:

frame_offset = the number of frames since we switched to this ring buffer
start_offset = 0 in all cases except the first (partial) archive period. In the first archive period this denotes how many frames into the archive period we started.

The most tricky part is to try to have a decent time synchronization at the first archive period switch after startup. After that it is a simple matter of sample counting until the subsequent switches. I've chosen to solve that with:

if (gettimeofday(&tv, NULL)) {
rotter_fatal("Failed to gettimeofday(): %s", strerror(errno));
return 1;
}

time_t period = start_of_period(tv.tv_sec);
// First time we set start_offset to compensate for the fact
// that we are in the middle of an archive period.
double diff = difftime(tv.tv_sec, period) + tv.tv_usec / 1000000.0;
switch_ringbuffer(&tv, period);
active_ringbuffer->start_offset = (jack_nframes_t)(diff * rate);

This is not perfect. I've been thinking of maybe utilizing jack_frames_to_time instead of gettimeofday but couldn't figure out how that would work in a reliable way. So likely the start time is a couple of microseconds off, but overall the tests I've been running with this looks very promising.