Sonification
Coding Project
Objectives
- Establishing a workflow for editing and testing algorithms in webchuck
- Exploring Chuck unit generators (UGens) and applying simple oscillators (with frequency, amplitude and timbre dimensions)
- Understanding data mapping functions and iteration in Chuck
- Exploring data sonification
Key Results
- Code experimentation with codesandbox (forking, customizing, sharing)
- Synthesizer using oscillators (or other sound producing) UGens.
- Time series of your choosing performed by the synthesizer.
- Data mappings between the time series data and synthesis parameters explored.
- Final work shared with codesandbox project link.
Browsers and Caches
Use Firefox, Chrome or Brave (these have been tested with webchuck). Beware of browser caches where what you see is not up to date with what you've changed. For example, suppose you've made an edit and displayed or uploaded it, but there's no effect from the change that was made to the file. The browser may still be hanging on to the earlier version when displaying the page. To force it to forget the older version, use the Ctrl + Shift + Delete command and confirm your intent to "clear" recent history. Then do a Ctrl + r to refresh the page. (On Mac, substitute Cmd for Ctrl in these commands.)
Live code experiments
The pages shown here (NOAA CO2 level sonification) and here (Chris Harley's tidal data) have a live chuck code editor. The former is a one-click, one-file project that should run anywhere and the latter is a codesandbox project for developing code. Experiment with the suggested modifications.
Live code modifications can't be saved directly from the page. To save, manually copy and paste the mods to the version in the code editor, save, and then refresh the page in the browser.
Really Quick Tutorial - Chuck script
Let's break it down. What does this do?
SinOsc osc => dac;
SinOsc
is a class of oscillator and we're instantiating one of those and naming the instance,osc
dac
is a special UGen which is our sound output, aka digital-to-analog converter
osc
is wired todac
so we can hear the tone
But for how long? nVals
is the number of data points in the time series.
for (0 => int i; i < nVals; i++) {
...
100::ms => now;
}
The script completes after the last iteration. Total duration =
nVals * 100ms
At each iteration the frequency of the oscillator gets updated.
osc.freq(vals[i]*1000+200); // data values in range 0 - 1
See Chuck UGens documentation for more.
The rest of index.html
- overview
That's a web page? Looks more like a Frankenstein concoction. But not to worry, there's logic in this madness.
<head>
block- make it responsive for mobile devices
- includes the ACE editor and webchuck stuff
- specifies the dataset (time series) file and a name for the data
- title, graph display area, project description
- fill Chuck editor blocks with Chuck code
- suggestions
- live code script
- button elements
- JavaScript code
- get display elements from the page and set any options
- add any hidden Chuck code to be prepended to the script above
- define button press events
- define dataFilter (see below)
- read data file, filter the data and send to Chuck
- define graph drawing functions
- draw the graph
Handling Time Series Data
Now is the time to pick interesting datasets to sonify. You can choose whatever you want, but an easy place to start is Google Trends. Search a term and download a CSV file from the chart. Be sure to explore various sources for interesting time series data.
Let's assume that the new .csv file is downloaded to your local computer to your working directory. In order to use it in your sonification, a copy needs to be served from some server directory, in our case, from codesandbox.
Use the codesandbox upload feature or drag and drop the file into your codesandbox project.
To tell your script about the file, edit index.html
so that dataURL corresponds to the appropriate FILENAME.csv and specify an appropriate DATANAME (which will label the Y-axis of the graph).
<script>const dataURL = 'FILENAME.csv';</script>
<script>const dataName = 'DATANAME';</script>
Formatting and Sanitization - JavaScript
We need to process the downloaded CSV file to use with the synthesizer (and draw a simple graph), so we need some code to format, sanitize, and filter the dataset. D3 is very useful for this purpose, and it is included in index.html
.
The task here is to edit (or write from scratch) dataFilter()
function. The following is the starting example which leverages code found here.
// define dataFilter
const dataFilter = (row) => {
// The target data set has 2 columns. Skip the row otherwise.
if (row.length !== 2) return null;
// Arranges the given row into a preferred form.
// The incoming source data could be in different formats depending on the project.
const filteredRow = {
// The first column (row[0]) produces a decimal year in the example.
// -- otherwise, if date is in strings, like "2021-09-26",
// swap in this next form to convert the string to decimal year
// year: (1969.5 + (Date.parse(row[0])) / (31556925993.6) ),
year: parseFloat(row[0]),
// The second column (row[1]) produces the actual value.
value: parseFloat(row[1])
};
// Sanitizing; if the row contains a value that is negative or not a number, remove it
// from the dataset.
for (const column in filteredRow) {
const value = filteredRow[column];
if (value < 0 || isNaN(value)) return null;
}
return filteredRow;
};
Mapping parameters
D3 is helpful in transforming different domains/ranges to one another. In the index.html
js code, the time series values get rescaled for use in the Chuck script and transformed for graphing. This documentation might be useful for further exploration.
The Chuck script handles mapping of data values to musical dimensions (for example, frequency). For convenience the rescaled data range is 0 - 1, something that makes it easier to apply the time series values across a variety of musical dimensions (not just frequency, but also amplitude, timbre, etc.).
Create a Composition
Once you have the synthesizer and have mapped the dataset, now it is time for your creativity. Try to draw out the character or the unique quality of a dataset. Or simply make some music with the material.
The simple examples provided are monophonic
in that they only play one "voice" at a time. The oscillator gain is by default set to the maximum amplitude -- 1.0. If adding more sound generators / oscillators that play at the same time, be sure to reduce gains, so that the total doesn't exceed the 1.0 maximum signal, otherwise clipping distortion will occur.
This a non-exhaustive list of variables for experimentation in monophonic
texture
- The number of generators
- The frequency, amplitude and timbre of generators
- The harmonic relation between generators
- Generator event timing depends on data value
Exploring polyphonic
textures
- Generator start times differ (staggered patterns)
- Each generator's tempo or event timing gets set differently (so generators' events aren't at the same time))
- Generators play different patterns, best if the patterns are sporked as separate shreds See Concurrency & Shreds in the Chuck documentation and the code examples
spork.html
andsporkTwice.html
included in the starter project directory you can reference.
Happy sonifying!