A Simple Synthesizer
For this mini-project, you will continue extending the basic synthesizer you developed last week into a more full-featured synthesizer. In particular, you'll implement two features:
- Multiple voices in the synthesized output, i.e., creating a wave by combining two input waves in a uniform manner.
- A complete ASDR envelope filter, building the simple envelope you developed in lab.
With these features, we are getting closer to emulating how real synthesizers work, such as the legendary Minimoog Model-D. Note in the picture of the Model-D the collections of dials for the "Oscillator Bank" and "Modifiers." These control the voices produced by the Moog as well as the ASDR envelope the Moog uses to shape its sound!
As a starting point, you should use the synthesize-square-wave-note function from your ASDR lab and their associated helper functions.
And their associated helper functions.
Note that you will be expected to use these functions as a basis for your mini-project.
Feel free to edit and modify them as you see fit to achieve the functionality described below.
Because you are using code that you used from your previous work, you should cite yourself and your partner to be clear where your code came from. In a comment at the top of your file give a brief acknowledgement describing what code you have adapted from the lab and who worked on it. The format of these acknowledgements is less important than ensuring that you write some acknowledgement in your file!
Write your program in a file called synthesizer.scm and submit it to Gradescope when you are done.
Part 1: Multi-voice synthesis
We saw that the various fundamental waveforms produce different timbre of sound.
One way to create even more varied timbres is to mix together different waveforms at different frequencies together into a single clip.
In the lab, your synthesize-square-note function generated a note from a square wave.
For this part, generalize synthesize-square-note to generate-note.
generate-note has the same parameters as synthesize-square-note—sample-rate, frequency, duration—but instead of generating a note from a square wave, it generates a note by combining a square wave and a sine wave.
To generate the sine wave portion of the clip, you should leverage your lab code for making a sine wave appropriately. If you have done so, feel free to include the code in your assessment verbatim with appropriate acknowledgement at the top of your file. If you have not done so, you should create such functionality for this part.
To perform this superposition of waves, each sample generate-note creates is the result pointwise averaging the samples from a square wave and sine wave.
That is, the first samples from the square and sine wave are averaged together, then the second samples of each clip are averaged together, and so forth.
The resulting values, by the nature of averaging, will be valid amplitude values in the range (-1.0, 1.0).
Part 2: The ADSR Envelope
In lab, our volume envelope was a simple envelope that had an instantaneous attack and no sustain or decay. Let's enhance our envelope to a true ADSR envelope that allows you to customize the attack, sustain, decay, and subsequent release. For reference, here is the shape of an ADSR envelope from Wikipedia:
The way that we'll proceed is to specify the durations of the first three periods of the envelope, attack, sustain, and decay, with the fourth period, release, being implied by the first three periods.
Augment generate-note to also take a list of three floating point numbers in the range 0.0 to 1.0 that represent the relative percentages of time that the envelope, attack, and sustain should take; the release takes up the remaining time.
For example, if we pass the list (list 0.1 0.3 0.2), then the envelope should have:
- An attack period that lasts
0.1of the samples of the envelope, - A decay period that lasts
0.3of the samples, - A sustain period that lasts
0.2of the samples, and - A release period that lasts
1.0 - 0.1 - 0.3 - 0.2 = 0.4of the samples.
To create this generalized envelope, break up the envelope into its pieces:
- An attack period that increases linearly from 0.0 to 1.0.
- A decay period that decreases linearly from 1.0 to 0.5.
- A sustain period that stays at 0.5.
- A release period that decreases linearly from 0.5 to 0.0.
For reference, note that our original envelope from synthesize-square-note can be obtained by passing (list 0.0 0.0 0.0) for the envelope list.
Notes On Style
For this mini-project, we are being less specific about how you approach the tasks at hand because there are a variety of ways to approach these problems. We're concerned less about how you approach a problem, but whether your implementation reads like the solution you intend to write down. When in doubt, follow our basic principles for style:
- Make sure your code is indented correctly and employs good names for its variables.
- Realize the decomposition of the problem at hand directly using functions and let-binding to capture relevant sub-computations as necessary.
- Use top-level definitions, functions, and let-bindings to eliminate redundancy in your program.
Grading rubric
Redo or above
Submissions that lack any of these characteristics will get an N.
- Displays a good faith attempt to complete every required part of the assignment.
Meets expectations or above
Submissions that lack any of these characteristics but have all the prior characteristics will get an R.
- Includes the specified file,
synthesizer.scm. - Includes an appropriate header on all submitted files that includes the course, author, etc.
- Correctness
- Code runs without errors.
- Core functions are present and correct (except for non-obvious corner cases, when present)
-
(generate-note sample-rate frequency asdr-params). (Note:asdr-paramsmay not be present if part 2 is not fully implemented). generate-noteappropriately combines a square and sine wave.
- Design
- Documents and names all core procedures correctly.
- Code generally follows style guidelines.
Exemplary / Exceeds expectations
Submissions that lack any of these characteristics but have all the prior characteristics will get an M.
- Correctness
generate-notetakes anasdr-paramsparameter and successfully applies to create an ADSR envelope.
- Design
- Function documentation is complete and appropriately evocative of each function's behavior.
- Code follows style guidelines completely, with at most three minor errors present.
- Code is well-designed, avoiding repeated work through decomposition and appropriate language constructs.