//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Thu Jul 12 12:47:53 PDT 2001
// Last Modified: Thu Jul 12 15:33:30 PDT 2001
// Filename: ...sig/doc/examples/sig/sigfile/preen/preen.cpp
// Syntax: C++; sig
//
// Description: multiple comb filters affecting an input soundfile
// at various random times. An input file listing
// comb filter applications can be specified with the
// -f command-line option.
//
#include "sigAudio.h"
#include <stdlib.h>
#include <string.h>
#ifndef OLDCPP
#include <iostream>
using namespace std;
#else
#include <iostream.h>
#endif
#define BLANKCHARS " \t\n,:;"
void checkOptions(Options& opts, int argc, char** argv);
void example(void);
void usage(const char* command);
class CombInfo {
public:
int start1; // sample time of comb connect to source
int start2; // sample time of comb connect to summation (not used)
int end1; // sample time of comb disconnect from sound source
int end2; // sample time of comb disconnect from summation
int delay; // delayline length in samples
int id; // from 0 to size - 1;
sampleType gain; // feedback gain of delayline
sampleType scale; // output volume from comb filter
};
// function declarations:
int prepareCombList(int numSamples, Collection<CombInfo>& combinfo);
void printCombInfo(Collection<CombInfo>& combinfo);
int start1compare(const void* a, const void* b);
int end1compare(const void* a, const void* b);
int end2compare(const void* a, const void* b);
// user interface variables:
Options options;
int combcount = 0; // used with the -c option
int printQ = 0; // used with the -p option
int quietQ = 0; // used with the -q option
///////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) {
checkOptions(options, argc, argv);
SoundHeader header;
char* infilename = "";
char* outfilename = NULL;
int numSamples;
if (options.getArgCount() == 1) { // use whitenoise if no input sound
outfilename = options.getArg(1);
header.setHighMono();
if (options.getInt("samples") > 0) {
numSamples = options.getInt("samples");
} else {
numSamples = (int)(options.getDouble("duration") * 44100 + 0.5);
}
} else { // use input soundfile
infilename = options.getArg(1);
header.setHeader(infilename);
numSamples = header.getSamples();
outfilename = options.getArg(2);
}
// Elements:
SoundFileIn insound(infilename);
SoundFileOut outsound(outfilename, header);
Envelope noiseenv(options.getString("gain-env"));
WhiteNoise noise(1);
Empty soundsource;
Multiply mul;
Multiply nmul;
Array<Comb> comb;
Array<Scale> combscale;
Add combsum;
Envelope globalscale(options.getString("amp-env"));
DCBlock dcblock;
Collection<CombInfo> combinfo;
numSamples = prepareCombList(numSamples, combinfo);
comb.setSize(combinfo.getSize());
combscale.setSize(combinfo.getSize());
Collection<CombInfo*> combend1(combinfo.getSize());
Collection<CombInfo*> combend2(combinfo.getSize());
int i;
for (i=0; i<combinfo.getSize(); i++) {
combscale[i].connect(comb[i]);
combend1[i] = &combinfo[i];
combend2[i] = &combinfo[i];
}
qsort(combend1.getBase(), combend1.getSize(), sizeof(CombInfo*),
end1compare);
qsort(combend2.getBase(), combend2.getSize(), sizeof(CombInfo*),
end2compare);
// Connections:
dcblock.connect(mul);
outsound.connect(dcblock);
mul.connect(combsum);
mul.connect(globalscale);
nmul.connect(noise);
nmul.connect(noiseenv);
if (strlen(infilename) == 0) { // use noise
soundsource.connect(nmul);
} else { // use input file
soundsource.connect(insound);
}
if (printQ) {
printCombInfo(combinfo);
}
int s1i = 0;
int e1i = 0;
int e2i = 0;
int csize = combinfo.getSize();
for (i=0; i<numSamples; i++) {
topcheck:
if (s1i < csize && combinfo[s1i].start1 <= i) {
if (!quietQ) {
cout << "Starting " << combinfo[s1i].id << " at time " << i << endl;
}
// connect sound source to a comb filter and to summation
comb[combinfo[s1i].id].connect(soundsource, 0);
comb[combinfo[s1i].id].setDelaySamples(combinfo[s1i].delay);
comb[combinfo[s1i].id].setGain(combinfo[s1i].gain);
comb[combinfo[s1i].id].clear();
combscale[combinfo[s1i].id].setScale(combinfo[s1i].scale);
combsum.connect(combscale[combinfo[s1i].id]);
s1i++;
goto topcheck; // incase of multiple combs starting at same time
}
if (e1i < csize && combinfo[e1i].end1 <= i) {
if (!quietQ) {
cout << "Ending " << combinfo[e1i].id << " at time " << i << endl;
}
// disconnect the comb from the sound source
comb[combinfo[e1i].id].disconnect(0);
e1i++;
goto topcheck;
}
if (e2i < csize && combinfo[e2i].end2 <= i) {
// disconnect the comb from the summation after it has died out
if (!quietQ) {
cout << "Finishing "<< combinfo[e2i].id << " at time " << i << endl;
}
combsum.disconnect(comb[combinfo[e2i].id]);
e2i++;
goto topcheck;
}
Tick(outsound);
}
return 0;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////////
//
// printCombInfo --
//
void printCombInfo(Collection& combinfo) {
int i;
cout << "# start\tend\tfreq\tdecay\tvol\n";
for (i=0; i<combinfo.getSize(); i++) {
cout << "comb\t";
cout << combinfo[i].start1; // start
cout << "\t" << combinfo[i].end1; // end;
cout << "\t" << 44100.0 / combinfo[i].delay; // resonance frequency
cout << "\t" << -3.0 / combinfo[i].delay /
log10(combinfo[i].gain); // decay
cout << "\t" << combinfo[i].scale; // volume gain
cout << "\n";
}
}
//////////////////////////////
//
// prepareCombList -- generate a list of the comb filter insertions
// and deletions into the audio signal stream.
//
int prepareCombList(int numSamples, Collection& combinfo) {
combinfo.setSize(combcount);
combinfo.setSize(0);
CombInfo info;
if (options.getBoolean("comb-file")) {
#ifndef OLDCPP
fstream combfile(options.getString("comb-file"), ios::in);
#else
fstream combfile(options.getString("comb-file"), ios::in | ios::nocreate);
#endif
if (!combfile.is_open()) {
cout << "Error cannot open file: " << options.getBoolean("comb-file")
<< " for reading of comb filter settings" << endl;
exit(1);
}
int id = 0;
int in_start;
int in_end;
double in_freq;
double in_decay;
double in_volume;
char buffer[1024] = {0};
char* sptr;
combfile.getline(buffer, 512, '\n');
while (!combfile.eof()) {
sptr = strtok(buffer, BLANKCHARS);
if (sptr == NULL || strcmp(sptr, "comb") != 0) {
combfile.getline(buffer, 512, '\n');
continue;
}
sptr = strtok(NULL, BLANKCHARS);
if (sptr == NULL) {
combfile.getline(buffer, 512, '\n');
continue;
}
in_start = atoi(sptr);
sptr = strtok(NULL, BLANKCHARS);
if (sptr == NULL) {
combfile.getline(buffer, 512, '\n');
continue;
}
in_end = atoi(sptr);
sptr = strtok(NULL, BLANKCHARS);
if (sptr == NULL) {
combfile.getline(buffer, 512, '\n');
continue;
}
in_freq = strtod(sptr, NULL);
sptr = strtok(NULL, BLANKCHARS);
if (sptr == NULL) {
combfile.getline(buffer, 512, '\n');
continue;
}
in_decay = strtod(sptr, NULL);
sptr = strtok(NULL, BLANKCHARS);
if (sptr == NULL) {
combfile.getline(buffer, 512, '\n');
continue;
}
in_volume = strtod(sptr, NULL);
info.start1 = in_start;
info.delay = (int)(44100 / in_freq + 0.5);
if (info.delay <10) {
info.delay = 10;
}
info.start2 = info.start1 + info.delay;
info.end1 = in_end;
if (info.end1 < info.start1 + info.delay) {
info.end1 = info.start1 + info.delay + 100;
}
info.gain = pow(0.001, 1.0/(info.delay * in_decay));
info.end2 = info.end1 + (int)(-3.0 / info.delay / log10(info.gain));
info.id = id++;
info.scale = in_volume;
combinfo.append(info);
if (numSamples < info.end2) {
numSamples = info.end2;
}
}
} else {
Distribution u; u.doUniform(0,numSamples);
Distribution scaled; scaled.doTriangular(0.001, 0.1, 0.4);
Distribution scalev; scalev.doTriangular(0.75, 0.97, 0.999);
Distribution g; g.doGaussian(numSamples/10.0, 0.0);
Distribution delayg; delayg.doGaussian(1000.0, 2000.0);
for (int i=0; i<combcount; i++) {
info.start1 = (int)u.value();
info.delay = abs((int)(44100.0/delayg.value())); // fixed srate
if (info.delay < 10) {
info.delay += 10;
}
info.start2 = info.start1 + info.delay;
info.end1 = info.start1 + (int)(fabs(g.value()));
if (info.end1 - info.start1 < info.delay) {
info.end1 = info.start1 + info.delay + 1000;
}
info.gain = scalev.value();
info.end2 = info.end1 + (int)(-3.0/(info.delay * log10(info.gain)));
info.scale = scaled.value();
info.id = i;
combinfo.append(info);
if (info.end2 > numSamples) {
numSamples = info.end2;
}
}
}
qsort(combinfo.getBase(), combinfo.getSize(), sizeof(CombInfo),
start1compare);
return numSamples + 1000;
}
//////////////////////////////
//
// checkOptions -- handle command-line options.
//
void checkOptions(Options& opts, int argc, char** argv) {
opts.define("a|amp|amp-env=s:0 0.2 1 0.2", "Amplitude env output");
opts.define("g|gain-env=s:0 1 1 1", "Amplitude env output for noise");
opts.define("d|dur|duration=d:1.0 second", "Time in sec. for noise");
opts.define("s|samples=i", "Number of white noise sample if no soundfile");
opts.define("c|count=i:10", "Number of comb filter to generate");
opts.define("p|print=b", "For printing comb info parameters used");
opts.define("q|quiet=b", "For printing comb progress");
opts.define("f|file|comb-file=s", "Filename for input file of comb info");
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, July 2001" << endl;
exit(0);
}
if (opts.getBoolean("version")) {
cout << "Version 12 July 2001" << endl;
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 input and one output filename
if (opts.getArgCount() == 0) {
cout << "Error: need one output file name." << endl;
usage(opts.getCommand());
exit(1);
} else if (opts.getArgCount() > 2) {
cout << "Error: too many arguments. Given "
<< opts.getArgCount() << " but need only 2." << endl;
usage(opts.getCommand());
exit(1);
}
combcount = opts.getInteger("count");
printQ = opts.getBoolean("print");
quietQ = opts.getBoolean("quiet");
}
//////////////////////////////
//
// example -- gives example calls to the comb program.
//
void example(void) {
}
//////////////////////////////
//
// usage -- how to run the osc program on the command line.
//
void usage(const char* command) {
cout << endl;
cout <<
"A comb filter. Uses whitenoise if no input sound is specified" << endl;
cout << endl;
cout << "Usage: " << command << " [insound] outsound" << endl;
cout << endl;
}
///////////////////////////////
//
// start1compare -- for sorting the CombInfo entries by start1
//
int start1compare(const void* a, const void* b) {
CombInfo& aa = *((CombInfo*)a);
CombInfo& bb = *((CombInfo*)b);
if (aa.start1 < bb.start1) {
return -1;
} else if (aa.start1 > bb.start1) {
return 1;
} else {
return 0;
}
}
///////////////////////////////
//
// end1compare -- for sorting the CombInfo* entries by end1
//
int end1compare(const void* a, const void* b) {
CombInfo& aa = **((CombInfo**)a);
CombInfo& bb = **((CombInfo**)b);
if (aa.end1 < bb.end1) {
return -1;
} else if (aa.end1 > bb.end1) {
return 1;
} else {
return 0;
}
}
///////////////////////////////
//
// end2compare -- for sorting the CombInfo* entries by end1
//
int end2compare(const void* a, const void* b) {
CombInfo& aa = **((CombInfo**)a);
CombInfo& bb = **((CombInfo**)b);
if (aa.end2 < bb.end2) {
return -1;
} else if (aa.end2 > bb.end2) {
return 1;
} else {
return 0;
}
}
/* Sample comb parameter file for use with -f option:
# start end freq decay vol
comb 1119 9182 604.11 1.17297 0.191731
comb 1669 2051 1378.12 0.9478 0.209458
comb 4900 12895 2100 6.91364 0.146091
comb 4996 7041 2321.05 2.01521 0.263378
comb 7384 10647 2004.55 2.39535 0.189616
comb 8061 17086 4009.09 7.71661 0.330953
comb 8081 10681 2100 5.37237 0.260881
comb 8089 9199 2940 3.61925 0.0489331
comb 12809 13335 2940 21.4995 0.0354243
comb 17572 19144 2004.55 2.46674 0.172416
comb 17681 23163 2004.55 1.74748 0.209238
comb 19288 23852 711.29 0.76728 0.17405
comb 20696 24902 3675 20.2594 0.196204
comb 21635 22399 864.706 1.26352 0.117229
comb 21909 23868 1160.53 2.76592 0.0903678
comb 22520 26817 1837.5 2.09309 0.159363
comb 25045 32946 1575 1.43017 0.0452757
comb 26165 27622 1837.5 4.24314 0.0983655
comb 27883 29013 2321.05 5.73193 0.183661
comb 30439 37054 2450 5.95126 0.150158
comb 30739 33374 1225 5.87005 0.139962
comb 31920 32785 1225 8.8128 0.151489
comb 31966 32435 2594.12 3.28092 0.0921744
comb 36238 42315 2321.05 4.28848 0.145472
comb 37066 37779 2940 2.32236 0.151643
comb 38014 39960 1696.15 27.6497 0.132293
comb 38237 46489 1191.89 1.86635 0.119446
comb 38678 38833 1696.15 3.38284 0.0882454
comb 41651 42534 4009.09 12.9445 0.0445691
comb 41686 44190 2321.05 12.7503 0.131357
*/
/* Another test comb parameter file:
#
# preen score file for use with sound sounds/base/paperjam-French.au
# Thu Jul 12 20:57:06 PDT 2001
# Craig Stuart Sapp
#
# preen -a 1.2 -f preen.data sounds/base/paperjam-French.wav /tmp/output.wav
#
# start end freq decay vol
comb 100 10000 440 1.17297 0.2
comb 10000 20000 140 0.17297 0.4
comb 12000 29000 1760 1.17297 0.2
comb 18000 20000 2760 40.1729 0.4
comb 22000 30000 5000 100.0 0.05
comb 29000 45000 300 1.0 0.15
comb 44000 60000 220 2.0 0.1
comb 34000 52000 110 0.5 0.2
comb 50000 60000 660 1.1 0.3
comb 55000 63000 330 1.1 0.4
comb 60000 73000 1530 49.1 0.1
*/
// md5sum: f572a402545b6037c529892b631f3f88 preen.cpp [20050403]