Project 4 (Due Oct-24 11:59pm)
Download all related code and media here.
Short Questions
As always, show your work where applicable. Please include your answers for this section in ANDREWID-p4-answers.txt.
1. DFT and Spectral Centroid (20 points)
- Suppose you want to perform discrete spectral analysis. You found in a book that people often use a window size (FFT-size) of 50ms. What's the bin-size of the spectrum in this case?
- Assuming a sample rate of 48000 Hz, what are the (approximate) spectral centroids of the following signals?
- Ideal White Noise (equal values for all frequency components)
- Synthesized harmonics:
hzosc(335) + 0.75 * h1zosc(900) - 0.25 * hzosc(1000) + hzosc(1800)
- Considering time and frequency resolution, what's the problem with using a very large FFT window?
2. Algorithmic Composition (10 points)
Match each of the score-gen functions with ALL POSSIBLE note sequences (can be more than one).
1. make-cycle({1 2 1 1})
2. make-heap({1 2})
3. make-palindrome({1 1 2})
4. make-random({1 2})
5. make-accumulate(make-random({-1 1})
6. make-window(make-cycle({1 2 1}), 3, 2)
7. make-copier(make-heap({1 1 1 2}), repeat: 2)
Note sequences are
- 2 1 2 1 2 1
- 1 1 2 2 1 1
- 1 2 1 1 1 2
- 1 2 1 2 1 2
Write your answers in compact style: "1. ABCD 2.BCD 3.CD 4.D ..."
3 Recursive Sound Sequences with L-system (10 + 10 bonus)
The Lindenmayer system, or L-system, is a parallel rewriting system used in simulating plant growth and fractal patterns. In this question, we are going to show that recursive functions can act as a context free generator of the L-system, and can be used to create fractal music. (You can find helpful information on wikipedia.)
~~
Short Introduction to L-system
An L-system is defined as L = (V, s, P) where V is the alphabet of symbols, s is the starting string and P is a set of rules in the form v → a (v ∈ V, and α is a string made from the vocabulary V). For example,
Example 1- Alphabet V = {a, b}
- Starting-string: s = a
- Rules: (a → baa, b → bab)
|
The production of an L-system is obtained by replacing left-side characters by the corresponding right-side strings one at a time until reaching the maximal iteration n. Suppose n=3. in the above example, we start from the starting string “a” and replace it with baa as indicated in rule a → baa to get baa; then replace all the a with baa and b with bab to get babbaabaa:
- (starting) a
- (iteration 1) baa
- (iteration 2) babbaabaa
- (iteration 3) babbaababbabbaabaababbaabaa
An L-system can be used to generate visual patterns (and in this question, we are going to apply it to algorithmic composition). After defining the meaning of each character (e.g. a for drawing a line forward and b for turning 15 degrees clockwise) and maximal iteration, we can generate complicated patterns like the one below.
| Example 2- V = {E, +, -, [, ]} where E is “go forward while drawing 10 px line”, + is “turn clockwise 10 degrees”, - is “turn counterclockwise 10 degrees”, [ is “save the current location”, and ] is “retrieve the last saved location”.
- S = “E”
- Rules = (E → E[+E][-E])
- Maximal iteration is 14
|
Implementing L-system in Nyquist
Here is an example of how to define an L-system into Nyquist code. Recall the L-system defined in the introduction: (a → baa, b → bab); in the following code
define function funcA(level)
if level = 5 then return noise() * pwl(0.01,0.8,0.1)
else return seq(funcB(level + 1), funcA(level + 1), funcA(level + 1))
define function funcB(level)
if level = 5 then return osc(c4, 0.1) * pwl(0.01,0.8,0.09,0.6, 0.1)
else return seq(funcB(level + 1), funcA(level + 1), funcB(level + 1))
play const(1, 5) * funcA(1)
The symbol a is defined as a function funcA; and the symbol b is defined as funcB. The realization of the symbol is in the statement: "if level = 5 then …" which returns the desired sound clip when the maximal iteration is reached. The following statement "else return … " implements the rules. Now we can do something even more interesting. We can add parameters like pitch and duration into all these symbols, for example:
Example 3- Alphabet V = {a(pitch), b(pitch)}
- Starting-string: s = a(pitch: 60)
- Rules:
a(pitch: n) → b(pitch: n – 2) a(pitch: n) a(pitch: n + 2), b(pitch: n) → b(pitch: n + 3) a(pitch: n - 3) b(pitch: n) )
|
This system results in the sound file lsys.wav, included in this package. This algorithmic composition technique is highly useful; it gives you another way to do pattern generation based on fractals.
Questions
- Implement Example 3 in Nyquist. Include your code in ANDREWID-p4-answers.txt.
- Bonus Question: Create your own L-system formulas and make a 30 second work! Your work should meet the following standards to get the bonus points
- Use an alphabet of size 3 or more (name them A, B, C, …)
- Use one or two parameters for the symbols, like A(pitch, duration)
- You may need to use n = 5 or more to meet the 30-second requirement
If you choose to do this part, include a description of your L-system in ANDREWID-p4-answers.txt, put your Nyquist code into ANDREWID-p4-bonus.sal, and include the resulting sound file as ANDREWID-p4-bonus.wav.
Composition Tasks (60 + 10 bonus)
Timed-seq Bug Fix
Nyquist has a problem with extremely dense sequences. When you have thousands of notes in a sequence and they are spaced less than 1020 samples apart (about 23 ms at 44100 Hz sample rates), Nyquist can overflow its stack. The latest implementation works around this problem. The work-around defines score-split, so you can test for the "good" code with the command:
print fboundp(quote(score-split))
If does not print #t, then you should probably patch Nyquist by replacing nyquist/runtime/seq.lsp with the new seq.lsp included in this package. Alternatively, you can just load this new seq.lsp. To make sure you don’t just reload the original version (which is on the path, so it’s easy to reload), rename the new seq.lsp to newseq.lsp and
load “newseq.lsp”
to get it.
Composition Task
In this assignment you will be experimenting with granular synthesis. Granular synthesis is a method of sound synthesis based on summing thousands of small sounds called “grains”. By manipulating and layering those grains on top of each other, you can create rich textures and interesting sounds.
Grains can be synthesized or grains can be extracted from sound files. For this project, you can decide how you will produce grains. (We encourage you to try both, and you can submit two compositions, one with synthesized and one with sampled grains, for extra credit.) The two approaches are described in the following subsections.
1. Grains from Sound Files
We have provided you with the granular.sal file in this package. This file will help you get started. See the .sal file for documentation on how it works.
To get started:
- Find some sounds to granulate. Often, non-music sources are the most interesting. Sometimes, music works well if it is highly stretched (see below).
- Try it out: Run make-granulate-score to make a score, and use timed-seq or score-play to play it.
Much of the fun of granular synthesis comes from the effects that are applied to the grains:
- Changing Pitch: Rather than playing the grains back at the original speed, try resampling them. Find the following line in grain():
;; set grain = sound(grain) ~~ 1.5 ; see project description
Recall that sounds are immutable, so how do you play a sound faster or slower? Answer: the sound behavior makes a sound stretchable and shiftable. Grain is already in an environment with a stretch factor and the stretch factor has already been applied to compute grain, so if we just wrote sound(grain) or sound(grain)~1.5, the current environment stretch factor would be applied again – not good. Instead, we use “absolute stretch” (~~) to replace the stretch factor in the environment with an absolute value (1.5 in this case) while evaluating stretch(). The result is that the grain is resampled to make it longer or shorter, depending on the value of the stretch factor. Uncomment the line to get the effect, and replace 1.5 with larger and smaller values. Predict what a value less than 1 will do and test your hypothesis. (You do not have to turn in these results.)
- Dynamic Pitch: Now rather than resampling by a constant value we will resample by a dynamic amount. Create an argument for grain that will take in a resample rate. For each grain, choose a resampling parameter from the set {0.7, 0.8, 0.9, 1}. You can choose these randomly, or if you’re adventurous, use pattern generators to generate interesting sequences of resampling parameters. Save your code to comp/ANDREWID-p4-sample-pitch.sal and the output sound to comp/ANDREWID-p4-sample-pitch.wav.
- Dynamic Amplitude: Now create an argument that changes the amplitude of the grain. Pass an amplitude argument into grain to vary the amplitude of resulting sound. Create a sound file where your grains are crescendoing (getting louder). If you are adventurous, you can mix in some amplitude randomness or some amplitude variation generated by pattern objects, but your result should have a clear trend of increasing amplitude. Make sure your sound does not clip. If it does, read about clipping and auto-norm, and fix the problem. Save your code to comp/ANDREWID-p4-sample-cresc.sal and the output sound to comp/ANDREWID-p4-sample-cresc.wav.
- Play with Stretch: The stretch parameter to make-granulate-score causes the scan through the source sound file to go faster or slower. You can achieve a kind of stretching or shrinking with this parameter. Try stretch: 2 with your sound.
2. Synthesized Grains
Just as in the “Grains from Sound Files” described above, we’ll use (very long) scores to make synthesized grains.
To get started:
- Make a grain function. A good one is simply a sine tone multiplied by the raised-cosine envelope provided in granular.sal. You might want to use a table to add harmonics. (Similar to project 2.)
- Make a function to create a score. Use score-gen. Initially, try score duration (the score-dur: parameter) = 5, grain duration (dur:) = 0.05 and ioi (ioi:) = 0.025 * rrandom(). The purpose of the rrandom() is that if ioi is deterministic, and there’s no other randomization, you will end up with a signal the repeats with a period of ioi. Since ioi is 0.025, the repetition rate is 1/0.025 = 40 Hz, but 40 Hz is audible! Effectively, this produces a 40 Hz tone. By randomizing the ioi, you get a complex aperiodic signal and the strong 40 Hz tone is avoided.
Much of the fun of granular synthesis comes from the effects that are applied to the grains.
- Changing Pitch: Give your grain function a frequency (hz) parameter. (If you use osc(), you can compute the pitch parameter using hz-to-step(hz).) Add a parameter for hz in the parameters to score-gen. Try different frequencies. (You do not have to turn in these results.)
- Dynamic Pitch: Now rather than specifying a constant value, we compute frequencies. For each grain, choose a Hz value from the set {700, 800, 900, 1000}. You can choose these randomly, or if you’re adventurous, use pattern generators to generate interesting sequences of Hz parameters. Save your code to comp/ANDREWID-p4-synth-pitch.sal and the output sound to comp/ANDREWID-p4-synth-pitch.wav.
- Dynamic Amplitude: Now create an argument that changes the amplitude of the grain. Pass an amplitude argument into grain to vary the amplitude of the resulting sound. Create a sound file where your grains are crescendoing (getting louder). If you are adventurous, you can mix in some amplitude randomness or some amplitude variation generated by pattern objects, but your result should have a clear trend of increasing amplitude. Make sure your sound does not clip. If it does, read about clipping and auto-norm, and fix the problem. Save your code to comp/ANDREWID-p4-synth-cresc.sal and the output sound to comp/ANDREWID-p4-synth-cresc.wav.
Composition
Now using all the tools that you have built, create a 30 second composition. (Remember you can do just “Grains from Sound Files” or just “Synthesized Grains” or for extra credit do both.) In your piece you should at least include:
- Substantial use of granular synthesis, with
- Dynamic amplitude, and
- Dynamic pitch control
You should also experiment with other parameters. In fact, ALL parameters should be considered fair game for experimentation. Shorter and longer grains have a very different sound. Very high stretch factors, very small ioi, and other changes can be very interesting, so please spend some time to find amazing sounds.
Save the Nyquist code for your composition as comp/ANDREWID-p4-comp.sal, and the output sound as comp/ANDREWID-p4-comp.wav. Describe in comp/ANDREWID-p4-comp.txt how you vary the amplitude and pitch. Also include any additional effects that you added (for bonus points!). Lastly include your motivation or inspiration behind your composition.
Hand-in
Please hand in a zip file containing the following files (in the top level, not inside an extra directory):
- Answers for short-answer questions 1, 2, and 3: ANDREWID-p4-answers.txt
- If you choose to do the bonus part in question 3:
- Description of your own L-system: add to ANDREWID-p4-answers.txt
- Code: ANDREWID-p4-bonus.sal
- Sound file: ANDREWID-p4-bonus.wav
- Composition: all of these files should be in the comp/ folder.
- Short description: ANDREWID-p4-comp.txt (please make a clear list of what additional effects you added)
- Files demonstrating sampled grains (if you use this approach): ANDREWID-p4-sample-pitch.sal, ANDREWID-p4-sample-pitch.wav, ANDREWID-p4-sample-cresc.sal, ANDREWID-p4-sample-cresc.wav
- Files demonstrating synthesized grains (if you use this approach): ANDREWID-p4-synth-pitch.sal, ANDREWID-p4-synth-pitch.wav, ANDREWID-p4-synth-cresc.sal, ANDREWID-p4-synth-cresc.wav
- Composition code: ANDREWID-p4-comp.sal
- Any external source files you used in your composition: in the origin/ folder
- Composition sound file: ANDREWID-p4-comp.wav
You have a chance to get 20 bonus points this time!