Professional Documents
Culture Documents
Welcome to 15-210!
In this very first (zeroth?) lab, you will use the 210 Sequence library to manipulate discrete-time signals,
which are represented as sequences of real numbers. Signals can be used to represent many things, but
we will be using them to manipulate audio.
Files
2.1
Extracting Files
After downloading the assignment tarball from Autolab, extract the files by running:
written.pdf
which contains the answers to the written parts of the assignment.
3
3.1
Submission
Autolab
SignalLab
are visible immediately after you submit. The results of the private tests will not be visible until after the
due date.
If you submit multiple times, we will consider only your most recent submission.
3.2
Submission Instructions
Every labs handout directory includes a Makefile, which executes the submit script in support/.
This script automatically packages up only the files marked with a * in section ??, as well as your
written.pdf, and submits it to Autolab.
To submit your assignment to Autolab, open a terminal, cd to the signallab folder, and run:
make
Alternatively, run make package, open the Autolab webpage and submit the handin.tgz file via the
Submit File link.
After submitting, it is your responsibility to double check your submission on Autolab.
SignalLab
Introduction to Signals
Abstractly, a signal is just a function which holds information about some measurable quantity. In this
lab, we will focus exclusively on audio signals and control signals:
An audio signal is an electrical voltage which varies over time. This voltage can be used to drive a
speaker, producing a sound.
A control signal is simply a parameter which varies over time. For example, we might want to
control the frequency of an oscillator or the amplitude of an output signal.
Signals are normally analog, meaning that they vary continously with time. In order to manipulate
continuous-time signals with computers, we must convert them into a digital representation. This is
accomplished via sampling: measuring the value of the signal at discrete time intervals.
For example, here is a basic, continuous function: a sine wave with a period of 1 second. Next to it is
the same function sampled at a rate of 20 Hz.
It may seem that sampling results in loss of information. Amazingly, however, the original signal can be perfectly reconstructed from its sampled form under certain conditions. For more information, see http://en.wikipedia.org/wiki/
Sampling_(signal_processing) and http://en.wikipedia.org/wiki/Nyquist-Shannon_sampling_theorem.
SignalLab
The Basics
Well begin by implementing a few basic signal manipulations. As mentioned above, type signal =
real seq.
You might find some of the following functions from the Real structure useful: fromInt, round,
max, min, abs, sign, etc. See more at http://sml-family.org/Basis/real.html.
You should refer to http://www.cs.cmu.edu/~15210/docs/sig/SEQUENCE.html for documentation on the SEQUENCE interface, and http://www.cs.cmu.edu/~15210/docs/cost/ArraySequence.
html for cost bounds associated with the ArraySequence implementation.
Task 5.1 (4%). In MkSignalBasics.sml, implement the function
val const : int real signal
where (const n x) evaluates to x : 0 i < n . For full credit, your implementation should have O(n)
work and O(1) span.
Task 5.2 (4%). In MkSignalBasics.sml, implement the function
val ramp : int real signal
where ramp n x evaluates to inx : 0 i < n . For full credit, your implementation should have O(n)
work and O(1) span.
Task 5.3 (4%). In MkSignalBasics.sml, implement the function
val normalize : signal signal
D
E
where (normalize S) evaluates to maxx |S | : x S . For full credit, your implementation should have
0i<|S|
where (S ++ T ) and (S ** T ) evaluate to Si0 + Ti0 : 0 i < n and Si0 Ti0 : 0 i < n , respectively,
and n = max(|S|, |T |). Since S and T might be different lengths, let
(
Si , if 0 i < |S|
0
Si =
0, otherwise
Of course, we define Ti0 similarly for T . For full credit, both implementations should have O(|S| + |T |)
work and O(1) span. There should be a lot of code reuse here.
Note that both ++ and ** are declared as infix operators near the top of MkSignalBasics.sml.
The notation | | is abused here. For sequences, it means length. For numbers, it means absolute value.
SignalLab
When implementing the following functions, you may (and should) use functions from the previous
section.
Task 6.1 (18%). In MkSignal.sml, implement the function
val pwl : int (int * real) seq signal
where pwl n (x i , yi ) : 0 i < m evaluates to a piece-wise linear function which is sampled at x =
0, 1, . . . , n 1. This function has initial point (0, 0), final point (n, 0), and interior points as specified by
the pairs (x 0 , y0 ), . . . , (x m1 , ym1 ). Assume that x i < x i+1 for each adjacent pair of points, and that
every x i satisfies 0 x i n (this means that new initial and final points might appear in the input
see the second example below). For full credit, your implementation must have O(n) work and O(log m)
span.
Hint: (const k a ++ ramp k b) evaluates to the line segment between points (0, a) and (k, a+ b).
Examples:
pwl 10
pwl 5
(2, 1.0), (6, 5.0), (8, 1.0) = 0.0, 0.5, 1.0, 0.5, 2.0, 3.5, 5.0, 3.0, 1.0, 0.5
(0, 10.0), (2, 10.0), (5, 1.0) = 10.0, 10.0, 10.0, 7.0, 4.0
SignalLab
Task 6.2 (14%). In MkSignal.sml, implement the function
val osc : real seq signal signal
where we define
W i = nth W ((Real.round i ) mod |W |)
and (osc W F ) evaluates to
+
*
i1
X
F j |W |
W
: 0 i < |F |
44100
j=0
For full credit, your implementation must have O(|F |) work and span.
(osc W F ) is a frequency-modulating oscillator whose frequency varies with the control signal F .
For example, if F = ramp n 261.63, then the output will be an audio signal which rises in pitch from 0
n
Hz to 261.63 Hz (approximately the frequency of middle C) over the course of 44100
seconds.
W is a lookup table for one period of a waveform. The length of W only determines the resolution
of the lookup table, and consequently the amount of noise in the output. We could modify the definition
of W i to reduce the noise (we would need a better interpolation strategy Real.round isnt a very
good one), but for our purposes its easier to just increase the length of W .
Task 6.3 (18%). In MkSignal.sml, implement the function
val delay : int real signal signal
where delay n a S evaluates to
*bi/nc
X
+
j
a Si jn : 0 i < |S|
j=0
Or, perhaps more useful: delay n a S evaluates to a sequence T of length |S| such that
(
Si ,
if 0 i < n
Ti =
Si + aTin , if n i < |S|
The two formulations are equivalent. You may assume 1 n |S|. For full credit, your implementation
must have O(|S|) work and span.
delay n a S applies a decaying echo effect to the signal S, similar to tape echo effects that
emerged as early as the 1950s. The distance between successive echoes is n samples. We can choose a
in the range [0, 1] to affect the amount of decay: smaller values of a result in weaker echoes.
Hint: Consider processing the input in chunks of size n. The functions const, ++, and ** should be
useful, as well as the sequence function subseq.
SignalLab
Now were ready for some fun! On an Andrew UNIX machine, cd into the top level of the handout
directory, then run the following command:
val
val
val
val
All of these functions produce lookup tables for different waveforms, for use with osc. The
integer parameter is the length of the produced table. For example, (Waveforms.saw 10000)
produces one period of a sawtooth wave, sampled at 10000 points.
WaveIO, which contains functions
is only capable of reading files with a sample rate of 44.1 kHz and a resolution of 16 bits per sample.
WaveIO.writeSound S "path/to/file.wav" writes S as a monophonic sound at 44.1 kHz
to a .wav file on disk. Any samples from the input which are outside the range [1.0, +1.0] will
be clipped.
SignalLab
Try playing with your functions! Save them to .wav files on disk and listen to the results using
software such as VLC or Audacity. Here are some tips:
Use const, ramp, and pwl as frequency control signals for osc.
Use the functions in Waveforms as lookup tables for osc. Try changing the length to hear how it
affects the noise in the output.
Use normalize to prevent a sound from being clipped before it is written to disk.
Whenever you specify a number of samples, remember that there are 44,100 samples per second.
Below are some examples. The first is a sine wave lasting 2 seconds (88200 samples) that begins at
220 Hz, ramps up to 440 Hz, then drops back down to 220 Hz. The second applies a delay effect to one
of the example sounds that was included in the handout. The distance between successive echoes is 0.5
seconds (22050 samples).
Testing
To test your implementations, add testcases to the file Tests.sml. Enter the REPL as described in the
previous section, then call the following functions:
SignalLab
Written Questions
9.1
The following questions are warm-up questions, intended to make sure that you have read and understood the academic integrity policy (see http://www.cs.cmu.edu/~15210/) of the course, as well as
found the tools that weve set up to communicate with you.
Task 9.1 (1%).
Task 9.2 (3%). In each of the following situations, have the students followed or broken the collaboration policy? Briefly justify your answers with a sentence or two.
1. Ludmilla, Joaquin, and Alexander have lunch together after 210 lecture. Over lunch, they discuss
the homework assignment released earlier in the week, including their progress so far. After lunch,
all three go to different classes and dont think about the 210 homework until that evening.
2. While working on 213 homework near his friends from 210, Jon has a moment of insight into the
210 homework assignment. He becomes excited, and tells his friends what he thought of. Ishmael
hasnt gotten that far in the homework, so he doesnt quite understand what Jon is talking about.
Nonetheless, Ishmael copies down what he thinks are the key bits of what he heard to look at when
he gets that far.
3. Yelena has been working on the 210 homework but cant figure out why her solution isnt compiling. She asks Abida to work through a couple of toy examples of functor syntax together, and they
do so with a text editor and the SML REPL. Afterwards, Yelena gets her homework to compile and
keeps working towards a solution.
Task 9.3 (1%). If you hand in your homework 25 hours after the posted due date, and you have no
late days remaining, what is your maximum possible score? (Assume full score is 100%)
SignalLab
9.2
Asymptotics
7. f7 (n) = nlg(n
8. f8 (n) = nn!
Task 9.5 (10%). Carefully prove or disprove each of the following statements. You must refer to the
definition of Big-O. When attempting to disprove, a counterexample along with a brief justification is
sufficient.
1. O is a transitive relation on functions. That is to say, for any functions f , g, h, if f O g and
g O (h), then f O (h).
2. O is a symmetric relation on functions. That is to say, for any functions f and g, if f O g , then
g O f .
9.3
Recurrences
On each homework assignment, we may mark a few questions as forward questions, meaning that they
havent been fully covered in class yet. Studies show that a bit of a struggle is helpful in your education!
Find tight Big-O bounds for the following recurrences. Do not just give a final answer we expect
to see a brief explanation as well. You can assume for both of them that T (1) = T (2) = 1.
Task 9.6 (5%). Forward. T (n) = 9T ( 3n ) + O(n2 )
p
Task 9.7 (5%). Forward. T (n) = T ( n) + O(log n)
10