@extends('template') @section('title') Lab 09: Part 2B: Recursive Drums @stop @section('content') # Lab 09: Part 2B: Recursive Drums ## Setup In the `lab09` 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/lab09/_toc') @stop