Show syncfiles.c syntax highlighted
#include <sndfile.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <libmisc/misc.h>
#define MAX_FILES 10
struct sync_point {
int offset;
int mark;
};
struct skipper {
int skipfrom;
int skipignore;
int zerolen;
};
struct file_info {
char *name;
int channels;
int samples;
struct sync_point sps[1000];
int sps_count;
int offset;
int offset_avg;
int offset_count;
struct skipper skippers[10];
int next_skipper;
int skipper_count;
int zerocount;
SNDFILE* f;
};
struct file_info files[MAX_FILES];
int file_count = 0;
struct mark_info {
int offset;
int present;
int invalid;
/* state for the correction code */
int skipthis;
int ignore;
int fillin;
};
struct mark_info marks[256][MAX_FILES];
int mark_valid(int i, int j)
{
return marks[i][j].present && !marks[i][j].invalid;
}
int main(int argc, char **argv)
{
#define SYNC_CHANNEL 0
#define THRESH 9000
#define BAUD 19200
FILE *sync_out = stdout;
memset(files, 0, sizeof(files));
memset(marks, 0, sizeof(marks));
int samplerate = 0;
int format = 0;
char buf1[128];
char buf2[128];
char *outfile_name = "/tmp/output.wav";
char *sync_filename = "/tmp/output.sync";
char *outfile_root = misc_parse_out_option(&argc, argv, "outfile", 0);
if (outfile_root) {
strcpy(buf1, outfile_root);
strcat(buf1, ".wav");
strcpy(buf2, outfile_root);
strcat(buf2, ".sync");
outfile_name = buf1;
sync_filename = buf2;
}
int skip=0;
while (misc_parse_option_as_int(&argc, argv, "skip", 0, &skip) >= 0) {
int i;
for (i=0; i<MAX_FILES; i++)
marks[skip][i].invalid = 1;
}
char *str;
while ((str = misc_parse_out_option(&argc, argv, "fixchannel", 0))) {
int channel, mark;
sscanf(str, "%d,%d", &channel, &mark);
marks[mark][channel].skipthis = 1;
printf("# Setting skipthis for channel %d, mark %d\n", channel, mark);
}
while ((str = misc_parse_out_option(&argc, argv, "fixup", 0))) {
int channel, mark, ign, fill;
sscanf(str, "%d,%d,%d,%d", &channel, &mark, &ign, &fill);
marks[mark][channel].skipthis = 2;
marks[mark][channel].ignore = ign;
marks[mark][channel].fillin = fill;
printf("# Setting custom skip for channel %d, mark %d, fill %d\n", channel, mark, fill);
}
if (misc_parse_out_switch(&argc, argv, "help", 'h')) {
printf("usage: --outfile <root> [--force] [--skip num] ..\n"
" rcvfile rcvfile feedfile\n"
" --force will force completion when errors flagged\n"
" --skip will invalidate specific marks\n"
" --fixchannel chan,mark will replace the following segment of specified channel with blanks.\n");
exit(1);
}
int force = misc_parse_out_switch(&argc, argv, "force", 0);
/* load each file */
char **iter = argv + 1;
while (*iter) {
files[file_count].name = *iter;
/* load the file */
SF_INFO info = {};
files[file_count].f = sf_open(*iter, SFM_READ, &info);
if (files[file_count].f == NULL) {
fprintf(stderr, "Can't open sound file %s: %m\n", *iter);
exit(1);
}
/* ok, it's open. now, read in the sync channel */
fprintf(sync_out, "# File %s, %lld samples, %d data channels\n",
*iter, info.frames, info.channels);
files[file_count].channels = info.channels;
files[file_count].samples = info.frames;
samplerate = info.samplerate;
format = info.format;
/* determine the bit rate */
float samp_per_bit = info.samplerate / (float)BAUD;
//float samp_per_bit = 44100 / (float)BAUD;
int16_t *sync_data = malloc(info.frames * sizeof(int16_t));
int i=0;
while (i<info.frames) {
int16_t temp[1024][info.channels];
sf_count_t count = sf_readf_short
(files[file_count].f, (int16_t *)temp, 1024);
int j;
for (j=0; j<count; j++,i++) {
sync_data[i] = temp[j][SYNC_CHANNEL];
//printf("%d %d\n", temp[j][0], temp[j][1]);
}
}
/* ok, we have the sync channel. now we parse it */
int k;
/* first find the threshold */
int max = 0;
for (k=0; k<info.frames; k++) {
if (abs(sync_data[k]) > max)
max = abs(sync_data[k]);
}
int thresh = max/2;
printf("##thresh = %d\n", thresh);
for (k=0; k<info.frames; k++) {
if (abs(sync_data[k]) > (3*max/4)) {
/* ok, maybe got a byte */
float bits[9];
int kk;
#ifdef DEBUG
printf("##sync data: ");
for (kk=0; kk<22; kk++)
printf("%d ", sync_data[k+kk]);
printf("\n");
#endif
/* average the data into bits */
float fk;
for (kk=0, fk=0; kk<9; kk++, fk+=samp_per_bit) {
bits[kk] = sync_data[k+(int)(fk+0.5)];
}
#ifdef DEBUG
printf("##bits: ");
for (kk=0; kk<9; kk++)
printf("%f ", bits[kk]/thresh);
printf("\n");
#endif
int bits2[9];
for (kk=0; kk<9; kk++)
bits2[kk] = (fabsf(bits[kk]) > thresh) ? 0 : 1;
#ifdef DEBUG
printf("##bits2: ");
for (kk=0; kk<9; kk++)
printf("%d ", bits2[kk]);
printf("\n");
#endif
int value = 0;
for (kk=8; kk>0; kk--) {
value = value << 1;
value |= bits2[kk];
}
printf("##value: %d (%c) at %d\n", value,
isprint(value) ? value : '.', k);
files[file_count].sps[files[file_count].sps_count].mark = value;
files[file_count].sps[files[file_count].sps_count].offset = k;
files[file_count].sps_count++;
k += 50;
}
}
file_count++;
iter++;
}
/* ok, now figure out how to sync up the files */
/* for each mark, find other marks */
int i,j,kk;
int maybe_stop=0;
for (i=0; i<file_count; i++) {
for (j=0; j<files[i].sps_count; j++) {
int mark = files[i].sps[j].mark;
if (marks[mark][i].present) {
printf("** warning, duplicate mark %d, file %s\n",
mark, files[i].name);
marks[mark][i].invalid = 1;
}
else {
marks[mark][i].offset = files[i].sps[j].offset;
marks[mark][i].present = 1;
}
}
}
/* drop out of order entries */
for (j=0; j<file_count; j++) {
for (i=1; i<255; i++) {
if (mark_valid(i,j) &&
mark_valid(i-1,j) &&
mark_valid(i+1,j) &&
(marks[i][j].offset < marks[i-1][j].offset ||
marks[i][j].offset > marks[i+1][j].offset) &&
marks[i-1][j].offset < marks[i+1][j].offset) {
printf("mark '%c' out of order.. marking invalid\n", i);
marks[i][j].invalid = 1;
}
}
}
/* locate earliest mark. */
int earliest = -1;
for (j=0; j<file_count; j++) {
int earliest_offset = -1;
int earliest_file = -1;
for (i=0; i<255; i++) {
if (mark_valid(i,j)) {
if (earliest_offset < 0 ||
marks[i][j].offset < earliest_offset) {
earliest_offset = marks[i][j].offset;
earliest_file = i;
}
}
}
if (earliest < 0 || earliest_file < earliest)
earliest = earliest_file;
}
printf("## earliest mark is %d\n", earliest);
/* locate skip requests and patch up offsets after skip */
for (j=0; j<file_count; j++) {
for (i=0; i<255; i++) {
if (marks[i][j].skipthis) {
printf("## processing skipthis for ch %d, mark %d\n", j,i);
int k,k2;
int region;
if (marks[i][j].skipthis == 2) {
k = i+1;
files[j].skippers[files[j].skipper_count].skipfrom = marks[i][j].offset;
files[j].skippers[files[j].skipper_count].zerolen = marks[i][j].fillin;
files[j].skippers[files[j].skipper_count].skipignore = marks[i][j].ignore;
files[j].skipper_count++;
region = marks[i][j].fillin - marks[i][j].ignore;
goto adjust;
}
/* got one. first, figure out the size of this region. */
for (k=i+1; k!=i; k = (k+1)%256) {
if (mark_valid(k,j)) {
for (k2=0; k2<file_count; k2++) {
if (k2 != j && mark_valid(k,k2))
goto found_range;
}
printf("## no range found, yet, skipping past mark %d\n", k);
}
}
printf("## ACK .. no range found\n");
exit(1);
found_range:
/* ok, use k2 */
printf("## using file %d mark %d, offset is %d, %d\n",
k2, k, marks[k][k2].offset, marks[i][k2].offset);
region = marks[k][k2].offset - marks[i][k2].offset;
int oldregion = marks[k][j].offset - marks[i][j].offset;
printf("## region change from %d to %d. %d samples lost, will drop %d unsyncable samples\n",
oldregion, region, region - oldregion, region);
if (region < oldregion) {
printf("## ACK! old region is bigger???\n");
exit(1);
}
/* save the amounts to kill and replace */
marks[i][j].ignore = oldregion;
marks[i][j].fillin = region;
files[j].skippers[files[j].skipper_count].skipfrom = marks[i][j].offset;
files[j].skippers[files[j].skipper_count].zerolen = region;
files[j].skippers[files[j].skipper_count].skipignore = oldregion;
files[j].skipper_count++;
region -= oldregion;
files[j].samples += region;
/* ok, clear any marks between those two */
int k3;
for (k3=i+1; k3 != k; k3 = (k3+1)%256) {
if (mark_valid(k3,j)) {
printf("## killing mark %d\n", k3);
marks[k3][j].invalid = 1;
}
}
adjust:
;
/* ok, we now add the difference to all the rest of the offsets */
int lastoff = marks[i][j].offset;
for (k3=k; k3 != i; k3 = (k3+1)%256) {
if (mark_valid(k3,j)) {
if (marks[k3][j].offset < lastoff) {
printf("## done shifting..\n");
goto done_shifting;
}
printf("## shifting mark at %d\n", k3);
lastoff = marks[k3][j].offset;
marks[k3][j].offset += region;
}
}
done_shifting:
;
}
}
}
/* we assume that there are no errors now.
* we will check this assumption and fail if it's incorrect */
/* find first full entry */
int first_full;
for (i=earliest; i!=earliest-1; i = (i+1)%256) {
for (j=0; j<file_count; j++) {
if (!mark_valid(i,j))
goto next;
}
first_full=i;
goto found_full;
next:
;
}
printf("*** no full sync entry found, aborting.\n");
exit(1);
found_full:
printf("## Using %d as the first full mark.\n", i);
/* dump the errors */
printf("## dumping errors..\n");
int last_errors[MAX_FILES] = {};
int last_diff[MAX_FILES] = {};
int last_offset[MAX_FILES] = {};
int last_mark[MAX_FILES] = {};
float skew_count[MAX_FILES] = {};
float sum_skew[MAX_FILES] = {};
float skew_avg[MAX_FILES] = {};
float delta = 0;
for (kk=earliest; kk!=earliest-1; kk = (kk+1)%256) {
if (mark_valid(kk,0)) {
int j;
int the_offset = marks[kk][0].offset;
printf("## %d ", kk);
for (j=0; j<file_count; j++) {
if (mark_valid(kk,j)) {
int new_error = marks[kk][j].offset - the_offset;
int new_diff = 0;
float skew = 0;
if (last_offset[j] != 0) {
new_diff = new_error-last_errors[j];
skew = new_diff /
(float)(marks[kk][j].offset - last_offset[j]);
}
last_errors[j] = new_error;
if (last_offset[j] != 0) {
last_diff[j] = new_diff;
sum_skew[j] += skew;
skew_count[j]++;
if (skew_avg[j] != 0) {
float new_delta = fabs(skew - skew_avg[j])*48000.0;
if (new_delta > delta)
delta = new_delta;
}
}
float permark = 0;
if (last_offset[j] > 0) {
int pastmarks = (kk-last_mark[j]);
if (pastmarks < 0) pastmarks += 256;
permark = (marks[kk][j].offset - last_offset[j]) / (float)(pastmarks);
if ((permark / 468832.0) < 0.99) {
printf("\n*** suggest --fixup %d,%d,%d,%d\n",
j,last_mark[j],marks[kk][j].offset - last_offset[j], (pastmarks)*468832);
}
}
last_offset[j] = marks[kk][j].offset;
skew_avg[j] = skew_count[j] ? sum_skew[j] / skew_count[j] : 0;
printf("[%d %d %.2f %.2f %.2f] ", new_error, new_diff,
skew*48000.0, skew_avg[j]*48000.0, permark);
last_mark[j] = kk;
}
else
printf("[] ");
}
printf("\n");
}
}
if (delta > 1) {
printf("***WARNING: Deviation of %f from average skew... check for problems!\n",
delta);
maybe_stop = 1;
}
if (skew_count[0] < 2 || skew_count[1] < 2) {
printf("***WARNING: Fewer than 2 skew entries\n");
maybe_stop = 1;
}
if (skew_count[2] < 2) {
printf("***WARNING: Fewer than 2 skew entries on feedback channel\n");
maybe_stop = 1;
}
/* $$$ HACKS .. depends on receivers being skew free
* and on not needing high quality sync on feedback channel! */
/* check that receiver files are OK */
if (fabs(skew_avg[1])*48000.0 > 1) {
printf("***WARNING: Average skew on channel 1 is %f. This usually indicates a glitch or overrun\n",
skew_avg[1]*48000.0);
maybe_stop = 1;
}
/* adjust sender file */
for (i=0;i<256;i++) {
if (mark_valid(i,2)) {
marks[i][2].offset =
marks[i][2].offset / (1.0+skew_avg[2]);
}
}
int feedback_samples = files[2].samples;
files[2].samples = files[2].samples / (1.0+skew_avg[2]);
printf("adjusted feedback sample count from %d to %d\n",
feedback_samples, files[2].samples);
/* dump errors */
for (kk=earliest; kk!=earliest-1; kk = (kk+1)%256) {
if (mark_valid(kk,0)) {
int j;
int the_offset = marks[kk][0].offset;
printf("## %d ", kk);
for (j=0; j<file_count; j++) {
if (mark_valid(kk,j)) {
int new_error = marks[kk][j].offset - the_offset;
printf("%d ", new_error);
}
}
printf("\n");
}
}
if (maybe_stop) {
if (!force) {
printf("ABORTING. use --force to complete\n");
exit(1);
}
printf("WOULD ABORT, --force set\n");
}
/* which file has lowest offset */
int min_off = -1;
int min_off_file = -1;
for (j=0; j<file_count; j++) {
if (min_off_file < 0 ||
marks[first_full][j].offset < min_off) {
min_off = marks[first_full][j].offset;
min_off_file = j;
}
}
printf("## Minimum offset file is %d\n", min_off_file);
for (j=0; j<file_count; j++) {
files[j].offset = marks[first_full][j].offset - min_off;
}
/* verify the others */
int max_err = 0;
int max_mark = -1;
int max_file = -1;
int killed_count = 0;
int OK_count = 0;
for (kk=earliest; kk!=earliest-1; kk = (kk+1)%256) {
if (mark_valid(kk,min_off_file)) {
int local_offset = marks[kk][min_off_file].offset;
for (j=0; j<file_count; j++) {
if (mark_valid(kk,j)) {
int err = marks[kk][j].offset - local_offset - files[j].offset;
if (abs(err) > max_err) {
/* if the error is a lot, just mark invalid and keep going */
if (abs(err) > 20) {
printf("##found large err %d at %d,%d\n", err, kk, j);
printf("##killing this mark..\n");
killed_count++;
goto killit;
}
max_err = abs(err);
max_mark = kk;
max_file = j;
printf("##found max err %d at %d,%d\n", err, kk, j);
}
}
}
OK_count++;
}
else {
killit:
/* kill this if the low offset file is invalid */
for (j=0; j<file_count; j++) {
marks[kk][j].invalid = 1;
}
}
}
if (killed_count > OK_count*2) {
printf("***too many bad entries, aborting!\n");
exit(1);
}
if (max_err > 20) {
printf("***too much error, aborting!\n");
exit(1);
}
/* average the error */
for (j=0; j<file_count; j++) {
for (kk=0; kk<256; kk++) {
if (mark_valid(kk,j)) {
int local_offset = marks[kk][min_off_file].offset;
int err = marks[kk][j].offset - local_offset - files[j].offset;
files[j].offset_avg += err;
files[j].offset_count++;
}
}
files[j].offset_avg /= files[j].offset_count;
/* $$ but we arent currently using this info */
}
/* ok, now rewind the input files and offset them */
int total_samples = -1;
int total_channels = 0;
for (j=0; j<file_count; j++) {
/* rewind and seek the file */
sf_seek(files[j].f, files[j].offset, SEEK_SET);
printf("## Offset for file %d is %d\n",
j, files[j].offset);
/* adjust skip location if needed */
for (i=0; i<files[j].skipper_count; i++) {
printf("# correcting skipfrom at %d\n", files[j].skippers[i].skipfrom);
files[j].skippers[i].skipfrom -= files[j].offset;
printf("# skipfrom is now at %d\n", files[j].skippers[i].skipfrom);
}
/* compute the length we are copying */
if (total_samples < 0 ||
total_samples > (files[j].samples - files[j].offset)) {
total_samples = (files[j].samples - files[j].offset);
}
total_channels += (files[j].channels - 1);
}
printf("## Totals: samples %d channels %d\n",
total_samples, total_channels);
/* open output file */
SF_INFO info2 = {
samplerate: samplerate,
format: format,
frames: total_samples,
channels: total_channels
};
SNDFILE *output_file = sf_open(outfile_name, SFM_WRITE, &info2);
if (output_file == NULL) {
fprintf(stderr, "Can't open output sound file %s: %m\n", outfile_name);
exit(1);
}
/* $$$ HACK!! */
float feedback_increment = 1.0 / (1.0 + skew_avg[2]);
float feedback_counter = 0;
for (kk=0; kk<total_samples;
kk++, feedback_counter += feedback_increment) {
int16_t frame[total_channels];
memset(frame, 0, sizeof(frame));
int index = 0;
for (j=0; j<file_count; j++) {
int16_t temp[files[j].channels];
memset(temp, 0, sizeof(temp));
int drop = 0;
int insert = 0;
/* check for major glitch start */
if (files[j].skippers[files[j].next_skipper].skipfrom && kk == files[j].skippers[files[j].next_skipper].skipfrom) {
/* seek fwd */
printf("# seeking past %d samples\n", files[j].skippers[files[j].next_skipper].skipignore);
sf_seek(files[j].f, files[j].skippers[files[j].next_skipper].skipignore, SEEK_CUR);
/* set zero countdown */
files[j].zerocount = files[j].skippers[files[j].next_skipper].zerolen;
printf("# ignoring the next %d samples on this channel\n", files[j].zerocount);
files[j].next_skipper++;
}
/* skip glitches */
if (files[j].zerocount > 0) {
files[j].zerocount--;
index += (files[j].channels-1);
continue;
}
/* $$$ HACK!!! */
if (j == 2) {
/* feedback channel ahead? */
if (feedback_counter > (kk + 0.5)) {
feedback_counter -= 1.0;
drop = 1;
}
/* feedback channel behind? */
else if (feedback_counter < kk - 0.5) {
feedback_counter += 1.0;
insert = 1;
}
}
if (!insert) {
if (sf_readf_short(files[j].f, temp, 1) != 1) {
if (j == 2 && kk >= feedback_samples) {
printf("**** tried to read off end for feedback.. \n");
}
else {
printf("read failed.. %d %d %d ? %m\n", kk, j, index);
exit(1);
}
}
}
if (drop) {
index += (files[j].channels-1);
}
else {
for (i=0; i<files[j].channels; i++) {
if (i != SYNC_CHANNEL) {
frame[index++] = temp[i];
}
}
}
}
/* write a frame */
if (sf_writef_short(output_file, frame, 1) != 1) {
printf("write failed.. %d ? %m\n", kk);
exit(1);
}
}
/* close */
for (j=0; j<file_count; j++) {
printf("closing file %s, offset %d, dropped %d samples\n",
files[j].name, files[j].offset,
files[j].samples - total_samples);
sf_close(files[j].f);
}
sf_close(output_file);
/* dump sync file */
FILE *sync = fopen(sync_filename, "w");
/* find earliest entry */
int min_mark_entry = -1;
int min_mark_offset = 0;
for (i=0; i<256; i++) {
for (j=0; j<file_count; j++) {
if (mark_valid(i,j) &&
(min_mark_entry < 0 ||
min_mark_offset > marks[i][0].offset)) {
min_mark_entry = i;
min_mark_offset = marks[i][0].offset;
}
}
}
fprintf(sync, "#h mark offset\n");
fprintf(sync, "## sync file, channel map:\n");
int index = 0;
for (j=0; j<file_count; j++) {
fprintf(sync, "## %d .. %d from %s\n",
index, index + files[j].channels - 2,
files[j].name);
index += files[j].channels-1;
}
/* dump the errors */
fprintf(sync, "## errors..\n");
for (i=0; i<256; i++) {
int index = (i+min_mark_entry)%256;
int j;
if (mark_valid(index,min_off_file)) {
fprintf(sync, "## %d ", index);
for (j=0; j<file_count; j++) {
if (mark_valid(index,j)) {
fprintf(sync, "%d ",
marks[index][min_off_file].offset -
(marks[index][j].offset -
files[j].offset));
}
}
fprintf(sync, "\n");
}
}
/* dump entries in order */
for (i=0; i<256; i++) {
int index = (i+min_mark_entry)%256;
if (mark_valid(index,min_off_file)) {
fprintf(sync, "%d %d\n", index, marks[index][min_off_file].offset);
}
}
fclose(sync);
return 0;
}
See more files for this project here