//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Fri Nov 18 09:18:15 PST 2005
// Last Modified: Fri Nov 18 09:18:18 PST 2005
// Filename: ...soundfile/examples/tempochange.cpp
// Syntax: C++
//
// Description: Make a click track for experiments in tempo perception.
//
#include "soundfile.h"
#include <stdlib.h>
#ifndef OLDCPP
#include <iostream>
#include <fstream>
using namespace std;
#else
#include <iostream.h>
#include <fstream.h>
#endif
#define CLICKWIDTH 44
void getClickTimes(Array<long>& clicktimes, double startdur, double gain);
//////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {
srand48(time(NULL));
Options options;
options.define("p|padding=i:1000", "samples of slience at ends of sound");
options.define("a|amp=d:0.25", "amplitude scaling factor");
options.define("n|clicks=i:16", "number of clicks to produce");
options.define("s|startdur=d:1000.0", "starting duration in milliseconds");
options.define("g|gain|f|fraction=d:0.0", "the fractional change between beats");
options.define("v|verbose=b", "verbose: display timings of clicks");
options.define("t|tempo=d:60.0", "starting tempo");
options.define("r|randgain=b", "randomize gain direction");
options.process(argc, argv);
int clickcount = options.getInteger("clicks");
int padding = options.getInteger("padding");
double startdur = options.getDouble("startdur");
double gain = options.getDouble("gain");
double amp = options.getDouble("amp");
const char* outputname = options.getArg(1);
if (options.getBoolean("tempo")) {
startdur = 60.0 / options.getDouble("tempo") * 1000.0;
}
if (startdur < 10) {
startdur = 10;
} else if (startdur > 10000) {
startdur = 10000;
}
if (clickcount < 3) {
clickcount = 3;
} else if (clickcount > 1000) {
clickcount = 1000;
}
if (padding < 0) {
padding = 0;
} else if (padding > 100000) {
padding = 100000;
}
gain = -gain; // convert tempo gain into duration gain.
if (options.getBoolean("randgain")) {
if (lrand48() * 2.0/INT_MAX - 1.0 < 0) {
gain = -gain;
}
cout << "GAIN = " << -gain;
if (-gain < 0.0) {
cout << " (slower)";
} else if (-gain > 0.0) {
cout << " (faster)";
} else {
cout << " (constant)";
}
cout << endl;
}
gain = gain + 1.0;
Array<long> clicktimes;
clicktimes.setSize(clickcount);
clicktimes.setGrowth(0);
clicktimes.setAll(0);
getClickTimes(clicktimes, startdur, gain);
int i;
double clicksample;
SoundHeader header;
header.setHighMono();
SoundFileWrite outsound(outputname, header);
int clicki = 0;
int clickstatus = 0;
for (i=0; i<clicktimes.getSize(); i++) {
clicktimes[i] += padding;
}
int samplecount = clicktimes[clicktimes.getSize()-1] + CLICKWIDTH + padding;
for (i=0; i<samplecount; i++) {
if ((clicki < clickcount) && (clicktimes[clicki] == i)) {
clickstatus = 1;
clicki++;
}
if (clickstatus) {
clicksample = ((lrand48() * 2.0)/INT_MAX - 1.0) * amp;
clickstatus++;
if (clickstatus > CLICKWIDTH) {
clickstatus = 0;
}
} else {
clicksample = 0.0;
}
outsound.writeSampleDouble(clicksample);
}
return 0;
}
//////////////////////////////
//
// getClickTimes --
//
void getClickTimes(Array& clicktimes, double startdur, double gain) {
double duration = startdur * 44100.0 / 1000.0;
clicktimes[0] = 0;
int i;
for (i=1; i<clicktimes.getSize(); i++) {
clicktimes[i] = (int)(clicktimes[i-1] + duration + 0.5);
duration *= gain;
}
}
// md5sum: a51e61725e520c5d091efe382d3a575f tempochange.cpp [20060502]