@extends('template')
@section('title')
Lab 11: Part 2B: Recursive Drums
@stop
@section('content')
# Lab 11: Part 2B: Recursive Drums
## Setup
In the `lab11` folder, open `recursiveDrums.py`.
At the bottom of the starter file is a test function; you can uncomment
things there to run them.
If you want Python to play sounds directly instead of just saving a
`.wav` file, you should use the "Manage packages" option in the Thonny
"Tools" menu to install the `simpleaudio` package.
For today, we'll just need a few functions from the `wavesynth` audio
library: `addBeat`, `addRest`, `rewind`, and `setTime`.
The `addBeat` function takes 1 argument: the duration of the beat. Durations are
always specified in seconds (whole or fractional).
The `addRest` function also takes one parameters specifying the duration.
Both functions add to the "current track," and that track can later be
printed, saved to a file, or played (the testing code does all three).
Within each track, there is a current time, which can be modified by calling
`rewind`/`fastforward` or `setTime`. By default adding a beat or rest changes
the current time to the end of that beat or rest.
**Warning:** for minimum chaos you should probably use headphones while
working on this part of the lab, if you have them, but the 'snare' sounds
we synthesize can be pretty loud, so you may want to **turn down your
volume** a bit now and then figure out what's comfortable for you. The
audio elements below should be set to 1/2 volume by default, you may also
need to turn things up to hear them well.
## Task 1. `drumBeats`
Partner A
We will start off simple: write a function called `drumBeats` which
accepts two parameters: a number of beats, and a duration. It should add
that many beats, each with that duration, to the current track, starting
at the given start time, with each beat directly following the previous
one. To make things interesting, it must use recursion, and **may not**
use a loop.
Here is what the result sounds like for `drumBeats(4, 0.2)`;
you can see the correct printed output below as well.
Show the output for
drumBeats(4, 0.2)
```txt
a 0.2s snare beat
and a 0.2s snare beat
and a 0.2s snare beat
and a 0.2s snare beat
```
And here is what `drumBeats(4, 0.2)` followed by
`drumBeats(8, 0.1)` sounds like:
## Task 2. `spacedBeats`
Partner B
Now write a second function, `spacedBeats`, which accepts the same
arguments as `drumBeats` and which does the same thing, except that it
adds a rest in between each pair of beats. The rests should be twice as
long as the beats.
To make things interesting, see if you can get it to add the rests only
between notes, and avoid adding a rest after the last note (of course,
you can't hear the difference, but you'll be able to see it in printed
output, and if another beat were added to the track, it would matter).
Here is what the result sounds like for
`spacedBeats(4, 0.2)`; you can see the correct printed
output below as well.
Show the output for
spacedBeats(4, 0.2)
```txt
a 0.2s snare beat
and a 0.4s rest
and a 0.2s snare beat
and a 0.4s rest
and a 0.2s snare beat
and a 0.4s rest
and a 0.2s snare beat
```
## Task 3. `decreasingBeats`
Partner A
`decreasingBeats` will take the same parameters as `drumBeats` and
`spacedBeats`, but it will make each beat that it adds shorter than the
previous beat. Specifically, while the first beat that it adds will have
the specified duration, subsequent beats will each be 3/4 of the duration
of the previous beat. Another way to look at that is to say that the
pattern begins with a single beat of the specified duration, and then
continues with a `decreasingBeats` sequence which itself begins with a
beat of 3/4 the original duration.
Here is what the result sounds like for
`decreasingBeats(8, 0.4)`; you can see the correct printed
output below as well.
Show the output for
decreasingBeats(8, 0.4)
```txt
a 0.4s snare beat
and a 0.3s snare beat
and a 0.225s snare beat
and a 0.169s snare beat
and a 0.127s snare beat
and a 0.0949s snare beat
and a 0.0712s snare beat
and a 0.0534s snare beat
```
## Task 4. `squeezeBeats`
Partner B
To make things interesting, write a `squeezeBeats` function which adds
longer beats at first and in the end, with shorter beats in between. This
function will accept two parameters: `duration` and `minDuration`. If the
given `duration` is less than the given `minDuration`, it will do
nothing. However, if the given duration is longer than the `minDuration`,
it will add two beats to the current track of the given duration, and in
between those beats, it will add a full `squeezeBeats` pattern with 3/4
of the original duration. So for example, if the initial `duration` and
`minDuration` are 1.6 and 0.8, the beats added will have durations 1.6,
1.2, 0.9, 0.9, 1.2, and 1.6, in that order (0.9 * 0.75 is less than 0.8,
so no further notes are added between the two 0.9 notes).
**Note:** This one uses a slightly different recursive pattern than the
previous tasks, and in particular, it's not really possible to use the
base case (the last recursive function call frame) to add the longest
beat at the end of the pattern: that beat should actually be added by the
very first function call frame (which is also the last to be cleaned up).
Here is what the result sounds like for `squeezeBeats(0.2, 0.05)`;
you can see the correct printed output below as well.
Show the output for
squeezeBeats(0.2, 0.05)
```txt
a 0.2s snare beat
and a 0.15s snare beat
and a 0.113s snare beat
and a 0.0844s snare beat
and a 0.0633s snare beat
and a 0.0633s snare beat
and a 0.0844s snare beat
and a 0.113s snare beat
and a 0.15s snare beat
and a 0.2s snare beat
```
{{-- TODO: This task is kinda too hard... --}}
## Task 5. `layeredBeats`
Partner A
What happens if we layer multiple drum beats on top of each other?
`layeredBeats` does just that, adding increasing numbers of beats within
the same time segment based on a `complexity` value. It takes two
arguments: `duration` determines the total duration within which multiple
beats are added, and `complexity` determines how many beats (and how many
layers) are used.
When complexity is 1, we just add a single beat that fills the whole duration,
and if complexity is less than one, nothing is added. When complexity is two,
in addition to the single beat for complexity = 1 (added via a recursive call),
we also add two beats, each of which is half of the given duration. Complexity
3 adds a further three beats at 1/3 duration, complexity four adds four beats
of 1/4 duration on top of that, and so on.
**Note:** In each recurive function call frame for `layeredBeats`, you'll
want to use your `drumBeats` function from above to add multiple beats at
once, and then use `rewind` to go back to the beginning of the time
period so that the next layer will overlap with the current one.
Here's a diagram showing how the beats are arranged for complexity 3:
three beats cover the whole duration, then two more that also cover the
same duration are layered on that, and finally a single beat also
covering the same duration is layered on both of those.
-----------
-----|-----
---|---|---
Here is what the result sounds like for
`layeredBeats(0.5, 2)`; you can see the correct printed
output below as well.
Show the output for
layeredBeats(0.5, 2)
```txt
a 0.25s snare beat
at 0.25s a 0.25s snare beat
at 0s a 0.5s snare beat
```
...and here is what the result sounds like for
`layeredBeats(0.5, 4)`:
Show the output for
layeredBeats(0.5, 4)
```txt
a 0.125s snare beat
and a 0.125s snare beat
and a 0.125s snare beat
at 0.375s a 0.125s snare beat
at 0s a 0.167s snare beat
at 0.167s a 0.167s snare beat
at 0.333s a 0.167s snare beat
at 0s a 0.25s snare beat
at 0.25s a 0.25s snare beat
at 0s a 0.5s snare beat
```
{{-- Too complicated with start times!
## Task 5. `triangleBeats`
For your last task, write a function called `triangleBeats` which takes
three parameters: a `startTime` time, a `duration` value and a
`complexity` integer. This function will produce a recursive pattern of
beats with a long beat in the middle and shorter beats on either side
(but those shorter beat sequences will themselves have their longest beat
in the middle of each sequence).
If the given `complexity` value is 0 or negative, nothing is added to the
current track. On the other hand, if it is positive, a single beat of the
given duration is added between two sub-patterns which have 3/4 of the
original duration and one less complexity. Accordingly:
- If the complexity is 1, just a single beat is added (both sub-patterns
have complexity 0 and thus add nothing).
- If the complexity is 2, a single beat is added between two shorter
beats (the complexity-1 patterns).
- If the complexity is 3, two 3-beat sub-patterns (of complexity 2)
surround a single beat with the given duration, for total of 7 notes.
So as a specific example, if the duration is 1.6 and the complexity is 3,
beats with the following durations will be added, in this order:
0.9, 1.2, 0.9, 1.6, 0.9, 1.2, 0.9
Meanwhile, if the duration is 1.2 and the complexity is 2, we get this
pattern:
0.9, 1.2, 0.9
...which you can see is repeated twice as part of the complexity-3
pattern.
Here is what the result sounds like for `triangleBeats(0, 0.16, 3)`;
you can see the correct printed output below as well.
Show the output for
triangleBeats(0, 0.16, 3)
```txt
a 0.09s snare beat
and a 0.12s snare beat
and a 0.09s snare beat
and a 0.16s snare beat
and a 0.09s snare beat
and a 0.12s snare beat
and a 0.09s snare beat
```
Additionally, here is what the result sounds like for
`triangleBeats(0, 0.4, 5)`, with printed output below.
Show the output for
triangleBeats(0, 0.4, 5)
```txt
a 0.127s snare beat
and a 0.169s snare beat
and a 0.127s snare beat
and a 0.225s snare beat
and a 0.127s snare beat
and a 0.169s snare beat
and a 0.127s snare beat
and a 0.3s snare beat
and a 0.127s snare beat
and a 0.169s snare beat
and a 0.127s snare beat
and a 0.225s snare beat
and a 0.127s snare beat
and a 0.169s snare beat
and a 0.127s snare beat
and a 0.4s snare beat
and a 0.127s snare beat
and a 0.169s snare beat
and a 0.127s snare beat
and a 0.225s snare beat
and a 0.127s snare beat
and a 0.169s snare beat
and a 0.127s snare beat
and a 0.3s snare beat
and a 0.127s snare beat
and a 0.169s snare beat
and a 0.127s snare beat
and a 0.225s snare beat
and a 0.127s snare beat
and a 0.169s snare beat
and a 0.127s snare beat
```
--}}
@include('/labs/lab11_recursion/_toc')
@stop