//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Tue May  2 05:04:58 PDT 2006 (copied from sndpower)
// Last Modified: Tue May  2 20:11:48 PDT 2006
// Filename:      ...soundfile/examples/smoothpower.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 printLongSummary(SoundFileRead& soundfile, int totalframes, 
                         int start, int chunk);
double filterk  = 0.3;           // set with -k option
double smoothq  = 1;             // set with -S option
int    achannel = -1;            // set with -c option
int    roundq   = 1;             // set with -R option
int    centerq  = 1;
const char  *comment  = "";      // set with --comment option
const char  *datestring = "";    // set with -d option

int main(int argc, char** argv) {
   Options options;
   options.define("a|average=i:441",   "Number of samples to average over");
   options.define("s|start=i:0",       "Starting sample");
   options.define("c|channel=i:-1",    "Channel to analyze");
   options.define("C|no-center=b",     "Do not center the timing data");
   options.define("d|date=s:",         "Date string");
   options.define("comment=s:",        "Comment string");
   options.define("R|no-rounding=b",   "Turn off db value rounding");
   options.define("S|no-smoothing=b",  "Turn off smoothing");
   options.define("k|smoothing=d:0.3", "the 1/2 smoothing filter value");
   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);
   }

   SoundFileRead soundfile(options.getArg(1));

   filterk   = options.getDouble("smoothing");
   smoothq   = !options.getBoolean("no-smoothing");
   achannel  = options.getInteger("channel");
   roundq    = !options.getBoolean("no-rounding");
   centerq   = !options.getBoolean("no-center");
   if (options.getBoolean("comment")) {
      comment = options.getString("comment");
   }
   if (options.getBoolean("date")) {
      datestring = options.getString("date");
   }
   int chunk = options.getInteger("average");
   int start = options.getInteger("start");
   int totalframes = options.getInteger("count");
   if (totalframes <= 0) {
      totalframes = (soundfile.getSamples() - start)/chunk + 1;
   } 

   // print header information
   if (!options.getBoolean("quiet")) {
      cout << "###Filename:\t\t" << options.getArg(1) << "\n";
      cout << "###FrameSize:\t\t" << chunk << " samples; ";
      cout << 1000.0 * chunk/soundfile.getSrate() << " milliseconds" << "\n";
      cout << "###FrameCount:\t\t" << totalframes << "\n";
      if (datestring[0] != '\0') {
         cout << "###AnalysisDate:\t" << datestring << "\n";
      }
      if (comment[0] != '\0') {
         cout << "###Comment:\t\t" << comment << "\n";
      }
      cout << "###Smoothing:\t\t";
      if (smoothq) {
         cout << filterk;
      } else {
         cout << 1;
      }
      cout << "\n";

      cout << "###Channel:\t\t";
      if (achannel < 0) {
         if (soundfile.getChannels() == 1) {
            cout << "all (mono)";
         } else if (soundfile.getChannels() == 2) {
            cout << "all (stereo)";
         } else if (soundfile.getChannels() == 4) {
            cout << "all (quad)";
         } else {
            cout << "all";
         }
      } else {
         cout << achannel;
	 if (achannel == 0) {
            cout << " (left)";
         } else if (achannel == 1) {
            cout << " (right)";
         }
      }
      cout << "\n";

      cout << "# time (seconds), dB\n";
   }

   if (options.getBoolean("all")) {
      printLongSummary(soundfile, 1, 0, soundfile.getSamples()); 
   } else {
      printLongSummary(soundfile, totalframes, start, chunk);
   }

   return 0;
}



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

void printLongSummary(SoundFileRead& soundfile, int totalframes, int start, int chunk) {
   double sum, value, db;
   int i, j, channel;
   Array<double> dbvalues;
   dbvalues.setSize(totalframes);
   dbvalues.allowGrowth(0);

   for (i=0; i<totalframes; i++) {
      sum = 0.0;
      for (j=0; j<chunk; j++) {
         if (achannel < 0) {
            for (channel=0; channel < soundfile.getChannels(); channel++) {
               value = soundfile.getCurrentSampleDouble(channel);
               sum += value * value;
            }
         } else {
            value = soundfile.getCurrentSampleDouble(achannel);
            sum += value * value;
         }
         soundfile.incrementSample(); 
      }
      if (sum == 0.0) {
         db = -100;
      } else {
         db = 10.0 * log10(sum/chunk/soundfile.getChannels());
      }
     
      if (!smoothq) {
	 if (centerq) {
            cout << (double)((i+0.5) * chunk)/soundfile.getSrate();
         } else {
            cout << (double)(i * chunk)/soundfile.getSrate();
         }
	 if (roundq) {
            cout << "\t" << ((int)(db * 10.0 + 0.5))/10.0 << "\n";
	 } else {
            cout << "\t" << db << "\n";
         }
      } else {
         dbvalues[i] = db;
      }
   }

   if (!smoothq) {
      return;
   }

   // Difference equation for smoothing: y[n] = k * x[n] + (1-k) * y[n-1]
   // do first half of smoothing:
   double oneminusk = 1.0 - filterk;
   for (i=1; i<totalframes; i++) {
      dbvalues[i] = filterk * dbvalues[i] + oneminusk * dbvalues[i-1];
   }

   // do second half of smoothing:
   for (i=totalframes-2; i>=0; i--) {
      dbvalues[i] = filterk * dbvalues[i] + oneminusk * dbvalues[i+1];
   }


   // print results:
   for (i=0; i<totalframes; i++) {
      cout << (double)(i * chunk)/soundfile.getSrate();
      if (roundq) {
         cout << "\t" << ((int)(dbvalues[i] * 10.0 + 0.5))/10.0 << "\n";
      } else {
         cout << "\t" << dbvalues[i] << "\n";
      }
   }
}



// md5sum: 9df46191f314438a1cc3887e5791c510 smoothpower.cpp [20060701]