//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed May 14 16:12:29 PDT 2003
// Last Modified: Sun Aug 29 03:22:58 PDT 2004 (updates for GCC3 compiling)
// Filename:      ...soundfile/examples/cutoff.cpp
// Syntax:        C++ 
//
// Description:   Calculate the average signal power for given durations
//

#include "soundfile.h"
#include <stdlib.h>
#include <math.h>

#ifndef OLDCPP
   #include <iostream>
   #include <iomanip>
   using namespace std;
#else
   #include <iostream.h>
   #include <iomanip.h>
#endif

void generatePowerSummary(Array<double>& list, const char* filename,
      int chunk, int frames);
void printLongSummary(Array<double>& list, int chunk, int channels);
void generateOutputFile(const char* outfilename, const char* infilename, 
      Array<double>& powerlist, double cutoff, int direction, int chunk);

int main(int argc, char** argv) {
   Options options;
   options.define("a|average=i:10000", "Number of samples to average over");
   options.define("c|cutoff=d:0.1",    "Cut-off RMS amplitude");
   options.define("h|high=b",     "Remove stuff above the cutoff");
   options.define("l|low=b",      "Remove stuff below the cutoff");
   options.define("i|info=b",     "Print only power information");
   options.define("s|start=i:0",  "Starting sample");
   options.define("n|count=i:-1", "Number of analyses to make");
   options.define("all=b",        "Calculate the energy of the entire file");
   options.define("q|quiet=b",    "Suppress printing of header info");
   options.process(argc, argv);

   if (options.getArgCount() == 0) {
      cout << "Usage: " << options.getCommand() 
           << " filename" 
           << endl;
      exit(1);
   }

   int direction = 1;
   if (options.getBoolean("high")) {
      direction = 0;
   }
   double cutoff = options.getDouble("cutoff");

   SoundFileRead soundfile(options.getArg(1));
   int chunk = options.getInteger("average");
   int start = options.getInteger("start");
   int channels = soundfile.getChannels();
   int totalframes = options.getInteger("count");
   if (totalframes <= 0) {
      totalframes = (soundfile.getSamples() - start)/chunk + 1;
   } 

   // print header information
   if (!options.getBoolean("quiet")) {
      cout << "; Filename        = " << options.getArg(1) << "\n";
      cout << "; Samples         = " << soundfile.getSamples() << "\n";
      if (start != 0) {
         cout << "; Starting sample = " << start << "\n";
      }
      cout << "; Averaging size  = " << chunk << " samples" << "\n";
      cout << "; Total frames    = " << totalframes << "\n";
      cout << "; start-sample	dB	RMS		peak\n";
   }

   Array<double> powerlist;
   generatePowerSummary(powerlist, options.getArg(1), chunk, totalframes);

   if (options.getBoolean("info")) {
      if (options.getBoolean("all")) {
         printLongSummary(powerlist, soundfile.getSamples(), channels); 
      } else {
         printLongSummary(powerlist, chunk, channels);
      }
   } else {
      generateOutputFile(options.getArg(2), options.getArg(1), powerlist, 
            cutoff, direction, chunk);
   }

   return 0;
}


//////////////////////////////
//
// generateOutputFile --
//

void generateOutputFile(const char* outfilename, const char* infilename, 
      Array<double>& powerlist, double cutoff, int direction, int chunk) {

   SoundFileRead infile(infilename);
   SoundFileWrite outfile(outfilename, infile);
   int samples  = infile.getSamples();
   int channels = infile.getChannels();

   int i, chan;
   int frame;

   Array<double> rms(powerlist.getSize());
   for (i=0; i<rms.getSize(); i++) {
      rms[i] = sqrt(powerlist[i]/chunk/channels);
   }

   for (i=0; i<samples; i++) {
      frame = i/chunk;
      for (chan=0; chan<channels; chan++) {
         if (rms[frame] < cutoff && direction == 1) {
            outfile.writeSampleDouble(0.0);
         } else if (rms[frame] > cutoff && direction == 0) {
            outfile.writeSampleDouble(0.0);
         } else {
            outfile.writeSampleDouble(infile.getCurrentSampleDouble(chan));
         }
      }
      infile.incrementSample(); 
   }
}



//////////////////////////////
//
// generatePowerSummary --
//

void generatePowerSummary(Array<double>& list, const char* filename,
      int chunk, int frames) {

   SoundFileRead soundfile(filename);
   list.setSize(frames+100);
   list.setGrowth(frames+100);
   list.setSize(0);

   double sum, value;
   int i, j, channel;
   for (i=0; i<frames; i++) {
      sum = 0.0;
      for (j=0; j<chunk; j++) {
         for (channel=0; channel < soundfile.getChannels(); channel++) {
            value = soundfile.getCurrentSampleDouble(channel);
            sum += value * value;
         }
         soundfile.incrementSample(); 
      }

      list.append(sum);
   }
}



//////////////////////////////
//
// printLongSummary --
//

void printLongSummary(Array& list, int chunk, int channels) {
   double db, rms, peak;
   int i;
   for (i=0; i<list.getSize(); i++) {
      cout << i * chunk << ":\t";
      if (list[i] == 0.0) {
         db = -1000;
      } else {
         db = 10.0 * log10(list[i]/chunk/channels);
      }
      rms = sqrt(list[i]/chunk/channels);
      peak = sqrt(2.0) * rms;
      cout << db << '\t' << rms << '\t' << peak << "\n";
   }
}



// md5sum: df65bc0564edc975e12594ee774aed81 cutoff.cpp [20050403]