// $Id: cropsong.cc 6 2006-03-13 02:00:20Z flaterco $
//
// Adaptively crop silence from beginning and end of songs, replacing
// the original files.
//
// g++ -O2 -Wall -s -o cropsong cropsong.cc

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <math.h>

// Return length in seconds.
double lengthofwav (char *fname) {
  struct stat s;
  assert (stat (fname, &s) == 0);
  assert (s.st_size > 44);
  // wav files from sox and normalize have 44 bytes of header.
  // 1 second = 44100 samples * 2 channels * 16 bits = 176400 bytes.
  return (double)(s.st_size - 44) / 176400.0;
}

// Adaptively trim silence from start and end of song.  Some songs
// have silent bits in the middle, so they require a longer interval
// for detecting the end.

// Toofar:  if change in length exceeds toofar seconds, then you have
// screwed up.
#define toofar 20

void trim (char *from) {
  char cmd[1000];
  double interval=0.1, tolen=0.0;
  double fromlen = lengthofwav(from);
  printf ("Cropping %s\n  %02lu:%05.2f  orig length\n", from,
    (unsigned long)(fromlen/60), fmod(fromlen,60.0));
  do {
    printf ("  Trimming with interval %3.1f\n", interval);
    sprintf (cmd, "sox %s delme.wav silence 1 0:0:0.01 -50d 1 0:0:%3.1f -60d",
	     from, interval);
    system (cmd);
    tolen = lengthofwav("delme.wav");
    printf ("  %02lu:%05.2f  trimmed length",
      (unsigned long)(tolen/60), fmod(tolen,60.0));
    if (tolen < fromlen - toofar) {
      printf (" -- OOPS");
      interval *= 2;
    }
    printf ("\n");
  } while (tolen < fromlen - toofar);
  if (rename ("delme.wav", from)) {
    perror ("delme.wav");
    exit (-1);
  }
}

int main (int argc, char **argv) {
  if (argc < 2) {
    fprintf (stderr, "Usage: cropsong song song...\n");
    fprintf (stderr, "Original files are replaced.\n");
  }

  for (int i=1; i<argc; i++)
    trim (argv[i]);

  return 0;
}
