GUI and OSC
Assign: Thursday, November 18
Due: Thursday, December 2
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.
Part 1: Pulse Dream
To get started, download the starter code.
In this part, you will write a GUI interface to control a pattern in real time. The pattern and its SynthDef are given to you already. The interface should look similar to the one pictured below:
The interface was created with the following specifications:
- The Window has a title called “Pulse Dream”.
- The Window has a width of 400 and a height of 800. The Window is placed in the lower left corner of the screen.
- The Window is divided into eight 200x200 quadrants.
- The upper two quadrants have a play button and a stop button.
- The lower six quadrants have knobs and text that control specific parameters of the pattern.
Understanding the Starter Code
The SynthDef used in the pattern plays back pulse waves in octaves that run through a resonant lowpass filter. The second half of the starter code has a pattern wrapped in a function. If you execute the block, you will not hear anything. To hear how the pattern sounds, run the following:
(
~pat.value;
~evp.play;
)
Take a careful look at the ~pat
function that is used to play the pattern. All of
the features that we want to change with the GUI are given as environment variables
with default values. Inside the Pbind, we use the pattern Pfunc
to yield the
value of the environment variable. This allows us to change the
environment variable’s value and have it update the pattern in real time. For
example, start the pattern and run ~amp = 0.4
. The pattern should now sound
twice as loud.
Details about the Knobs and Buttons
You should implement your GUI at the bottom of the starter code as a single code block. The very first line should be to run the pattern function. This has been given to you:
(
~pat.value
// Your code here
)
Note that if you want to declared any local variables for the code block
(which you should) that those should be placed above the line ~pat.value
.
Below are the specific details for each button and knob.
Play and Stop Button
When the GUI starts up, the pattern should initially be silent. When the user
clicks on the “Play” button, the pattern should start playing by running the code
~evp.play
. When the user clicks on the “Stop” button, the pattern should stop
playing by running the code ~evp.stop
.
You are welcome to choose any color, text formatting (note that you must use the words “Play” and “Stop”), margins, or other styling. In my implementation, I made the buttons 180x180 with 10 unit margins but feel free to choose something different. Note that you must implement the functionality stated above.
The Knobs
Each knob is required to control their respective parameter of the GUI. Remember
to use the method .linlin
to map the range of the knob’s values to something
other than 0 to 1.
Amplitude Knob:
- Controls the overall amplitude of the Synths generated by the pattern.
- Sets the environment variable
~amp
between a range of 0.0 to 1.0.
Duration Knob:
- Controls the time between each synth generated by the pattern. A higher duration produces less notes per second.
- Sets the environment variable
~dur
between a range of 0.01 to 1.0. Note that if we set the duration too low (i.e., < 0.01) then we will start generating so many synths that it overloads the audio server.
Pulse Width Knob:
- Controls the width of the octave pulse waves.
- Sets the environment variable
~width
between a range of 0.0 to 1.0.
Cutoff Frequency Knob:
- Controls the cutoff frequency of the resonant lowpass filter.
- Sets the environment variable
~cutoffFreq
between a range of 200 to 10000. Note that this should map from a linear range of 0 to 1 to an exponential range of 200 to 10000 because we hear pitch logarithmically. Use the method.linexp
and.explin
to map from a linear to an exponential range and vice versa.
Pan Knob:
- Controls where the pattern plays in the audio field. The knob fully to the left comes only out of the left speaker and the knob fully to the right comes only out of the right speaker.
- Sets the environment variable
~pan
between a range of -1.0 to 1.0.
Attack Time Knob:
- Controls the attack time of each synth generated. The knob to the right gives longer attack times.
- Sets the environment variable
~atkTime
between a range of 0.001 to 0.4.
IMPORTANT: The image above shows the GUI when it is first launched. Notice
how the knobs are set to the initial default values provided by the environment
variables set in ~pat
. Your GUI should start with those initial settings on
the knobs. The easiest way to do that is to set the value of each knob. For
example, if I have a knob stored in the variable ~k
and want to set it to
the value of 0.2, I can simply write ~k.value = 0.2
. Be careful! The
knob for the cutoff frequency, for example, should initially be set to a frequency
of 1000Hz. That will need to be mapped to the range of 0 to 1.0 first and then
set.
Grading
You will be graded on the following criteria:
- Correct functionality. Each button and knob should meet the prescribed criteria detailed above.
- Well organized and cleanly designed interface. I recommend copying my interface for ease but you are welcome to reorganize my GUI so long as you keep the same buttons, text and behavior.
- Good coding style and readability of code.
Part 2: PyCollider
To get started, download the starter code.
SuperCollider was designed to integrate with other audio software and programming languages via OSC. In fact, many programmers have used SuperCollider as the backend audio engine for a variety of different interesting projects. Many of them are listed on SuperCollider’s main webpage. One very cool project is FoxDot which uses Python as the user language to produce audio with SuperCollider. How does that work? Both SuperCollider’s language sclang and server scsynth can listen for incoming messages across a network and respond to those messages to produce sound.
In this part, we will build a SuperCollider program to
respond to OSC messages from a python client contained in
the module client.py
that accompanies your starter code.
The client sends messages to play one of three types of
waves: a sine wave, a triangle wave, and a square wave.
In SuperCollider, three OSCDef
s respond to their
respective waveform messages from the client and
create synths to play those sounds. Your task will be
to write the SuperCollider backend. The client has been
written for you.
Overview of SuperCollider Backend
On the backend SuperCollider should produce synths for
a sine wave, triangle wave, and square wave. The backend
should have three OSCDef
s listening for incoming
OSC messages and produce the requested synth on the audio server.
The requirements for each OSCDef is as follows:
- Each OSCDef listens for messages with the addresses
/sine
,/tri
, and/square
, respectively. - The message for each OSCDef are always a frequency and an amplitude.
- Each OSCDef should post to the SC post window information about the messages received. For example, if the Python client calls the following below, then this is the output of the SuperCollider window. Your output should follow the same format.
Python client:
>>> from client import *
>>> connect()
>>> sine(300, 0.1)
>>> square(600, 0.1)
>>> tri(400, 0.2)
SuperCollider Post Window:
Sine Wave from a NetAddr(127.0.0.1, 61685):
- Freq: 300 | Amp: 0.10000000149012
Square Wave from a NetAddr(127.0.0.1, 61685):
- Freq: 600 | Amp: 0.10000000149012
Triangle Wave from a NetAddr(127.0.0.1, 61685):
- Freq: 400 | Amp: 0.20000000298023
And of course, we should hear a sine wave, followed by a square wave, followed by a triangle wave.
Overview of the Client: client.py
If you have taken CS111, you should already have python installed on your machine through Anaconda and Thonny. You can also use Canopy as well if you took an older version of CS111. Depending on your comfortability/preference you can either run the client from the Command Line or in Thonny.
Option 1: Command Line
If you want to use the Command Line you will
have to install a package for Python called python-osc
.
Run the following:
pip install python-osc
Then you can simply run python.
Option 2: Thonny
If you want to use Thonny (which I recommend if you are unsure about how to use the command line), then execute the following instructions:
- Go to Tools, then Manage Packages…, search for python-osc, and then hit install.
- Open client.py and then run the file using the Green arrow button.
In the python shell (either through the command line or
through Thonny), you can run the functions provided for
you by client.py
. The details of the functions are
below:
connect()
: Connect establishes a connection on your localhost (i.e., 127.0.0.1) with port 57120. This is SuperCollider’s default listening port for sclang. This may be different on your system. In SuperCollider, runNetAddr.langPort
to check the port. If the port is something other than 57120, then provide that as an argument toconnect
.sine(<freq>, <amp>)
: send an OSC message for a sine wave of provided frequency and ampltiude with OSC address of/sine
.tri(<freq>, <amp>)
: send an OSC message for a triangle wave of provided frequency and ampltiude with OSC address of/tri
.square(<freq>, <amp>)
: send an OSC message for a square wave of provided frequency and ampltiude with OSC address of/square
.
To get started sending messages to SuperCollider, always run these two lines first:
>>> from client import *
>>> connect()
Note that you must run Python from the folder that contains
client.py
.
Grading
You will be graded on the following critera:
- Correct implementation of the OSCDefs
- Correct posting to SC Post Window
- Works properly with
client.py
You can of course expand the relationship between both client.py
and the SC
backend to implement any of the functionality of SuperCollider in Python. Extra
credit will be given if you make significant additions to the functionality and
document them in your submission.
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%.