Tenney height is a simple measure of interval complexity that's pretty good at predicting perceptions of relative concordance for simple just intervals. But often we want to think of intervals as belonging to an octave-invariant class rather than as individual intervals. For example, a chord nominally containing the interval 5:4 might actually voice it as 5:2 or 5:8.
We can reflect this by taking the average of the Tenney norms of the base interval, its octave complement, and octave widenings of each of those two intervals. Since we are considering intervals equivalent to their octave complements and octave displacements, we must first normalize the base interval to its simplest form within the octave. For example, 12:5 would be normalized to 5:3.
One consequence of this is that normalized ratios containing an even number score better than purely odd-numbered ratios, since octave displacement will sometimes reduce complexity instead of always increasing it.
Interval table
| Interval | Complexity |
|---|---|
| 1:1 | 1.00 |
| 3:2 | 3.08 |
| 5:4 | 4.82 |
| 5:3 | 4.91 |
| 7:4 | 5.31 |
| 7:6 | 5.89 |
| 7:5 | 6.13 |
| 9:5 | 6.49 |
| 11:6 | 6.54 |
| 9:8 | 6.67 |
| 11:8 | 6.96 |
| 9:7 | 6.98 |
| 13:8 | 7.20 |
| 11:7 | 7.27 |
| 11:10 | 7.28 |
| 15:8 | 7.41 |
| 13:7 | 7.51 |
| 13:10 | 7.52 |
| 11:9 | 7.63 |
| 13:12 | 7.79 |
| 13:9 | 7.87 |
| 17:10 | 7.91 |
| 19:10 | 8.07 |
| 13:11 | 8.16 |
| 17:12 | 8.17 |
| 15:14 | 8.21 |
| 17:9 | 8.26 |
| 19:12 | 8.33 |
| 15:11 | 8.37 |
| 17:14 | 8.39 |
| 17:11 | 8.55 |
| 19:14 | 8.56 |
| 17:16 | 8.59 |
| 15:13 | 8.61 |
| 23:12 | 8.61 |
| 19:11 | 8.71 |
| 19:16 | 8.75 |
| 17:13 | 8.79 |
| 23:14 | 8.83 |
| 21:11 | 8.85 |
| 21:16 | 8.89 |
| 19:18 | 8.92 |
| 19:13 | 8.95 |
| 25:14 | 8.95 |
| 17:15 | 8.99 |
This metric is of questionable utility for more complex ratios, as the perceptual effects of intervals with such ratios are dominated by the simpler intervals in their vicinities. The exception would be quarter tones such as 33:32 and 36:35, which rate at about 10.
Sample Python code
from fractions import Fraction as F
from math import log2
from statistics import mean
def tenney(r: F) -> float:
return log2(r.numerator * r.denominator)
def normalize(r: F) -> F:
while r > 2:
r /= 2
while r < 1:
r *= 2
c = 2/r
if c.numerator + c.denominator > r.numerator + r.denominator:
return r
else:
return c
def tenneyclass(r: F) -> float:
r = normalize(r)
return mean(map(tenney, [r, 2/r, 2*r, 4/r]))