Digital Music Programming II: whitenoise~
This lab demonstrates how send audio signals out of an MSP object
as well as generate whitenoise.
Whitenoise by definition contains all frequencies at a constant
amplitude. To generate whitenoise is very simple in theory: each sample of
whitenoise is a uniformly random value in the range from [-1.0 .. 1.0).
The actual creation of the whitenoise is done in the program
below with the following line of code:
output[i] = randfloat() * 2.0 - 1.0;
where randfloat() returns a random number between 0.0 and
almost up to (but not quite) 1.0. The random number between
0 and 1 is multiplied by 2 to get it in a range from 0 to 2, then 1
is subtracted from the number in the range from 0 to 2 to generate
a number in the range from -1 to 1 which is the correct range for
audio output in Max/MSP.
To get a random number in C which is in the range from [0.0 to
1.0), you have to do this:
(float)rand()/RAND_MAX;
Where rand() generates a random integer from 0 up to some
number, RAND_MAX (or RAND_MAX-1 perhaps). To get a random number
between 0.0 and 1.0, divide by RAND_MAX. You must be careful not
to use integer division by casting the rand() output to
a float before dividing by RAND_MAX; otherwise, you will end up with
always getting the value of 0.0 as a result due to integer division:
rand()/RAND_MAX; // always 0
RAND_MAX is defined to be the highest number the rand()
function can output. This value is either the largest 16-bit signed
number or the largest 32-bit signed number. It depends on what
compiler you are using.
The rand() function generates a pseudo-random sequence
of numbers. This sequence is not actually random, and can be
generated exactly given the same starting condition. A seed
can be used to control the starting condition. The srand()
function can be used to set the starting condtion for the
rand() function. srand() takes a long integer as
input an starts the sequence of random numbers based on that starting
value.
The function time(NULL) returns the current time in
seconds since Jan 1, 1970, (0:00 UTC). So every time you start the
Max/MSP program, the whitenoise will be based on a completely
different pseudo-random sequence unless you seed it explictly with
a different value on input 1 to the object.
On a POSIX, computer, I would recommend using the
drand48() function in place of (float)rand()/RAND_MAX
because it is much more closer to a uniform distribution of random
numbers:
float randfloat(void) {
return (float)drand48();
}
drand48() generates a random double float in the range from
0.0 to 1.0. You would use srand48() to seed the
drand48() function just like the srand() function.
#include "ext.h"
#include "z_dsp.h"
#include <cstdlib> /* included for rand() function */
#include <ctime> /* included for time() function */
typedef struct {
t_pxobject msp_data;
} MyObject;
void* object_data;
void main (void);
void* create_object (void);
void InputSeed (MyObject* mo, long value);
void MessageDSP (MyObject* mo, t_signal** signal, short* count);
t_int* Perform (t_int *parameters);
float randfloat (void);
void main(void) {
setup((t_messlist**)&object_data, (method)create_object,
(method)dsp_free, (short)sizeof(MyObject), NULL, A_NOTHING);
addint((method)InputSeed);
addmess((method)MessageDSP, "dsp", A_CANT, A_NOTHING);
dsp_initclass();
}
void* create_object(void) {
MyObject *mo = (MyObject*)newobject(object_data);
dsp_setup((t_pxobject*)mo, 0);
outlet_new((t_pxobject*)mo, "signal");
InputSeed(mo, time(NULL));
return mo;
}
void InputSeed(MyObject *mo, long value) {
#pragma unused(mo)
srand(value);
}
void MessageDSP(MyObject* mo, t_signal** signal, short* count) {
#pragma unused(mo)
#pragma unused(count)
dsp_add(Perform, 3, 3, signal[0]->s_vec, signal[0]->s_n);
}
t_int* Perform(t_int *parameters) {
long pcount = (long) (parameters[1]);
t_float *output = (t_float*) (parameters[2]);
long count = (int) (parameters[3]);
long i;
for (i=0; i<count; i++) {
output[i] = randfloat() * 2.0 - 1.0;
}
return parameters+pcount+1;
}
float randfloat(void) {
return (float)rand()/RAND_MAX;
}
|
Exercises
- Compile and listen to the whitenoise~ object.
- Increase the amplitude of the whitenoise object by using the
*~ object. As you decrease the volume of the whitenoise to 0,
how does the sound qualilty change? As you increase the volume of the
whitenoise to 100 (or whatever), how does the sound quality change?
What is happening in the second case?
- Make your own noise generate that generate noise other than
whitenoise. Try another distribution besides uniform distribution
to generate the noise. For example, try the following triangular
distribution:
Three control parameters:
- lower limit (use -1.0, for example)
- upper limit (use +1.0, for example)
- maximum (choose 0.0 for minimizing DC component, for example)
For the simple case with -1 as the lower limit, 0 as the max and, +1 as
the upperlimit, you can use this function in place of the
floatrand() function in the program above.
float trianglerand(void) {
return floatrand() + floatrand() / 2.0;
}
Here is a function to generate a triangular distribution in the
generalized case where trianglerand() would replace the code
floatrand() * 2.0 - 1.0:
float trianglerand(float lower, float upper, float max) {
float norm = floatrand()+floatrand();
float output;
// scale each side of the triangle to the requested range:
if (norm < 1.0) {
output = (norm - 1.0) * lower + max;
} else {
output = (norm - 1.0) * upper + max;
}
// normalize the sides of the triangle so that the probability
// densities matchup at the maximum:
if (lower == upper) {
return output;
} else if (output < max && lower < upper) {
if (floatrand() < (float)lower/upper) {
return output;
} else {
return trianglerand();
}
} else if (output > max && upper < lower) {
if (floatrand() < (float)upper/lower) {
return output;
} else {
return trianglerand();
}
}
return output;
}
|