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)

  1. 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?
  2. Assuming a sample rate of 48000 Hz, what are the (approximate) spectral centroids of the following signals?
  1. Ideal White Noise (equal values for all frequency components)
  2. Synthesized harmonics:
    hzosc(335) + 0.75 * h1zosc(900)  - 0.25 * hzosc(1000) + hzosc(1800)
  1. 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

  1. 2 1 2 1 2 1
  2. 1 1 2 2 1 1
  3. 1 2 1 1 1 2
  4. 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:

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.


  1. Implement Example 3 in Nyquist. Include your code in ANDREWID-p4-answers.txt.
  2. 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
  1. Use an alphabet of size 3 or more (name them A, B, C, …)
  2. Use one or two parameters for the symbols, like A(pitch, duration)
  3. 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:

  1. Find some sounds to granulate. Often, non-music sources are the most interesting. Sometimes, music works well if it is highly stretched (see below).
  2. 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:

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:

  1. 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.)
  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.


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:

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.


Please hand in a zip file containing the following files (in the top level, not inside an extra directory):

You have a chance to get 20 bonus points this time!