Varèse pitch space calculations
Switch branches/tags
Nothing to show
Clone or download
wchargin Add a BUILD_ONLY deploy mode
Summary:
This allows running `BUILD_ONLY=true ./deploy.sh` to generate a static,
dependency-free version of the site without actually deploying to the
GitHub Pages site.

Test Plan:
Run `BUILD_ONLY=true ./deploy.sh`. Inspect the temporary directory that
it emits; make sure that the site is fully usable without an Internet
connection. Note that the deploy script does not actually deploy.
Latest commit 0d5b214 Nov 10, 2016

README.md

Varèse-inspired pitch space tools Build status Coverage Status

What?

The gist of it is that if you throw away your contemporary notions of harmony—in particular, the notion that equivalence classes of pitches are determined by modding out octaves—then you get some interesting stuff.

The tree

One of the core concepts at hand is the notion of infoldings and outfoldings of a chord—say a trichord (three notes) for simplicity. If you have a trichord, you can outfold it by “pivoting” the middle note about the top note (an upward outfolding) or the bottom note (a downward outfolding), preserving the interval. So C–E–G would upfold to C–G–B♭, preserving the minor third, or downfold to A♭–C–G, preserving the major third.

The inverse operation is an infolding: pivot one of the outer notes about the middle note. So C–E–G could infold to C–C♯–E or E–G–G♯. But note only the first of those outfolds back to the original, because only the first one puts the folded pitch in the middle. So we see that it's more useful to infold the pitch that makes a smaller interval, and there's a canonical infolding.

Thus, the trichords form a (complete, infinite) binary tree: left children are downward outfoldings, and right children are upward outfoldings; parents are canonical infoldings.

This is what the tree explorer (/#/tree) allows you to explore.

This is closely related to Varèse's work.

The roots

There's also the concept of finding the acoustic root of an interval, or, by extension, of a chord.

Suppose we have an interval of two pitches. We want to find some fundamental frequency of which each of the pitches in the interval is an integer multiple. For example, if the pitches are at 500 Hz and 300 Hz, then the fundamental is 100 Hz; if the first pitch were instead 600 Hz, the fundamental would be 200 Hz.

We can extend that to chords recursively: most concisely, if intervalRoot : Pitch → Pitch → Pitch, then define

pairwiseRoots :: [Pitch]  [Pitch]
pairwiseRoots ps = zipWith intervalRoot ps (tail ps)

chordRoot :: [Pitch]  Pitch
chordRoot = head  last  takeWhile (not  null)  iterate pairwiseRoots

and chordRoot gives the root of a chord.

For example, consider the chord C4–E♭4–F♯4. The root of (C4, E♭4) is A♭1, and the root of (E♭4, F♯4) is B1. The root of (A♭1, B1) is E−1. The next iteration would yield the empty list, so we stop here; E−1 is the root of the chord. (Note that the roots get really low really fast: a normal trichord and we're already more than a full octave below the piano!)

If you're paying close attention, you may be wondering how we work with pitches when we really want to be dealing with frequencies—after all, they don't always coincide! So how do we convert something like C4–E4–G4 to frequency? Simple: we get the user to do it! Specifically, if the user gives us specific acoustic ratios for each of the simple intervals—e.g., “we'll call a minor sixth the ratio of the fifth to the eighth overtones”—we can extrapolate everything we need from there. (We only need ratios because the actual frequencies don't matter.) Going in the other direction is easy because we can get exact values logarithmically, and they should be mostly consistent with the user-specified rationalizations.

This is what the pitch calculator (/#/calculator) calculates.

This is mostly a new area of study.

Technical overview

The core operations (i.e., the actual calculations and the interesting stuff) are in a thoroughly tested functional core. The UI is as light as possible a React wrapper around this, which makes it really easy to reason about the data flow. We're also starting to write some tests for the React components themselves—both in terms of the DOM output they produce and with simulated user interactions.

The whole React app is webpacked into a bundle.js file, which is included by the HTML page. The HTML page is tiny, and just provides a <div id="app"></div> into which React will render the application.

The UI entry point is index.jsx, but you really probably want to start looking at either PitchCalculator or TreeExplorer.

Confused? Looking for API links? Here's the stack: React and React Router for the UI and routing, Bootstrap (not React-Bootstrap) for the CSS (not JS), Mocha and Chai for the unit testing. The global state (configuration and settings) is managed through the wonderful Redux and bound to the UI with React Redux. We use VexFlow for engraving chords.

The tool stack includes webpack for the module bundling and babel for the ES2015-and-up desugarings.

Building

$ cd varese
$ npm install
$ npm test
$ npm start
$ open http://localhost:8080/

This will start a development server with hot reloading enabled: just change the JSX files and you shouldn't even have to refresh the page to see your changes. If your changes aren't going through, try touching the file you changed.

Deploying

$ git commit -m "do a thing"
$ ./deploy.sh

You should also probably push to master.

You may have to restart your development server after deploying to get your hot loading working again. Just hit ^C and run npm start again.