Synthesis Theory I: Programming Lab 2
Command-Line Options
This lab demonstrates how to process command line arguments
in C++, so that your programs can be more useful for different
situations.
The program for generating noise
used in lab 1 will be used as a starting point
for this lab.
You may have noticed that in noise1.cpp,
the output filename was always test.wav. If you wanted to run
the program again, you had to rename the file or delete it, because the
program would not overwrite the file by itself. But why run the computer
a second time, anyway? It always outputs exactly one second of noise,
so the file test.wav will always has the same contents.
To make noise1.cpp a more useful
program, we will add command-line options to the program. This will
allow the program to produce a wider range of output. First, lets
plan on what would be useful parameters to be able to control
after the program has been compiled into a command. I would
suggest the following:
- be able to specify the name of the output soundfile.
- be able to control the duration of the output sound.
- be able to control the amplitude of the output sound.
For example, I would like to type the command:
noise -a 0.1 -d 5 noise.wav
To create a soundfile called noise.wav containing
5 seconds of whitenoise at an amplitude of 0.1.
The following program demonstrates a C++ object called
Options which can be used to manage the command-line
arguments to a program. If you want to copy and paste the
program to a file, here is the source code: noise2.cpp.
The primarily new section of the code is highlighted in red in the
following figure:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
#include "soundfile.h"
int main(int argc, char** argv) {
Options options;
options.define("a|amplitude=d:0.1", "amplitude of output sound");
options.define("d|duration=d:1.0", "duration of output sound (seconds)");
options.define("r|sample-rate=i:44100","set the sample rate of the output");
options.process(argc, argv);
if (options.getArgCount() != 1) {
cout << "Usage: " << options.getCommand()
<< " [-a amp][-d dur][-r srate] file.wav"
<< endl;
exit(1);
}
int sampleCount = (int)(44100 * options.getDouble("duration") + 0.5);
int channels = 1;
int srate = options.getInteger("sample-rate");
double amplitude = options.getDouble("amplitude");
const char* filename = options.getArg(1);
SoundHeader header;
header.setChannels(channels);
header.setSrate(srate);
SoundFileWrite soundfile(filename, header);
int i;
double sample = 0.0;
for (i=0; i<sampleCount * channels; i++) {
sample = drand48() * 2 - 1;
soundfile.writeSampleDouble(sample);
}
return 0;
}
|
The option variable reads the command line options
in three stages:
- Define the commands you want it to understand by using the
define() function.
- Read the parameters argc and argv coming
into the function main using the process()
function.
- Extract parameter values from the option variable
by using one of the four functions:
getInteger(),
getDouble(),
getString(), or
getBoolean().
To define a command, look at line 5 as an example:
options.define("a|amplitude=d:0.1", "amplitude of output sound");
This line defines a command line option called amplitude, or
a as an alias name for the amplitude option. There are
two string parameters given to the define function.
The first string:
a|amplitude=d:0.1
is the actual definition of the command-line option. This parameter
is composed of three main parts:
- the option name(s) which is a list of acceptable command line
names which can be used on the command line to mean the same
thing. This is a list of names separated by a pipe character
(|). No spaces or non-alphanumeric characters are
allowed in the option names.
- next follows an equal sign (=) and then the intended
data type for the option, which can be one of the following
letters:
- i an integer is expected for the option.
- d a floating-point number (a double) is expected
for the option.
- s a character string is expected for the option.
- b no data is expected for the option, but if the
option is used, then the program will know
that a flag option was set.
- after the data type character comes an optional colon
(:) followed by a default value for the option.
For the amplitude option, the default value is set to 0.1.
This means that if the amplitude option is not specified on the
command line, then the program will use an amplitude of 0.1
as a default.
The second
string in the define() function:
amplitude of output sound
is an optional parameter which is useful for remembering what the
option actually means and does not do anything important in the
program except help you remember what is going on when you revise
the program the following day.
Example access to the command-line options is given on line
19 of the noise2.cpp program:
double amplitude = options.getDouble("amplitude");
On this line of the program the variable amplitude is being
declared as a double (which is a floating-point number).
It is being defined as the value options.getDouble("amplitude").
In this case, the function getDouble will return the
value of the command-line option "amplitude" as it is
specified on the command-line or the default value of 0.1 given
in the definition of the amplitude option.
The output filename is extracted from the options with the
getArg() function as shown on line 20 of the example program
above. getArg(1) means return (as a string) the first
command line argument (other than the command name).
Try compiling the program noise2.cpp
on mambo, in the mambo terminal, you can copy the program by
copying and pasting this command:
wget http://peabody.sapp.org/class/350.867/lab/prog2/noise2.cpp
To compile the program, refer to the procedure in lab1. You will compile the program by typing the
command:
mkprog noise2
After the program is compiled, you can try out the new options added
to the noise program. Here are a few program calls to try:
- noise2 test1.wav
- This should create a WAVE file called test1.wav which
contains one second of noise at an amplitude of 0.1
- noise2
- This should display the usage statement given in the program on
lines 9-14.
- noise2 -a 1 test2.wav
- This should raise the amplitude of test2.wav to the maximum
possible without clipping the sound.
- noise2 --amplitude 1 test2.wav
- The --amplitude option is an alias for the -a
option. Notice that two dashes are used for multi-character
options according to the POSIX standards.
- noise2 -a1 test2.wav
- This should also work like the previous example.
- noise2 -d 15 test3.wav
- This should create a soundfile with 5 seconds of noise.
- noise2 --options
- This will display a list of all of the options defined in the
program. The hidden option is useful for finding out what the
lazy programmer forgot to document.
- noise2 test4.wav -d 5 -a 0.4
- This should create a file called test4.wav which is 5 seconds
long and has an amplitude of 0.4. Notice that the soundfile
name is first. The order and placement of the optional
arguments which start with a dash is not important.
Exercises
- create an option to control the number of channels (to switch
between mono and stereo output soundfiles).
- create a program which can do stereo noise where each
channel contains independent noise.
- create a program which can do stereo noise where each
channel contains the exact noise data (it should sound like
a mono file, but when using peak, the sound will come from
both speakers).
|