You are on page 1of 10

Lab 0 - SignalLab

Due: Mon, Jan 26th , 2015 @ 11:59 pm

Parallel and Sequential Data Structures and Algorithms

15-210 (Spring 15)

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:

tar -xvf signallab-handout.tgz


from a terminal window. Some of the files worth looking at are listed below. The files denoted by * will
be handed in by the submission script.
1. Makefile
2. support/SIGNALBASICS.sig
3. support/SIGNAL.sig
4. * MkSignalBasics.sml
5. * MkSignal.sml
6. Tests.sml
Additionally, you should create a file called:

written.pdf
which contains the answers to the written parts of the assignment.

3
3.1

Submission
Autolab

We will be using Autolab (https://autolab.cs.cmu.edu/15210-s15) for homework submissions


and grading. Each lab will have several grade columns, corresponding to different parts of the assignment.
When you submit your work to Autolab, the autograder will grade your code and fill in the appropriate grade columns. Most labs have two suites of tests: public and private. The results of the public tests

15-210 (Spring 2015)

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.

15-210 (Spring 2015)

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.

The sampled signal is just the following sequence of (approximate) values:


0.0, 0.31, 0.59, 0.81, 0.95, 1.0, 0.95, 0.81, 0.59, 0.31, 0.0, 0.31, 0.59, . . .
This is what we call a discrete-time signal. In SML, we can represent it using a real seq. Each value
in the sequence is called a sample. For the sake of simplicity, we will assume that all signals are sampled
at a rate of 44,100 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.

44,100 Hz is the standard sample rate for audio CDs.

15-210 (Spring 2015)

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|

O(|S|) work and O(log |S|) span.


Task 5.4 (8%). In MkSignalBasics.sml, implement the functions
val ++ : signal * signal signal
val ** : signal * signal signal


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.

15-210 (Spring 2015)

SignalLab

The Slightly More Advanced

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

15-210 (Spring 2015)

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.

15-210 (Spring 2015)

SignalLab

Putting It All Together

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:

smlnj -m sources.cm infix.sml


This compiles your code and opens the SML/NJ REPL (Read-Evaluate-Print Loop). Within the REPL,
there are five primary structures available to you:
Signal, which contains all of your code implementations.
RefSignal, which contains reference implementations of all code tasks in this assignment. Note
that this implementation is purposefully terrible and slow in order to not give away any answers.
We use these implementations to verify the correctness of your code.
Tester, which contains functions

val testBasics : unit unit


val testSignal : unit unit
These functions run your code with inputs from Tests.sml and check that the output is correct.
More on this in the next section.
Waveforms, which contains functions

val
val
val
val

sine : int real seq


square : int real seq
triangle : int real seq
saw : int real seq

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

val readSound : string signal


val writeSound : signal string unit
WaveIO.readSound "path/to/file.wav" reads a .wav file from disk, collapsing all channels into a single monophonic signal at 44.1 kHz, normalized to the range [1.0, +1.0]. readSound


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.

15-210 (Spring 2015)

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

$ smlnj -m sources.cm infix.sml


Standard ML of New Jersey v110.xx [built: ...]
...
- open Signal;
...
- val sinetable = Waveforms.sine 10000;
- val freq = pwl 88200 (Seq.%[(0, 220.0), (44100, 440.0), (88200, 220.0)]);
- WaveIO.writeSound (osc sinetable freq) "sounds/test1.wav";
...
- val inputSound = WaveIO.readSound "sounds/tom.wav";
- WaveIO.writeSound (delay 22050 0.3 inputSound) "sounds/test2.wav";

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:

$ smlnj -m sources.cm infix.sml


Standard ML of New Jersey v110.xx [built: ...]
...
- Tester.testBasics ();
...
- Tester.testSignal ();
...
Note that for these functions only, your MkSignal.sml will be compiled with MkRefSignalBasics
in support/MkRefSignalBasics.sml. This implementation is purposefully terrible and slow, but still
produces correct output. We do this so that the correctness of Tasks 6.1, 6.2, and 6.3 is not dependent
on having completed the previous tasks. To see the correct output for any particular testcase, you can
access our reference implementations in the structure RefSignal : SIGNAL.
8

15-210 (Spring 2015)

SignalLab

Written Questions

For all tasks in this section, write your answers in written.pdf.

9.1

Academic Integrity and Policy

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

Describe the picture posted as an instructors note on Piazza. Be creative!

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%)

15-210 (Spring 2015)

SignalLab

9.2

Asymptotics

Lets begin by recalling the definition of Big-O:


Definition 9.1. Consider functions f : N R+ and g : N R+ . We say that f O(g) if and only if
there exist constants N0 N and c R+ such that for all n N0 , f (n) c g(n).
Task 9.4 (5%). Rearrange the list of functions below so that it is ordered with respect to Big-O from
smallest to largest. Just state the ordering by number. For example, if you write 12345678, that
indicates f1 O( f2 ), f2 O( f3 ), f3 O( f4 ), etc.
1. f1 (n) = 36n52 + 15n18 + n2
2. f2 (n) = 2n1.5
3. f3 (n) = (nn )!
4. f4 (n) = 43n
5. f5 (n) = 210n
6. f6 (n) = lg(lg(lg(lg(n))))
2

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

You might also like