We’ll first use a recursive algorithm to generate a one-dimensional fractal noise signal. This kind of noise can create patterns that are more similar to natural phenomena than noise consisting of uniformly-distributed random numbers (i.e. white noise). We will stream this noise to the audio output of your computer to synthesize the sound of ocean waves crashing on the shore.
2 You can use random.gauss() or
random.normalvariate() to generate
such a random value, providing the
mean and standard deviation (square
root of variance) as arguments.
Problem 1: Brownian Bridge
A Brownian bridge is a stochastic process (think of this as a random
function over time) that models a random walk connecting two
points. In our case, the bridge is a function of time, and if we fix
its value at an initial and a final time point, b(to) and b(t1), we can
compute the in-between values with the following recursive process:
1. Compute the midpoint of the interval, ym = { (b(to) +b(t1)).
2. Add to the midpoint value a random value 8, drawn from a nor-
mal distribution with zero mean and given variance, and assign
the sum to the bridge function: b(tm) + Ym+8.
3. Recur on the subintervals (to, tm) and (tmeti), dividing the vari-
ance for the next level by a given scale factor.
The shape of the function is controlled by two parameters: the
volatility and the Hurst exponent. Volatility is the initial value of the
variance, and controls how far the random walk strays from the
straight line connecting the fixed endpoints. The Hurst exponent, H,
controls the smoothness of the result. The variance of the distribution
from which the random value 8 is drawn is divided by 22H at each
recursive level. When H = 0.5, the variance is halved after each recur-
sion, and the result is a true Brownian bridge that models Brownian
motion between the endpoints.
Write a recursive Python function to fill an array with numerical
values that form a Brownian bridge between two specified endpoints.
The function should take a reference to an array, a first and last in-
dex, a variance, and a scale factor as parameters. These would allow
the function to fill the array according to the recurrence described.
For example, your function may have a definition that looks like this:
Note that Program 2.3.5 in the course
text uses a recursive function to plot a
Brownian bridge on the screen. Rather
than drawing a graph, this problem
requires you to fill an array with values
of the function. Studying that example
would certainly be a good idea, and
you may complete this problem by
starting from the textbook example and
modifying it if you like.
def fillbrownian(a, io, il, variance, scale):
Recursive function that fills an array with values forming a
Brownian bridge between indices im and il (exclusive).
Assumes that a[io] and aſil) are values of the fixed endpoints.
:param a: Array to fill with Brownian bridge.
:param io : Index of first endpoint.
:param il:
Index of second endpoint
:param variance: Variance of the normal distribution from
which to sample a displacement.
:param scale:
Scale factor to reduce variance for next level.
When you’ve completed the function, write a program to test it
out. Call your recursive function to fill an array with 129 elements,
the first and last of which are anchored at a value of o.o. Use a
volatility of 0.05 and allow the Hurst exponent to be specified as a
command line argument. Then visualize the contents of your array
by drawing a graphical line plot of the values to the screen. You may
either write your own code, use some of the code we wrote in class
or that’s found in the course text, or use the stdstats module from
the booksite library to draw the plot.
Levy
Inputs: The Hurst exponent for your Brownian bridge function, spec-
ified as a command line argument. For example, you might run
your program with the following invocation:
$ python3 my.pl.py 0.5
Outputs: A line plot of your randomly-generated Brownian bridge
between the two zero endpoints. It should resemble those shown
on the right when run with the corresponding Hurst exponent.
Figure 1: Line plots of 129 values of a
Brownian bridge with Hurst exponents
of 1.0, 0.5, and 0.2, respectively from
top to bottom.
Problem 2: Ocean Waves
3 You can use the booksite library
function stdaudio.playSamples(a) to
stream a sequence of samples in the
array a to your computer’s default
audio output device. The values in a
should be floats between -1.0 and +1.0.
If you stream the Brownian bridge function values to your com-
puter’s audio output, the result sounds like a pleasant, rumbling
kind of noise. In fact, if you generate the values with a Hurst expo-
nent of 0.5, the result is what audio engineers call “brown noise” or
“red noise”. This is distinct from “pink noise” and the higher-pitched
and more commonly found “white noise”, which is simply a stream
of uniformly distributed random numbers.
Both kinds of noise are useful, and we can actually synthesize a
half-decent sound of ocean waves crashing on the shore by oscillating
between brown noise and white noise at a very low frequency. If
no(t) represents the sequence of brown noise values over time, and
nu(t) represents white noise, we can achieve the ocean wave effect by
blending them with a time-varying blend factor, s(t), as follows:
s(t) = (1-s(t)) n (f)+s(t) nu(t),
with s(t) = sin(aft).
In this equation, t represents the time (in seconds), and f is the fre-
quency at which the ocean waves are crashing on the shore.
Write a program that generates 20 seconds of crashing ocean
waves using the method described above, then plays the sound
through the computer’s audio output. If you use the stdaudio mod-
ule, the output sample rate is fixed to 44 100 Hz (samples per sec-
ond), which means you will need to generate a total of 20 x 44 100 =
882000 data values. Use your Brownian bridge function from Prob-
lem 1 to generate values for no(t). You can adjust any parameters
within these equations that you’d like in order to synthesize a more
realistic or more pleasant ocean sound, but perhaps we can suggest
that you start with the following:
• Use a Hurst exponent of H = 0.5. Increase it slightly if you want
lower-frequency noise, and decrease it for higher-frequency noise.
• A frequency of f = 0.25 will give you a wave crash every four
seconds. Adjust as you like.
• The exponent of 6 on the blend factor, s(t), controls the balance of
the two noise types during each cycle. Increase the exponent for
quicker wave crashes, but use even exponents to keep the blend
factor non-negative.
• An amplitude of about 0.25 works well for the white noise compo-
nent. In other words, generate random values between -0.25 and
+0.25 for nyt). Adjust as you like as well.
You may also want to introduce one more slowly-oscillating function
to modulate the amplitude (volume) of your output sound to achieve
the ultimate ocean wave simulator, but that’s purely optional.
+ You will want to re-anchor your
bridge to a value of o.o several times
a second so that the values don’t
drift too far from the neutral zero-
point. . In other words, rather than
generating all 882000 samples with
a single function call, you should
generate a few thousand samples at a
time, setting the two endpoints to 0.0
each time.
Inputs: None.
Outputs: The sound of synthetic ocean waves crashing to sooth and
relax you for 20 seconds when you run your program.