Just Intonation with Python
I wrote this library back in 2000. I got away from playing around with JI for years, but recently started working with Supercollider (Sept 2014), so I wanted to resuscitate this code.
Another interesting library is Just Intonation Experiments in Python.
In the last few months I've become a big fan of Just Intonation. One tedious aspect of this is that you end up fondling ratios a lot. The math gets boring after a while, though I do believe you should be able to do the math on your own to get a feel for what it is you're doing.
Having said that, I decided I needed some help because I got sick of reducing multiplied ratios.
I've written a quick
Python module,
ratio.py
, which handles a lot
of the tedium. In particular, building up JI tetrachords and scales
based on justly intuned chords or by katapyknosis is pretty simple for
me now.
Here's an example interactive Python session of me diddling with the generation of a subminor scale.
Python 1.5.1 (#1, Jul 9 1998, 17:44:38) [GCC 2.7.2.3] on sunos5 Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam >>> from ratio import * >>> s = Ratio(1, 1).chord_of('subminor-7') >>> s [<1:1>, <7:6>, <3:2>, <11:6>] >>> s.append(Ratio(4, 3)) >>> s.sort() >>> s [<1:1>, <7:6>, <4:3>, <3:2>, <11:6>] >>> s = s + Ratio(3, 2).chord_of('subminor') >>> s [<1:1>, <7:6>, <4:3>, <3:2>, <11:6>, <3:2>, <7:4>, <9:8>] >>> s.sort() >>> s [<1:1>, <9:8>, <7:6>, <4:3>, <3:2>, <3:2>, <7:4>, <11:6>] >>> del s[4] >>> s [<1:1>, <9:8>, <7:6>, <4:3>, <3:2>, <7:4>, <11:6>] >>> s.append(Ratio(2, 1)) >>> s [<1:1>, <9:8>, <7:6>, <4:3>, <3:2>, <7:4>, <11:6>, <2:1>] >>> gamut2intervals(s) [<9:8>, <28:27>, <8:7>, <9:8>, <7:6>, <22:21>, <12:11>] >>> ratiotable(s) 1:1 | 1:1 9:8 7:6 4:3 3:2 7:4 11:6 2:1 9:8 | 8:9 1:1 28:27 32:27 4:3 14:9 44:27 16:9 7:6 | 6:7 27:28 1:1 8:7 9:7 3:2 11:7 12:7 4:3 | 3:4 27:32 7:8 1:1 9:8 21:16 11:8 3:2 3:2 | 2:3 3:4 7:9 8:9 1:1 7:6 11:9 4:3 7:4 | 4:7 9:14 2:3 16:21 6:7 1:1 22:21 8:7 11:6 | 6:11 27:44 7:11 8:11 9:11 21:22 1:1 12:11 2:1 | 1:2 9:16 7:12 2:3 3:4 7:8 11:12 1:1 >>> # Well, that was a little difficult. Time for a Lattice... >>> g = Duodene(vrat=Ratio(7, 6)) >>> g <14:9> <7:6> <7:4> <21:16> <4:3> <1:1> <3:2> <9:8> <8:7> <12:7> <9:7> <27:14> >>> g.gamut() [<1:1>, <9:8>, <8:7>, <7:6>, <9:7>, <21:16>, <4:3>, <3:2>, <14:9>, <12:7>, <7:4>, <27:14>] >>> cents(g.gamut()) [0.0, 203.910001731, 231.174093531, 266.870905603, 435.084095261, \ 470.780907334, 498.044999134, 701.955000865, 764.915904737, \ 933.129094395, 968.825906468, 1137.03909613] >>>
Manual
The module file itself is well documented, and should be perfectly
clear to any Pythoneer. The only strange thing to observe is that
since I think of interval addition as addition, the code
snippet Ratio(4, 3) + Ratio(9, 8)
is actually doing a
fraction multiply behind the scenes.
Ratio methods
Most of the tedious bits hang out in the Ratio
class, as
you can tell from the example above.
__float__
- This is called by the
float
function. It simply turns the ratio into a float (real) value. Seecents
complement(against=None)
- Returns (2:1 - the ratio) if
against
isn't set. cents()
- Returns the ratio value in cents.
reduce()
- Simplifies the ratio.
octave()
- Moves the ratio into the range 1:1 =< ratio <= 2:1.
katapykne(n=1)
- Do n divisions of the ratio by katapyknosis.
If n==1, the (non-superparticular) ratio is simply divided. So, <5:3> will return [<5:4>, <4:3>].
This does 1:1 katapyknosis.
katapykne_ab(a, b)
- For people who find 1:1 katapyknosis boring, this does a/b
katapyknosis by the formula in Chalmers'
"Divisions of the
Tetrachord", p. 13 (chapter 2).
You may also do this using the Python slice notation. That is,
r[1:1]
will do 1:1 katapyknosis on the Ratior
chord_of(chord_name, ident=0)
-
Return a list of ratios which make a chord of the named type. The types available: major, minor, diminished, subminor, dom-7, major-7, minor-7, subminor-7, dom-9, major-9, minor-9. See the source for a complete list.
The default is to make the instance ration the lowest identity of the chord. If you wanted to make a septimal minor (or subminor) triad with 3:2 as the second note, you'd do this:
>>> Ratio(3, 2).chord_of('subminor', 1) [<9:7>, <3:2>, <27:14>]
Since Python is zero-indexed, '1' is the second identity in the chord.
Finally, you may use a list or tuple of harmonic identities instead of a chord name:
>>> Ratio(1, 1).chord_of((5, 7, 9, 33), 1) [<10:7>, <1:1>, <9:7>, <33:28>]
Duodene methods
This class should probably be called
Latice
but the default behavior looks like Ellis's
duodene, so that's what it's called.
__init__(hrat=Ratio(3, 2), hn=4, vrat=Ratio(5, 4), vn=3)
- Generate a new duodene.
d = Duodene(horizontal_ratio, horizontal_number, vert_ratio, vert_num) d = Duodene(vrat=Ratio(7, 4), hn=5, vn=5)
The default is for the horizontal scale to be 4 3:2 (perfect 5ths), and for the vertical scale to be 3 5:4 (major 3rds). The chart will be centered on 1:1, but is biased toward producing more ratios above 1:1, rather than below.
gamut
- This simply returns a list of all ratios in the duodene. If you
leave
hn
andvn
at their default values, this will be 12 ratios.
Utilities
In addition the the Ratio methods listed above, there are a number of utility functions which operate on a list of either scale or interval ratios.
cents(intervals)
- Returns the sequence of cent values from a sequence of scale degrees.
intervals2gamut(intervals)
- Returns the sequence of intervals from a sequence of scale degrees.
gamut2intervals(ratios)
- Returns a sequence of scale degrees from a sequnce of intervals.
gamut2series(gamut)
- Return a harmonic overtone list based on a sequence of pitches.
This gets a touch confused when the series spans more than an octave. You may need to fiddle with the argument to guarantee a completely reduced answer.
gamut2identities(gamut)
- Return a harmonic overtone identity list based on a sequence of
pitches.
That is, give us [1, 5, 3] from a major chord, instead of [4, 5, 6].
ratiotable(ratios)
- Generate a table of inter-scale-degree ratios.
triads(ratios)
- This will try to find all triads it knows about in the given collection of scale degrees.
tetrads(ratios)
- This will try to find all four-note chords it knows about in the given collection of scale degrees.