//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat Sep 7 16:10:45 2002
// Last Modified: Sat Sep 7 22:30:41 2002
// Filename: ...sig/doc/examples/sig/sigfile/addsyn/addsyn.cpp
// Syntax: C++; sig
//
// Description: make a soundfile with the specified moveable
// frequency/amplitude sinewaves
//
#include "sigAudio.h"
#include <stdlib.h>
#include <ctype.h>
#ifndef OLDCPP
#include <iostream>
#include <fstream>
using namespace std;
#else
#include <iostream.h>
#include <fstream.h>
#endif
class OscInfo {
public:
int startSample;
int stopSample;
double phase;
double freq;
double amp;
void setFreqEnv (const char* string) {
int len = strlen(string);
if (freqEnv != NULL) {
delete [] freqEnv;
}
freqEnv = new char[len+1];
strcpy(freqEnv, string);
}
void setAmpEnv (const char* string) {
int len = strlen(string);
if (ampEnv != NULL) {
delete [] ampEnv;
}
ampEnv = new char[len+1];
strcpy(ampEnv, string);
}
void print(void) {
cout << "Start sample: " << startSample << endl;
cout << "Stop sample: " << stopSample << endl;
cout << "Phase: " << phase << endl;
cout << "Frequency: " << freq << endl;
cout << "Amplitude: " << amp << endl;
cout << "FreqEnv: " << getFreqEnv() << endl;
cout << "AmpEnv: " << getAmpEnv() << endl;
cout << "======================" << endl;
}
const char* getFreqEnv(void) {
if (freqEnv == NULL) return "";
else return freqEnv;
}
const char* getAmpEnv(void) {
if (ampEnv == NULL) return "";
else return ampEnv;
}
OscInfo(void) { freqEnv = ampEnv = NULL; clear(); }
OscInfo(OscInfo& a) {
startSample = a.startSample;
stopSample = a.stopSample;
phase = a.phase;
freq = a.freq;
amp = a.amp;
setFreqEnv(a.getFreqEnv());
setAmpEnv(a.getAmpEnv());
}
OscInfo& operator=(OscInfo& a) {
if (&a == this) return *this;
startSample = a.startSample;
stopSample = a.stopSample;
phase = a.phase;
freq = a.freq;
amp = a.amp;
setFreqEnv(a.getFreqEnv());
setAmpEnv(a.getAmpEnv());
return *this;
}
void clear(void) {
startSample = stopSample = 0;
freq = amp = -1000.0;
phase = 0.0;
if (freqEnv != NULL) {
delete [] freqEnv;
freqEnv = NULL;
}
if (ampEnv != NULL) {
delete [] ampEnv;
ampEnv = NULL;
}
}
private:
char* freqEnv;
char* ampEnv;
};
class ActionTime {
public:
int time;
int number;
ActionTime(void) { time = number = 0; }
};
// function declarations:
void checkOptions(Options& opts);
void example(void);
void usage(const char* command);
int timesort(const void* a, const void* b);
void buildOscInfo(Array<OscInfo>& oscinfo, const char* filename);
void setOnTimes(Array<ActionTime>& ontimes, Array<OscInfo>& oscinfo);
void setOffTimes(Array<ActionTime>& offtimes, Array<OscInfo>& oscinfo);
void checkOnConnections(int currtime, Add& add, Array<Osc>& osc,
Array<ActionTime>& ontimes, int& onindex);
void checkOffConnections(int currtime, Add& add, Array<Osc>& osc,
Array<ActionTime>& offtimes, int& offindex);
void prepareOscillators(Array<OscInfo>& oscinfo, Array<Osc>& osc,
Array<Envelope>& freqEnv, Array<Envelope>& ampEnv);
// global variables:
Array<OscInfo> oscinfo;
int srate = 44100; // sampling rate of output soundfile
int verboseQ = 0; // used with the -v option
///////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) {
Options options(argc, argv);
checkOptions(options);
// determine how many samples in the output based on the input data
int numSamples = 0;
int i;
for (i=0; i<oscinfo.getSize(); i++) {
if (oscinfo[i].stopSample >= numSamples) {
numSamples = oscinfo[i].stopSample + 1;
}
}
Array<ActionTime> ontimes;
Array<ActionTime> offtimes;
int onindex = 0;
int offindex = 0;
setOnTimes(ontimes, oscinfo);
setOffTimes(offtimes, oscinfo);
// prepare for a monophonic output file
SoundHeader header;
header.setHighMono();
Array<Envelope> freqEnv(oscinfo.getSize());
Array<Envelope> ampEnv(oscinfo.getSize());
Array<Osc> osc(oscinfo.getSize());
prepareOscillators(oscinfo, osc, freqEnv, ampEnv);
Add add;
SoundFileOut outsound(options.getArg(2), header);
// connections
outsound.connect(add);
for (i=0; i<numSamples; i++) {
checkOnConnections(i, add, osc, ontimes, onindex);
outsound.tick(i);
checkOffConnections(i, add, osc, offtimes, offindex);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// prepareOscillators --
//
void prepareOscillators(Array<OscInfo>& oscinfo, Array<Osc>& osc,
Array<Envelope>& freqEnv, Array<Envelope>& ampEnv) {
int i;
for (i=0; i<oscinfo.getSize(); i++) {
if (oscinfo[i].freq == -1000.0) {
freqEnv[i].setEnvelope(oscinfo[i].getFreqEnv());
freqEnv[i].setDurationSamples(oscinfo[i].stopSample -
oscinfo[i].startSample);
osc[i].connect(freqEnv[i], 0);
} else {
osc[i].connect(oscinfo[i].freq, 0);
}
if (oscinfo[i].amp == -1000.0) {
ampEnv[i].setEnvelope(oscinfo[i].getAmpEnv());
ampEnv[i].setDurationSamples(oscinfo[i].stopSample -
oscinfo[i].startSample);
osc[i].connect(ampEnv[i], 1);
} else {
osc[i].setAmplitude(oscinfo[i].amp);
}
osc[i].setPhase(oscinfo[i].phase/360.0);
}
}
//////////////////////////////
//
// checkOnConnections --
//
void checkOnConnections(int currtime, Add& add,
Array<Osc>& osc, Array<ActionTime>& ontimes,
int& onindex) {
int i;
for (i=onindex; i<ontimes.getSize(); i++) {
if (currtime < ontimes[i].time) {
break;
} else {
add.connect(osc[ontimes[i].number]);
}
}
onindex = i;
}
//////////////////////////////
//
// checkOffConnections --
//
void checkOffConnections(int currtime, Add& add, Array<Osc>& osc,
Array<ActionTime>& offtimes, int& offindex) {
int i;
for (i=offindex; i<offtimes.getSize(); i++) {
if (currtime < offtimes[i].time) {
break;
} else {
add.disconnect(osc[offtimes[i].number]);
}
}
offindex = i;
}
//////////////////////////////
//
// setOnTimes -- collect and sort the on times.
//
void setOnTimes(Array& ontimes, Array& oscinfo) {
ontimes.setSize(oscinfo.getSize());
int i;
for (i=0; i<ontimes.getSize(); i++) {
ontimes[i].number = i;
ontimes[i].time = oscinfo[i].startSample;
}
qsort(ontimes.getBase(), ontimes.getSize(), sizeof(ActionTime), timesort);
}
//////////////////////////////
//
// setOffTimes -- collect and sort the off times.
//
void setOffTimes(Array& offtimes, Array& oscinfo) {
offtimes.setSize(oscinfo.getSize());
int i;
for (i=0; i<offtimes.getSize(); i++) {
offtimes[i].number = i;
offtimes[i].time = oscinfo[i].stopSample;
}
qsort(offtimes.getBase(), offtimes.getSize(), sizeof(ActionTime), timesort);
}
//////////////////////////////
//
// timesort -- sort by specified time
//
int timesort(const void* a, const void* b) {
ActionTime &A = *((ActionTime*)a);
ActionTime &B = *((ActionTime*)b);
if (A.time < B.time) {
return -1;
} else if (A.time > B.time) {
return +1;
} else {
return 0;
}
}
//////////////////////////////
//
// checkOptions -- handle command-line options.
//
void checkOptions(Options& opts) {
opts.define("v|verbose=b", "display input osc data");
opts.define("author=b");
opts.define("version=b");
opts.define("example=b");
opts.define("help=b");
opts.process();
if (opts.getBoolean("author")) {
cout << "Written by Craig Stuart Sapp, "
<< "craig@ccrma.stanford.edu, September 2002" << 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);
}
// can only have one output filename
if (opts.getArgCount() != 2) {
cout << "Error: need two arguments." << endl;
usage(opts.getCommand());
exit(1);
}
verboseQ = opts.getBoolean("verbose");
oscinfo.setSize(0);
buildOscInfo(oscinfo, opts.getArg(1));
}
//////////////////////////////
//
// buildOscInfo --
//
void buildOscInfo(Array& oscinfo, const char* filename) {
#ifndef OLDCPP
fstream infile(filename, ios::in);
#else
fstream infile(filename, ios::in | ios::nocreate);
#endif
if (!infile.is_open()) {
cout << "Error: cannot open file: " << filename << endl;
exit(1);
}
oscinfo.setSize(10000);
oscinfo.setGrowth(10000);
oscinfo.setSize(0);
OscInfo tempinfo;
char buffer[100001] = {0};
infile.getline(buffer, 100000, '\n');
double starttime = 0.0;
double duration = 0.0;
double phase = 0.0;
double frequency = -1000;
double amplitude = -1000;
char* ptr = NULL;
int line = 1;
while (!infile.eof()) {
tempinfo.clear();
frequency = -1000.0;
amplitude = -1000.0;
phase = 0;
if (buffer[0] == ';' || buffer[0] == '/' || buffer[0] == '#') {
infile.getline(buffer, 100000, '\n');
line++;
continue;
}
if (strncmp(buffer, "osc", 3) == 0 ||
strncmp(buffer, "OSC", 3) == 0 ||
strncmp(buffer, "Osc", 3) == 0 ) {
ptr = strtok(buffer, " \t\n:,;"); // OSC identifier
ptr = strtok(NULL, " \t\n:,;"); // start time (required)
if (ptr == NULL) {
cout << "Error on line " << line
<< ": expecting start time." << endl;
exit(1);
}
starttime = strtod(ptr, NULL);
ptr = strtok(NULL, " \t\n:,;"); // duration (required)
if (ptr == NULL) {
cout << "Error on line " << line
<< ": expecting duration." << endl;
exit(1);
}
duration = strtod(ptr, NULL);
if (duration < 0.0) {
duration = 0.0;
}
ptr = strtok(NULL, " \t\n:,;"); // frequency (optional)
if (ptr != NULL) {
frequency = strtod(ptr, NULL);
}
ptr = strtok(NULL, " \t\n:,;"); // amplitude (optional)
if (ptr != NULL) {
amplitude = strtod(ptr, NULL);
}
ptr = strtok(NULL, " \t\n:,;"); // phase (optional)
if (ptr != NULL) {
phase = strtod(ptr, NULL);
}
infile.getline(buffer, 100000, '\n');
line++;
// cout << "Start = " << starttime << endl;
// cout << "Duration = " << duration << endl;
if (duration == 0.0) {
continue;
}
tempinfo.startSample = (int)(starttime * srate + 0.5);
tempinfo.stopSample = (int)((starttime + duration) * srate + 0.5);
if (frequency != -1000.0) {
tempinfo.freq = frequency;
}
if (amplitude != -1000.0) {
tempinfo.amp = amplitude;
}
tempinfo.phase = phase;
while (isspace(buffer[0])) {
int n = 0;
while ((buffer[n] != '\0') && isspace(buffer[n])) {
n++;
}
if (strncmp(&buffer[n], "freq", 4) == 0) {
n = n+4;
while ((buffer[n] != '\0') && isspace(buffer[n])) { n++; }
tempinfo.setFreqEnv(&buffer[n]);
// cout << "Found freq env: " << &buffer[n]<< endl;
} else if (strncmp(&buffer[n], "amp", 3) == 0) {
n = n+3;
while ((buffer[n] != '\0') && isspace(buffer[n])) { n++; }
tempinfo.setAmpEnv(&buffer[n]);
// cout << "Found amp env: " << &buffer[n] << endl;
} else if (strncmp(&buffer[n], "phase", 5) == 0) {
n = n+5;
while ((buffer[n] != '\0') && isspace(buffer[n])) { n++; }
// cout << "Found phase: " << &buffer[n] << endl;
tempinfo.phase = strtod(&buffer[n], NULL);
}
infile.getline(buffer, 100000, '\n');
line++;
}
if (verboseQ) {
tempinfo.print();
}
oscinfo.append(tempinfo);
continue;
}
infile.getline(buffer, 100000, '\n');
line++;
// cout << buffer << endl;
}
}
//////////////////////////////
//
// example -- gives example calls to the addsyn program.
//
void example(void) {
cout <<
" addsyn script output.wav \n"
" where the file script contains: \n"
" \n"
"osc 0.0 1.0 440 0.5 0 \n"
"osc 0.3 0.5 880 0.1 \n"
" \n"
"osc 1.0 1.0 \n"
" freq 0 0 1 1000 2 500 \n"
" amp 0 0 5 0.5 95 0.5 100 0 \n"
" phase 90 \n"
"; script comment \n"
" \n"
<< endl;
}
//////////////////////////////
//
// usage -- how to run the osc program on the command line.
//
void usage(const char* command) {
cout <<
" \n"
"Creates a frequency and amplitude varying sinusoids from a script file. \n"
" \n"
"Usage: " << command << " script outsound \n"
" \n"
"Options: \n"
" -v = display a list of the parsed oscillator inputs. \n"
" --options = list of all options, aliases and default values. \n"
" \n"
" \n"
<< endl;
}
// md5sum: abd9ba92e401a40ae6fd7474da7d40f8 addsyn.cpp [20050403]