In this project, you will explore physical models and pattern generators. Your main composition task is to generate music using physical model functions built into Nyquist. One problem you will encounter immediately is that just as a violin does not play itself, physical models require careful control to create musical sounds. The warm-up exercises are designed to explore physical models and their control.
Using clarinet-freq
(accept no substitutes!), create a
sequence of clarinet tones separated by 1 s of silence as follows:
F2()
should play a 1-second tone
at pitch C4 with no vibrato and a breath (roughly the amplitude
control) envelope that sounds natural to you.F3()
should play a 2-second tone
at pitch D4 with breath envelope vibrato. (See a discussion below on
vibrato.) Design a vibrato that you think sounds natural. You will
find the clarinet model has a fairly sudden threshold below which
there is no oscillation and above which the tone is fairly
strong. If breath vibrato causes the breath envelope to cross this
threshold, the clarinet tone may pulse on and off. Control vibrato
so that this does not happen. (We hope it’s obvious
by this time in the course that if you need the breath envelope to
oscillate in a vibrato-like manner, you should add to your breath
envelope a low-frequency sine, e.g. with lfo
, scaled
appropriately, and perhaps multiplied by another envelope if you do
not want the full lfo
amplitude from the
beginning. Wikipedia has a fine discussion and sound examples if you
need to learn about vibrato, but note that here we are
“vibrating” the breath and not using frequency
vibrato.)F4()
should play a 5-second tone
at pitch E4 that has a slow crescendo. The sound should start with a
noisy breathy sounding attack that barely has any pitch. Within 1
second, the pitch should be clear. The crescendo should obviously
continue until at least 4 seconds. You will find the clarinet model is
very sensitive to the breath envelope and there is a very narrow range
of envelope values over which a crescendo takes place. You will find
that only a small amount of crescendo is possible with this model. You
will need to determine good envelope values experimentally. (See the
section below on RMS for more tools.)F5()
should play the 8-second
sequence (F4, G4, A4, rest, F4, G4, A4, rest) where elements have
IOI’s of 1 second. The first 3 “notes” should be
made with one call to clarinet-freq()
, using the
frequency control to change pitch. (Hint: to get from F4 to G4, the
frequency envelope should step up by step-to-hz(G4) -
step-to-hz(F4)
. Hint 2: For the best sound, the frequency
control transitions should take around 30ms rather than jumping
instantaneously from one pitch to the next.) The second 3 notes
should be separate calls to clarinet-freq(). Try to get a continuous
sound similar to the first 3 notes by slightly overlapping F4 to G4
to A4. (Hint: Overlapping is easy if you use a score or write
expressions where you can explicitly control start times and
durations as opposed to using the seq() construct. If you want to
use seq()
, you can still get overlap if you set the
logical stop time of sounds to be 1s but make the actual duration
longer. Look for “logical-stop” and
set-logical-stop
in the Nyquist Reference
Manual.)F6()
should run one instance of
score-gen
with pattern generators to create a score
named p6score
. The score should have 4 contiguous
sections. In each section, 20 pitches are generated by randomly
selecting a pitch (score event attribute name pitch:
)
from the scale C4, D4, E4, F4, G4, A4, B4. These 20 pitches are
all transposed by the same value, which is randomly selected from
-12, 0, or +12. E.g. the first 20 notes may be transposed down an
octave, the second group of 20 transposed up an octave, then 20
notes are not transposed, etc. All pitches should end up either in
the 3rd, 4th or 5th octave.
Each note should have a duration and ioi of 0.2
seconds, for a total duration of 0.2 * 20 * 4 = 16 seconds. Finally,
the piece should never generate 2 consecutive sections in the same
octave. (The :max 1
attribute can be used for each item
in make-random
to prevent a repeated selection. See
below for an example.) F6()
should not play
p6score
: You can use
exec score-play(p6score)
F7()
to do this with one mouse click in the
IDE. The sound should be incorporated into p6warmup.wav
as described in the next paragraph.Put your code (for all of these tones and sequences) in
p6warmup.sal
. Concatenate all the sounds with some
silence separating them in p6warmup.wav
.
Use score-gen
with pattern generators to
algorithmically compose a piece for physical models. You can use any
of the physical models in Nyquist: clarinet
,
sax
, flute
, bowed
,
mandolin
, wg-uniform-bar
,
wg-tuned-bar
, wg-glass-harm
,
wg-tibetan-bowl
, modal-bar
, and
sitar
. (Acknowledgment: these unit generators are ported
from Synthesis Tool Kit by Perry Cook and Gary Scavone.)
Your piece should have multiple pattern generators, including nested pattern generators to get variation at more than one time scale. For example, if you have a cycle of pitches, you could add an offset to the cycle on each repetition so that you hear the melodic cycle transposed each period. Alternatively, you might generate small random pitch intervals at the small time scale and have the average pitch slowly drift up or down at a larger time scale. You can use make-window and make-repeat to further manipulate streams of data.
You should also have at least two “voices” as opposed to a single melodic line. As always consider panning, reverberation, other effects, and careful mixing to make your piece musical and interesting.
Write a statement about the intention of your composition in
p6comp.txt
. In p6comp.txt
, also describe
how you used pattern generators to achieve your results and how you
used nested patterns. (Your p6comp.sal
should also be
commented to make your algorithms understandable.)
Duration should be between 45 and 60 seconds. Hand in the following files:
p6comp.sal
– the code.
p6comp.wav
– the sound file.
p6comp.txt
– a short statement of your intention in
the composition.
To get you started on some advanced pattern generation, here are some interesting examples to study:
make-copier(make-heap({24 36 48 60}, for: 1), repeat: 7)
make-cycle
instead of
make-heap
:
make-copier(make-cycle({24 36 48 60}, for: 1), repeat: 7)You might expect the sequence 24, 24, 24, 24, 24, 24, 24, 36, 36, 36, 36, 36, 36, 36, 48, 48, ... (i.e. 7 copies of each number in the cycle), but you actually get just 24, 24, 24, ... repeating forever. This is because at the end of every period (even a period of 1), the cycle is restarted. This could be useful if you provide a pattern to compute the elements of the cycle. In that case, the next period of the pattern becomes the next cycle. To cycle through our list and repeat each element 7 times, you can use
make-length
to re-group the periods
of make-cycle
into periods of length 1 to be copied 7 times:
make-copier(make-length(make-cycle({24 36 48 60}), 1), repeat: 7)
make-sum(long-term-pattern-generator, short-term-pattern-generator)
make-line({5 2})
pattern returns 5, 2, 2, 2,
…, and make-accumulate
sums the series to get 5,
7, 9, 11, …
make-accumulate(make-line({5 2}))
When all else fails and you really want a specific computation, you
can use
make-eval({function-name})
to invoke a function to compute a
stream of numbers. In this example, a custom function,
myfunc
, does some computation and returns a value to
incorporate into a pattern stream. mypat
uses
make-eval
to call myfunc
.
define myfunc() return real-random()
set mypat = make-eval({myfunc})
Of course, myfunc()
could also access and modify data
from another pattern. The simple way to do this is using a global
variable since make-eval does not have any way to accept parameters
and SAL cannot construct closures.
(.wav and .aif files are acceptable)
F2()
, F3()
, F4()
,
F5()
, F6()
Some might say clarinets should not use vibrato. Eddie Daniels has a nice discussion and demonstration of clarinet vibrato, including breath vibrato created mainly by variations in air pressure, and frequency or lip vibrato, which varies the fundamental frequency while keeping the amplitude and breath pressure more or less constant. See Vibrato On The Clarinet?! with Eddie Daniels.
The rms
function in Nyquist computes the “root mean
square” or average power in a signal. Technically, RMS
gives the answer to the following question: If instead of an
irregular, oscillating signal, I wanted to substitute a single,
constant amplitude value and obtain the same overall power, what
value would I use? RMS is a good way to estimate an amplitude
envelope from a signal.
The Nyquist rms
function takes three parameters: (1) the signal to analyze, and (2) the
analysis rate, (3) the window size. By default, the analysis rate is
100 hz, meaning that rms
computes an amplitude value for every 10ms
(1/100 s) of the signal, and the window size default is 10 ms,
meaning that each amplitude value corresponds to the average power
in that 10 ms region of the input signal.
In “warmup” exercise 3 (function F4
), you
are asked to make a crescendo. You can plot the resulting envelope
of the computed signal using something like:
plot rms(my-computed-signal)
The clarinet-freq
model
has a narrow range of amplitude variation (unlike a real clarinet),
so you might expect to see an RMS envelope like the following:
A very musical variation of random selection is random selection
where you never repeat anything from a list of selections. With
make-random
, you can give weights, control repetitions
and even force repetitions. See the manual for details. Here is an
example of selecting A, B, or C where there are no immediate
repetitions of A or B, but C is allowed to repeat:
set abd-pat = make-random({{A :max 1} {B :max 1} C}) loop repeat 20 exec prin1(next(abc-pat)) end