Building Blocks

Assign: Thursday, October 14

Due: Thursday, October 21

Assignment Policies

All assignments are partner assignments. Please make sure to review the policies and guidelines about discussing assignments with those other than your partner. Assignments are usually, but not always, a combination of paper and coded exercises. Please make two submissions (one for paper exercises and one for coded exercises) to Gradescope. Paper exercises are marked with HW to indicate a handwritten question and C to indicate a coded question. Paper submissions can receive an extra 2% bonus if they are typed. Please visit the tools section of the website for more information.

Starter code

Download the starter code here

Written Exercises HW

  1. Generate the first ten samples of the sawtooth wave depicted in the figure below starting at $t = 0$ with a sampling rate of 5 Hz. Note that in instances where the waveform is discontinuous, you can choose between either -1 or 1.

    Image

  2. Generate the first eight samples of the triangle wave depicted in the figure below starting at $t = 0$ with a sampling rate of 2 Hz.

    Image

  3. Suppose that a complex signal consists of frequencies between 1000Hz and 2000Hz, exclusive. What is the minimum sampling rate required such that none of the frequencies are aliased? This sampling rate is also known as the Nyquist rate.

  4. You are designing a bandlimited sawtooth wave $g(t)$ whose fundamental frequency could be anywhere between 500-2000Hz. What is the maximum number of partials including the fundamental frequency that $g(t)$ can have such that it does not produce aliasing at a sample rate of 44.1kHz? Provide an explanation.

  5. What step size would cause no sound to be produced when reading through a sine table of 8192 samples? Explain your answer in a sentence or two. Note that the wavetable contains exactly one full period of the sine wave.

  6. Consider the following wavetable of samples from the signal $0.5\sin(3\pi x) + 0.5\sin(4\pi x + \pi)$ taken at a sample rate of 5Hz. Use this table to calculate samples for various different step sizes.

    Sample Amplitude
    0 0
    1 0.182
    2 0.182
    3 -0.769
    4 0.769
    5 0
    6 -0.769
    7 0.769
    8 -0.182
    9 -0.182
    1. Will the sample rate of 5Hz lead to aliasing? Say yes or no and briefly explain why in a sentence or two.
    2. Write the first five samples using truncation with a step size of 0.75.
    3. Write the first five samples using rounding with a step size of 1.25. If the desired sample is equidistant between two samples (i.e., something like 3.5), round up.
    4. Write the first five samples using linear interpolation with a step size of 2.4.

Coding Exercises C

  1. Aliasing is a huge issue in digital signal processing and can lead to audible distortion. From class, we know that if there are frequencies above the Nyquist frequency, we will have aliases. But it turns out we can determined the exact frequency of an alias if we know the sampling rate and the frequencies from the original analog signal. Specifically, given a frequency $f_x$ such that $f_s \geq f_x \geq f_s/2$ where $f_s$ is the sampling rate, then the aliased signal produced is $f_s - f_x$. In other words, the alias is symmetrical about $f_s/2$. This is called folding.

    1. Suppose we have the following SuperCollider code: {SinOsc.ar([400, 400], 0, 0.1)}.play;. Write a similar piece of SuperCollider code that produces a sine wave of 400Hz but uses a frequency argument other than 400Hz. Test in SuperCollider to confirm! Leverage your new knowledge of folding. At startup, SuperCollider is set to a default sampling rate of 44.1kHz. Check to make sure this is true when you boot the audio server! Assume this sampling rate for this problem and part(b).

    2. You will notice that the condition of $f_x$ was restricted to $f_s \geq f_x \geq f_s/2$. Any frequency $f_x$ between $f_s/2 \geq f_x \geq 0$ will certainly be heard as expected because $f_x$ is below the Nyquist frequency. What happens though if $f_x$ is greater than $f_s$? It turns out the same folding principle applies! First calculate $f_x \mod f_s$ to wrap $f_x$ to a frequency less than the sampling rate. If $(f_x \mod f_s) \geq f_s/2$, then apply the folding principle. Otherwise, the $f_x \mod f_s$ is the frequency heard. Calculate another frequency argument that could produce a sine wave of 400 Hz other than 400Hz or your answer for part (a). Write your answer as a piece of SuperCollider code.

  2. SinOsc uses a single wavetable of 8192 samples from a single period of a sine wave. What frequency would cause SinOsc to produce no sound with a sample rate of 44100Hz? As proof of concept, execute the line s.meter; after executing s.boot;. Play various different sine waves with {SinOsc.ar(<your freq>, 0, 1) ! 2}.play. You should see the output meter rise to 0 for nearly any input frequency. Zero is the loudest a sound can be in SuperCollider. Even for frequencies above human audible range like 21000Hz, the meter should still output a value close to 0 even if we can’t hear it. The correct answer to this question, however, will produce no output (i.e., the meter won’t show any colored bar). To answer this question, write a piece of code of the format {SinOsc.ar(<your freq>, 0, 1) ! 2}.play.

    Your solution should lie in between 0Hz and 44100Hz exclusive. Note that both 0 and 44100Hz are solutions but find the singular solution between them.

    Note: I recommend answering the handwritten exercise 5 first before attempting this question.

  3. In this question, we will work with samples to create some simple effects and waveforms. In SuperCollider, audio samples are typically generated on the server and not in sclang. UGens in sclang are the client-side abstraction used to send messages to the server to produce those samples. Here though, we will generate samples in sclang and send them to the server to be played. To do so, we will use a special array to store our samples called Signal. A Signal is simply an array of floats but comes with special methods including an option to play those floats back on the server. Therefore, Signal is primarily, but not exclusively, used to represent samples in sclang.

    We will use Signal to generate three different effects/waveforms:

    1. Write a function called ~gain that takes a Signal holding samples and scales all the samples by a factor. As we know, increasing and decreasing each sample proportionally increases/ decreases the amplitude and our perceived loudness of the signal. You should return a new Signal. You may find the class method Signal.newClear helpful to declare a new empty array depending upon your implementation.

    2. Write a function called ~reverse that takes a Signal and reverses the order of the samples. This can be a cool audio effect. Note that you should not use the method .reverse as this reverses the signal in place. Your function should return a new Signal. You may find the .size method helpful for getting the number of elements in a Signal.

    3. Write a function called ~lfSaw that implements the UGen LFSaw. Our implementation will differ slightly in that we will not be using a wavetable and we will ignore the parameter for phase for ease. See the problem set starter code for more details but suffice it to say, your function will produce a pure sawtooth wave of the desired frequency for a specified number of samples. You may find the .frac method for floats helpful to get the fractional portion of a floating point number. Note that the multiplication argument is calculated before the addition argument.

  4. In this exercise, we will implement a function for linear interpolation for a wavetable used in UGens like SinOsc or LFSaw. Again, we will do our work on the client-side using Signal. Write a function that takes a wavetable as a signal, a desired frequency, a sample rate, and a number of samples to be produced and produces a new Signal with the interpolated waveform. You will need to think carefully about how to wrap around the table. The .mod method will be helpful. For example, 4.mod(3) evaluates to 1. You may also find the methods .floor and .ceil helpful to find the nearest integers above and below a certain float. You can use the .size method to get the size of the table, and you can use the .frac method to get the fractional portion of any floating point number.

Challenge HW

In the first exercise, we stated that a frequency $f_x$ folds when $f_s \geq f_x \geq f_s/2$. In this challenge, we will prove that this is true. We need a little setup first though. It is readily apparent that $f_x \neq f_s - f_x$ for all $f_x$. For example, if $f_s = 44100$ and $f_x = 100$, then 100 does not equal 44000. Recall that $f_s - f_x$ is the frequency of the alias after folding. What is true though is that the samples are the same for both $f_x$ and $f_s - f_x$. Recall the sampling examples from class seen below:

You can see above that frequencies of each wave are not the same. One image has a frequency of 2Hz and the other has a frequency of 6Hz, yet they have the same samples at a sampling rate of $f_s$ = 8Hz. Based on our knowledge of folding, $f_s - f_x$ does indeed produce an alias of 2Hz because 8Hz - 6Hz = 2Hz. Mathematically, we can generate samples from a given sine wave by writing $\sin(2 \pi fnT)$. $2\pi f$ produces a sine wave of frequency $f$. $T$ is the sampling period and $n$ is any whole number that is zero or greater (i.e., a natural number). $n$ represents the $n$th sample of our wave. Therefore, $nT$ provides the specific point in time to generate a sample from our sine wave.

In the pictures above, let’s consider $f_x$ to be 6 Hz. Therefore the image with 2Hz is the alias. Do you notice anything else interesting about these two waveforms? They are $\pi$ out of phase! Folding not only generates a frequency below $f_s/2$ but also produces an alias whose phase is shifted by $\pi$.

Therefore, we want to show mathematically that the samples of $f_x$ are equivalent to those of an alias whose frequency is $f_s - f_x$ and shifted by $\pi$, namely $\sin(2\pi f_x nT) = \sin(2\pi(f_s - f_x)nT + \pi)$ for all $n$. Remember to use your trignometric identities to aid you. You do not need to provide a formal proof but you should show every mathematical step to let you conclude that any $n$ generates the same sample for both the original signal $\sin(2\pi f_x nT)$ and the alias signal $\sin(2\pi(f_s - f_x)nT + \pi)$.

Submission

Feedback

When you are finished with the problem set, please fill out the form linked here to provide feedback on how long the problem set took you and how difficult you found it. Note that you must fill out this form to receive credit for the problem set.

Submission Guidelines

All assignments in this course are submitted through Gradescope. There are two kinds of assignments in this course: paper assignments and code assignments. Both are submitted to Gradescope. Most assignments are a mix of code and paper assignments. Each question or question part will be marked either “code” or “paper” to indicate which kind of question it is. For paper assignments, note that typed answers will receive a small additional bonus of 2%.