//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Tue Apr 14 19:05:47 PDT 1998
// Last Modified: Wed Apr 15 00:18:13 PDT 1998
// Filename:      ...sig/doc/examples/sig/sigfile/bleeps/bleeps.cpp
// Syntax:        C++; sig
//
// Description:   creates a unique sonic texture 
//

#include "sigAudio.h"

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

#ifndef OLDCPP
   #include <iostream>
   #include <sstream>
   #define SSTREAM stringstream
   #define CSTRING str().c_str()
   using namespace std;
#else
   #include <iostream>
   #ifdef VISUAL
      #include <strstrea.h>
   #else
      #include <strstream.h>
   #endif
   #define SSTREAM strstream
   #define CSTRING str()
#endif
   

void checkOptions(Options& opts, int argc, char** argv);
void distributions(void);
void example(void);
void getRange(const char* aString, double& p1, double& p2, double& p3);
void usage(const char* command);


///////////////////////////////////////////////////////////////////////////

Distribution randFrequency;         // random number generator for freq
Distribution randAmplitude;         // random number generator for amp
Distribution randTime;              // random number generator for time

double freq1, freq2, freq3;         // parameters for frequencies in Hertz
double amp1, amp2, amp3;            // parameters for amplitudes in dB
double time1, time2, time3;         // parameters for time durations in seconds

double amp = 0;
double ampold = 0;

int main(int argc, char* argv[]) {
   Options options;
   checkOptions(options, argc, argv);

   int maxSamples;
   int numSamples;
   int sampleCount = 0;
   if (options.getInt("samples") > 0) {
      maxSamples = options.getInt("samples");
   } else {
      maxSamples = (int)(options.getDouble("duration") * 44100 + 0.5);  
   }

   // prepare for a monophonic output file at 44.1 kHz sampling rate
   SoundHeader header;
   header.setHighMono();

   // Elements:
   Constant     frequency;
   Envelope     ampenv("0 0 1 1", 1000);
   Osc          osc;
   SoundFileOut outsound(options.getArg(1), header);

   // Connections:
   osc.connect(frequency, 0);
   osc.connect(ampenv, 1);
   outsound.connect(osc);

   Action action;

   SSTREAM *ampstring;

   numSamples = (int)(fabs(randTime.value()) * 44100 + 0.5);
   ampold = amp;
   amp = pow(10.0, randAmplitude.value()/20);
   ampstring = new SSTREAM;
   *ampstring << "0 " << ampold << " 1 " << amp << ends;
   ampenv.setDuration(numSamples+1);
   ampenv.setEnvelope(ampstring->CSTRING);
   delete ampstring;

   do {
      ampstring = new SSTREAM;
      frequency.setValue(fabs(randFrequency.value()));
      numSamples = (int)(fabs(randTime.value()) * 44100 + 0.5);
      ampold = amp;
      amp = pow(10.0, randAmplitude.value()/20);
      *ampstring << "0 " << ampold << " 1 " << amp << ends;
      ampenv.setDuration(numSamples+1);
      ampenv.setEnvelope(ampstring->CSTRING);
      sampleCount += numSamples;
      action.tick(outsound, numSamples);
      delete ampstring;
   } while (sampleCount < maxSamples);

   return 0;
}


///////////////////////////////////////////////////////////////////////////


//////////////////////////////
//
// checkOptions -- handle command-line options.
//

void checkOptions(Options& opts, int argc, char** argv) {
   opts.define("f|freq|frequency=s:440 880 Hertz");
   opts.define("a|amp|amplitude=s:-20 -5 dB");
   opts.define("t|time=s:0.1 1.0 second");
   opts.define("ftype|f-type=s:uniform");
   opts.define("atype|a-type=s:uniform");
   opts.define("ttype|t-type=s:uniform");
   opts.define("d|dur|duration=s:0.1 1.0 second");
   opts.define("s|samples=i");
   opts.define("distributions=b");
   opts.define("author=b");
   opts.define("version=b");
   opts.define("example=b");
   opts.define("help=b");
   opts.process(argc, argv);

   if (opts.getBoolean("author")) {
      cout << "Written by Craig Stuart Sapp, "
           << "craig@ccrma.stanford.edu, April 1998" << endl;
      exit(0);
   }
   if (opts.getBoolean("version")) {
      cout << "compiled: " << __DATE__ << endl;
      cout << SIG_VERSION << endl;
      exit(0);
   }
   if (opts.getBoolean("help")) {
      usage(opts.getCommand());
      exit(0);
   }
   if (opts.getBoolean("example")) {
      example();
      exit(0);
   }
   if (opts.getBoolean("distributions")) {
      distributions();
      exit(0);
   }

   getRange(opts.getString("frequency"), freq1, freq2, freq3);
   getRange(opts.getString("amplitude"), amp1, amp2, amp3);
   getRange(opts.getString("time"), time1, time2, time3);


   if (strcmp("gaussian", opts.getString("ftype")) == 0) {
      randFrequency.doGaussian(freq2, freq1);
   } else if (strcmp("triangular", opts.getString("ftype")) == 0) {
      randFrequency.doTriangular(freq1, freq3, freq2);
   } else if (strcmp("cauchy", opts.getString("ftype")) == 0) {
      randFrequency.doCauchy(freq2, freq1);
   } else {
      randFrequency.doUniform(freq1, freq2);
   }
      

   if (strcmp("gaussian", opts.getString("atype")) == 0) {
      randAmplitude.doGaussian(amp2, amp1);
   } else if (strcmp("triangular", opts.getString("atype")) == 0) {
      randAmplitude.doTriangular(amp1, amp3, amp2);
   } else if (strcmp("cauchy", opts.getString("atype")) == 0) {
      randAmplitude.doCauchy(amp2, amp1);
   } else {
      randAmplitude.doUniform(amp1, amp2);
   }
      

   if (strcmp("gaussian", opts.getString("ttype")) == 0) {
      randTime.doGaussian(time2, time1);
   } else if (strcmp("triangular", opts.getString("ttype")) == 0) {
      randTime.doTriangular(time1, time3, time2);
   } else if (strcmp("cauchy", opts.getString("ttype")) == 0) {
      randTime.doCauchy(time2, time1);
   } else {
      randTime.doUniform(time1, time2);
   }
      

   // can only have one output filename
   if (opts.getArgCount() == 0) {
      cout << "Error: need one output file name." << endl;
      usage(opts.getCommand());
      exit(1);
   } else if (opts.getArgCount() > 1) {
      cout << "Error: too many arguments.  Given "
           << opts.getArgCount() << " but need only 1." << endl;
      usage(opts.getCommand());
      exit(1);
   }

}



//////////////////////////////
//
// distributions -- print the types of distributions available
//

void distributions(void) {
   cout << 
" Types of random distributions availble:                                   \n"
"                                                                           \n"
"   uniform                                                                 \n"
"	parameter 1 = minimum value                                         \n"
" 	parameter 2 = maximum value                                         \n"
"                                                                           \n"
"   gaussian                                                                \n"
"	parameter 1 = center value                                          \n"
"	parameter 2 = deviation value                                       \n"
"                                                                           \n"
"   triangle                                                                \n"
"	parameter 1 = minimum value                                         \n"
"	parameter 2 = maximum value                                         \n"
"	parameter 3 = peak frequency (betweem min and max frequency)        \n"
"                                                                           \n"
"   cauchy                                                                  \n"
"	parameter 1 = center value                                          \n"
"	parameter 2 = spread value                                          \n"
"                                                                           \n"
" note negative frequencies and times are converted to positive frequencies.\n"
   << endl;
};



//////////////////////////////
//
// example -- gives example calls to the osc program.
//

void example(void) {
   cout <<
   "#                                                                      \n"
   "#                                                                      \n"
   << endl;
}



//////////////////////////////
//
// getRange -- extracts three numbers from a string.
//

void getRange(const char* aString, double& p1, double& p2, double& p3) {
   char *temp = new char[strlen(aString)+1];
   char *number;

   strcpy(temp, aString);
   number = strtok(temp, " \n\t,(){}[]|");
   if (number == NULL) {
      cout << "Error: string \"" << aString << "\" contains no numbers" << endl;
      exit(1);
   }
   p1 = strtod(number, NULL);
   number = strtok(NULL, " \n\t,(){}[]|");
   if (number == NULL) {
      p3 = p2 = p1;
      return;
   } 
   p2 = strtod(number, NULL);

   number = strtok(NULL, " \n\t,(){}[]|");
   if (number == NULL) {
      p3 = p2;
      return;
   } else {
      p3 = strtod(number, NULL);
   }
}



//////////////////////////////
//
// usage -- how to run the bleeps program on the command line.
//

void usage(const char* command) {
   cout << endl;
   cout << "Generates random sinewaves." << endl;
   cout << endl;
   cout << "Usage: " << command << " [-d duration|-s samples][-a amp-env]"
            "[-h hold] outsound" << endl;
   cout << endl;
   cout << "Options:" << endl;
   cout << "   -d = duration of the output in seconds. (default 1.0) \n";
   cout << "   -s = duration of the output in samples. (deault is null)\n";
   cout << "        will override the -d option if specified\n";
   cout << "   -a = amplitude range (default \"-20 -5\") \n";
   cout << "   -f = frequency range (default \"440 880\") \n";
   cout << "   -t = time range (default \"0.1 1.0 seconds\") \n";
   cout << "   --ftype = frequency distribution type (default uniform) \n";
   cout << "   --atype = amplitude distribution type (default uniform) \n";
   cout << "   --ttype = time distribution type (default uniform) \n";
   cout << "   --options = list of all options, aliases and defaults \n";
   cout << "\n";
   cout << "Distribution types: uniform, gaussian, triangle, and cauchy.\n";
   cout << "   use the --distributions options to see more details on\n";
   cout << "   the distribution types\n";
   cout << endl;
}



// md5sum: e9ae4288d8bf9db6b795f82ec89b1897 bleeps.cpp [20050403]