Meantone Counterpoint

I've talked a little bit on this blog about microtonal counterpoint, but my theories and my programs for composing it aren't very far along. Indeed, even just for regular counterpoint in 12-TET, I've never written a program that gets all the way up to fourth-species Fuxian counterpoint, to say nothing of florid or imitative counterpoint. I have a long way to go. But microtonal counterpoint is important to me and I'm going to re-devote myself to the project. Right here.

:: Contrapuntal Motion

When two simultaneously sounding voices each move by their own melodic intervals to new notes, the discipline of counterpoint classifies the relative motion in terms of the relative sizes of the melodic intervals and whether the voices cross. These contrapuntal motions are one of the most important things in counterpoint.

This is already a challenge for the theory of microtonal counterpoint, because intervals in one microtonal tuning system won't be the same size as intervals in another. There isn't, in full generality, a fact of the matter as to which of two intervals is larger, including whether an interval is larger than P1. In my post Orders of Modified Musical Intervals, I wrote about how if we assume that we're writing for tuning systems that preserve the order of natural intervals found in 12-TET, then that assumption induces a partial order over all rank-2 intervals. We could consult this partial order for composing and arranging; with it, we have some guarantees about relative interval size, and maybe we could constrain ourselves to writing with respect to those guarantees.

I'm proud of that post, and I stand by it in theory, but I'd also like an easier way to compose than consulting that partial order graph. My new plan is to compose for rank-2 tuning systems in which P1 < d2 < A1. I'll call this the meantone constraint. This constraint is true of the meantone tuning systems, and of 31-EDO, and of any other rank-2 pure octave syntonic tuning system that tunes P5 to a value between 2^(10/17) and 2^(13/22). It's a good family to work in. Also, for higher-rank tuning systems, the meantone constraint holds in Lilley's 5-limit just intonation tuning system, and in all three of the septimal extensions I've considered to Lilley's 5-limit system. The meantone constraint does not hold true in Pythagorean tuning or in 19-EDO; indeed, of the five families of once-modified interval orders that I identified in the Orders Of Musical Intervals post, only one family has A1 and d2 larger than P1. This assumption certainly isn't universal - but it's our starting point for this blog post. By tuning the octave purely, t(P8) = 2, and by also tuning one of the intervals below to a value below, we can get a few different famous members of the Meantone family of tuning systems:

t(m3) = 6/5  # Third-comma meantone
t(M3) = 5/4  # Quarter-comma meantone
t(A4) = 45/32  # Sixth-comma meantone
t(dddd3) = 1/1  # 31-EDO

.

Given the meantone constraint, it's fairly easy to compare the size of most simple rank-2 intervals. First, we represent intervals in the (A1, d2) basis, as we have done so often in the past. Here are some familiar intervals in (A1, d2) for reference, specifically ordered here according to quarter-comma meantone tuning:

P1 : (0, 0)
AA0 : (1, -1)
d2 : (0, 1)
A1 : (1, 0)
m2 : (1, 1)
AA1 : (2, 0)
dd3 : (1, 2)
M2 : (2, 1)
d3 : (2, 2)
A2 : (3, 1)
m3 : (3, 2)
AA2 : (4, 1)
dd4 : (3, 3)
M3 : (4, 2)
d4 : (4, 3)
A3 : (5, 2)
P4 : (5, 3)
AA3 : (6, 2)
dd5 : (5, 4)
A4 : (6, 3)
d5 : (6, 4)
AA4 : (7, 3)
dd6 : (6, 5)
P5 : (7, 4)
d6 : (7, 5)
A5 : (8, 4)
m6 : (8, 5)
AA5 : (9, 4)
dd7 : (8, 6)
M6 : (9, 5)
d7 : (9, 6)
A6 : (10, 5)
m7 : (10, 6)
AA6 : (11, 5)
dd8 : (10, 7)
M7 : (11, 6)
d8 : (11, 7)
A7 : (12, 6)
dd9 : (11, 8)
P8 : (12, 7)
.
To compare two intervals, first, subtract interval2 from interval1. If the components of the difference vector are both zero, then the intervals under comparison are the same. Otherwise, if the components of the difference vector are both non-positive, then interval2 is larger. If the components are both non-negative, then interval1 is larger. Both of those facts follow from A1 > P1 and d2 > P1. 

When the components of the difference vector have different signs, we can still sometimes make a judgement, since we also know that A1 > d2. If the A1 component of the difference vector is negative and d2 is positive and A1 is >= d2 in magnitude, then the interval2 is larger. If the A1 component is positive and the d2 component is negative and A1 is >= d2 in magnitude, then interval1 is larger. It's only when the components have mixed signs and abs(d2) > abs(A1) that there's ambiguity. And I think I can compose around that ambiguity.

This produces a new partial order - one that's much less partial than before. It's so close to total that it's basically a straight line and my nice Graphviz image of the graph was just too tall to post here, so you get text:
P1
AA0 >< d2
A1
m2
AA1 >< dd3
M2
d3
A2
m3
AA2 >< dd4
M3
d4
A3
P4
AA3 >< dd5
A4
d5
AA4 > < dd6
P5
d6
A5
m6
AA5 >< dd7
M6
d7
A6
m7
AA6 >< dd8
M7
d8
A7 >< dd9
P8
Here each line is smaller than the one below it, and >< means "incomparable". In summary, we can put all of the natural and once modified intervals into a total order, and twice modified intervals can basically be placed in the order, except occasionally you won't be able to compare a twice diminished interval against a twice augmented interval, and also there are incompatibilities between A7 >< dd9 and AA0 >< d2, but whatever. These constraints seem totally fine to me. I'm capable of composing music where I'm not allowed to use a melodic interval of a AA6 simultaneously with a melodic interval of a dd8. That's not even a little bit of a problem.

A fun side note: In 31-EDO, the intervals I list as incomparable above are actually equivalent, e.g. A7 and dd9 are both tuned to 2^(30/31), while AA4 and dd6 are both tuned to 2^(17/31).

How about a code snippet in python for comparing meantone intervals. It's not great code - there's some duplicated stuff, particularly the stuff that I should move into the Interval class - but it works. It's kind of funny to me that once upon a time, to compare intervals, I would just take the difference of midi notes. I think I've come a long way.

I think that solves contrapuntal motion - or it gives you the tools to solve it, anyway. Maybe I should spell it out though, since I'll be coding it anyway. Suppose we have voices called v1(t) and v2(t), and further, assume that v2 should always be above v1. If the harmonic interval {v2(t) - v1(t)}, is less than P1 at any time t, then v2 has crossed below v1. That's our first type of contrapuntal motion, and it's bad. I suppose technically the crossing motion exists at the point where the harmonic interval goes from being > P1 to being < P1, or back again when the harmonic interval goes from being < P1 to being > P1. But I'm not going to cross the voices, so it doesn't matter much where it technically happens - it's just not going to happen.

All of the other contrapuntal motions assume non-crossing. Once we've established non-crossing, then we don't need the harmonic intervals anymore to classify the motion, we just need the melodic intervals, e.g. {v2(t + 1) - v2(t)}. If melodic intervals in v1 and v2 sampled between two times (t) and (t+1) are both larger than P1, or both smaller than P1, or both equal to P1, then the motion is called "direct". If the motion is direct and the intervals are the same, then the motion is "parallel". If the motion is direct and the intervals differ, then the motion is "similar". If motion is not direct, we have two more options: If one melodic interval is equal to P1 and one is not, then we have oblique motion. If the motion is not direct and neither melodic interval is equal to P1, so that one melodic interval is larger than P1 and one is smaller, then the motion is contrary. Here's a hierarchy that's also a decision tree, sort of.


* crossing_motion: some harmonic interval < P1
* non_crossing_motion:
* direct_motion: (v1_mel = v2_mel) or (v1_mel > P1 and v2_mel > P1) or (v1_mel < P1 and v1_mel < P1)
* parallel_motion: v1_mel = v2_mel
* similar_motion
* indirect_motion
* oblique_motion: (v1_mel = P1 or v2_mel = P1)
* contrary_motion
.
That's the tersest way that I know to write it. If you've got a better way, I'm interested to see it.

I think I could write counterpoint purely in interval space, but let's talk about converting to pitch space anyway. That's what musicians like to read. Intervals and pitches exist in one-to-one correspondence - it's only when we tune them to frequency ratios and frequencies that we lose information. A pitch consists of a letter name, a group of symbols called an accidental, and finally an octave. If you don't include the octave, then you instead have a pitch class. The easiest way to convert intervals to pitches is to pick a pitch origin, like natural C in octave 1, or C1 for brevity. Then we can associate the interval "P5" to "C1 + P5", which is G1. To calculate the pitch name for an interval {i} over C1, first initialize a counter, octave_counter = 1. If the interval {i.d2 >= P8.d2}, then substract P8 from {i} and add 1 to the octave_counter. Keep doing that until {i.d2 < P8.d2}. Alternatively, if at the start you had {i.d2 < P1.d2}, then add an octave and substract 1 from the octave counter. Keep doing that until {0 <= i.d2 < 7}. Now the value of the octave_counter is the octave of the original pitch. Nice. Actually, we could probably do it all in one step with some modular arithmetic, like in python, for {i.d2 = 15}, we can do divmod(i.d2, P8.d2), which returns (2, 1), where the first component is the octave of the pitch and the second component is the new value of i.d2 after we've added or substracted octaves. Then we still need to adjust {i.A1} like as if we'd added or substracted octaves, e.g. {i.A1 = i.A1 - (P8.A1 * 2)} in this case.

Next we can get the letter name of the pitch by taking the {i.d2}th element of the array "CDEFGAB", counting from zero, so that {i.d2 = 0} will give us a letter name of C and {i.d2 = 4} will give us a letter name of "G". It happens to be that i.d2 will be one less than the interval's ordinal, so that i.d2 = 4 means we have some kind of a fifth interval. To get the accidental of the pitch for {i}, we need to compare {i.A1} to the A1 of the natural pitch with the same letter name. This is just as easy as finding the letter name: now we take the {i.d2}th element of the array [0, 2, 4, 5, 7, 9, 11]. For example, the G natural over C1 has an A1 of 7. This 7 also happens to also be the number of division in 12-EDO between G1 and C1, although we won't be working in 12-EDO. Now the accidental degree of the pitch can be found as {accidental_degree = i.A1 - A1_for_natural}. If the accidental_degree = 0, then we have a natural pitch and we usually don't write in any accidental. If the accidental_degree > 0, then the accidental is a sharp symbol repeated {accidental_degree} number of times. If accidental_degree < 0, then the accidental is a flat symbol repeated {accidental degree} number of times.

I've talked a lot in the past about how to tune frequency ratios, such as t(P8) = 2/1. Now that we're mapping intervals (above a pitch origin) to pitches, the only thing we need in order to tune pitches to frequencies (rather than frequency ratios) is a frequency for the pitch origin. You could do a little math and choose C1 so that A4 has a frequency of 440 hz or 432 hz or anything else, but as long as we're breaking/improving 12-EDO, why not break/improve the pitch origin also? I'm fond of Sauveur Pitch, in which all the natural Cs are tuned exactly to integer powers of 2 hz, starting with C-4 at 1 hz, and giving us our first clearly audible pitch at t(C1) = 32 hz. That's how I'll be fixing my pitch space. You're welcome to do something else. In comparison, C1 in 12-TET centered on an A4 of 440 hz has a frequency of 440 * 2^(-9/12) / 8, which is about 32.7 hz. Not too far off. Centering your pitch space on t(C1) = 32 hz, I think there's a range of about 44 cents that A4 can fall in, depending on the tuning system you use, and they're all flat compared to 440, which isn't a huge surprise since 32 hz is flat relative to 32.7 hz.

Honestly, I also like the system where A4 is set to 440, because an A1 around 55 hz is the lowest piano pitch that I can sing with force, and having an integer frequency for that point is pretty cool, and also apparently "the lowest note that one of your number can sing shall be called A natural" might historically have been how church monks would tune their choirs before tuning forks were invented, I once heard, and that's a fun rumor. But I'm still going with Sauveur.

From here on, I'll just progress through Fux's different counterpoint species until I meet some resistance, maybe posting insights and code snippets as I go. If that goes well, I'll probably talk about work other people have done on microtonal counterpoint, e.g. in addition to consonances and dissonances, the microtonal composer Easley Blackwood Jr. recognized "discords" and he had rules for preparing and resolving them contrapuntally. We'll talk about Blackwood eventually.

:: First Species Counterpoint

My guess is that it's not terribly easy to get cool pitches or intervals by following the Fuxian rules of counterpoint. The rules do a good job of keeping us in the space of natural intervals, with occasional deviations into once-modified intervals, but almost never will we reach twice-modified intervals or higher. The obvious way to get to weird intervals faster is to expand the set of melodic and harmonic intervals that we allow ourselves to use. For example, in 12-TET, the m3 and M3 are considered consonant harmonic intervals, but in other EDOs, microtonal composers often use nearby intervals as if they were consonant thirds, e.g. Zheanna Erose talks about the use of basic triads in 31-EDO built by combining the following intervals harmonically with P1 and P5:

Subminor triad: A2
Minor triad: m3
Neutral triad: AA2 = dd4
Major triad: M3
Super-major triad: d4
.
And those chords sound pretty good in Zheanna's video, yeah? So the pseudo-third intervals and their octave complements, the pseudo-sixths, might be good additions to the consonant harmonic intervals if you're writing counterpoint microtonally. I suppose you'd have to check the sound of triads based on AA2 and dd4 in tuning systems where they're not equated if you want to decide between them or decide how to use them with different functions.

Also, in traditional counterpoint, most melodic motion is done by "steps" - intervals of m2 and M2 - with larger melodic motions ("leaps") usually being following by steps in the opposite direction so we don't move too far too fast. For microtonal music, we might expand our notions of which intervals are suitable as melodic steps ("consonant" steps?), to include some other intervals between P1 and m3 - some number of {d2, A1, AA1, dd3, d3, A2}, if any of those sound good melodically - I don't know if they do, but I'm going to write my counterpoint programs so that it's easy to change the sets of usable harmonic and melodic intervals for experimentation.

But let's follow the rules before breaking them. I'll be developing my programs on the exercises in Jacob Gran's counterpoint youtube videos, starting with How to Compose 1:1 Counterpoint || Tonal Voice Leading 1. You may find his voice more bearable at 2x speed or faster. He starts us out with this cantus firmus composed by Salieri:

C4,w F4,w E4,w A4,w G4,w F4,w E4,w D4,w C4,w
.

We're going to write a counterpoint melody above that. Each counterpoint note has be a consonant interval above the note sounding at the same time in the cantus firmus. Our consonant options are:

P1, P5, P8, m3, M3, m6, M6, m10, M10

with P1 only being an option on the first and last notes. Gran tells us that we can't use two perfect consonant harmonic intervals (i.e. P1, P5, P8) in a row. You've got to throw in some imperfect consonant harmonic intervals, (m3, M3, m6, M6, m10, M10), for spice. And for now, we won't use any dissonant harmonic intervals, those being (2nds, 4ths, 7ths, augmented intervals, and diminished intervals).

When Gran talks about scale degrees in this exercise, we're going to assume a major scale, (P1 M2 M3 P4 P5 M6 M7), over C natural. Natural C is also the first and last pitch class of the cantus firmus.  Because we're writing a counterpoint above the cantus firmus, Gran tells us the counterpoint must begin on scale degrees ^1, ^3, or ^5. Among our consonant intervals, this limits us to P1, M3, P5, P8, M10.

The counterpoint can move melodically by these small intervals ("steps"):

(m2 M2)

up or down. So these seconds are ?consonant melodically, even though seconds aren't consonant harmonically. Different sets of intervals. Using non-positive numbers, we can write names for the descending versions of those steps, so the full set of ?consonant melodic steps could be written:

(m0 M0 m2 M2)

. The counterpoint can also move melodically by these large intervals ("leaps"):

(m3, M3, P4, P5, m6, M6, P8)

up or down. By embracing negative numbers, we can again write the full set explicitly:

(m3, M3, P4, P5, m6, M6, P8, m-1, M-1, P-2, P-3, m-4, M-4, P-6)

. Those are the ?consonant melodic leaps. Okay, I really want a different adjective. Harmonic intervals can be consonant, and melodic intervals can be... graceful? Fluent. The fluent melodic intervals are the fluent steps and fluent leaps.

The counterpoint can also move by P1, i.e. stay still. The cantus firmus won't have successive repeated pitches, so this is the only way that we'll get oblique motion between the cantus firmus and the counterpoint.

Gran also says that we can't use P4/P5 melodically between scale degrees ^4 and ^7. Weird flex, but okay. In a major scale, ^4 lies a P4 above P1, plus or minus some number of P8s, and ^7 lies a M7 above P1, plus or minus some number of P8s. Of course, we can't leap by more than an octave, but it might still happen that we reach scale degree ^4 or ^7 in a different octave by smaller motions, and then we'll have to do some tests.

Gran says that melodic leaps larger than a M3 are "typically" followed by step-wise melodic intervals in the opposite direction (i.e. the leaps are "recovered"), "especially" after leaping upward. The "especially" part isn't hard to code: I'll just always recover downward by step from melodic leaps upward that are larger than M3. The "typically" part is a little bit trickier: I'm okay with generating melodies probabilistically, but I'd like the part of my code that *verifies* melodies to be deterministic. I don't want a melody to be valid on one pass of my program and invalid on another pass. So how about these for deterministic rules: first, we'll always recover upward by step after a melodic leap downward that is larger than P4 in magnitude (i.e. less than P-2), and 2) no two successive downward melodic interval in sum will outline an interval more than A5 in magnitude, so that you can go down by P4 + M2 or M3 + M3, but not by P4 + m3 or m2 + P5. I haven't played with those intervals to see if they actually sound typical of a baroque setting, but I can change it later if it's not good. An A5 below P1 is better known as a d-3, a diminished negative third. No two successive melodic intervals of a voice shall sum to less than d-3.

Gran also gives some vague rules about how the overall melodic shapes of the cantus firmus and the counterpoint should differ. I'm not coding that and I'm not sorry.

You might be noticing that there are kind of a lot of rules, and we're only like halfway done. In my experience, this is one of the biggest challenges of writing counterpoint programs: there's a a lot of rules and weird special cases that make your code really ugly, and you'll get sad looking at it and quit for a few months, and when you come back to it, you'll want to start from scratch instead of looking at that awful code again. The next challenge is finding counterpoint melodies that satisfy all the rules, which is kind of like a fun search+optimization problem for computer science students, and the last challenge is wondering why you should keep working with all these rules when no one even likes baroque music, just play Driver's License by Olivia Rodrigo again, god damn. The second problem is fun and I have some new techniques I want to try against it, and my solution to the third problem is to instead make weird mathy microtonal music that challenges me and might help introduce me to some with weird mathy people who also like being challenged by art. But problem-one doesn't go away: the code's still going to be flaming garbage. Ready for more garbage? Cool.

Do you remember the types of contrapuntal motion, like parallel motion and similar motion? We've got rules about them now. First, the counterpoint can't cross the cantus firmus. Easy. Second, we can't approach a perfect harmonic consonance (e.g. a harmonic interval of P1, P5, or P8) by direct motion (parallel motion of similar motion). The contrapuntal motion that ends on a perfect harmonic consonance instead has to be indirect, i.e. it has to be contrary (with one interval larger than P1 and one smaller than P1) or oblique (with one equal to P1 and one unequal to P1).

Those are the rules as Gran presents them in the first exercise. You can find more rules in different sources, but these are a good start. Let's code up the constraints, find some solutions, and give the solutions a listen.

In the past, when I've tried to solve counterpoint constraints, I've tried a few strategies. One is to generate notes one at a time from start to finish, looking at the set of valid notes for the next time step and picking randomly from those. If you get to a point where there are no remaining valid steps, then abort and start over. This isn't memory intensive in each run, but it fails a lot, and it fails more with longer songs. Another thing I've tried is to a recursive function that can try every possible melody, which aborts, prunes, and backtracks when difficulties are encountered. This is more impressive and rarely fails, but it's slow and memory intensive and maybe overkill. One strategy I don't think I've ever tried is to make a complete song that violates some constraints and to then try to fix it by small edits. Not every optimization + design problem can be solved by small local edits, but I don't think I ever tried with counterpoint, and I'd like to. I'll eventually need to be able to make modifications to an existing phrase in order to compose imitative counterpoint, so I might as well figure that out now.

I kind of like the idea of making multiple passes through the contrapuntal melody in which you either:

* adjust things to satisfy the harmonic constraints, or
* adjust things to satisfy the melodic constraints.
.
There won't be any guarantee that you'll converge to something valid, but maybe it will work out fine anyway? Sometimes things work out fine, you know? And maybe you can maintain a list of flags on all of the notes of the melody that say either "this pitch is fixed, don't change it" or "this one is free for modification". That way maybe you're gradually locking down the form of the melody instead of alternating back and forth between two version, one that's more harmonically valid and one that's more melodically valid, without making progress.

I never mentioned or coded a rule saying that the pitch classes had to come from a C major diatonic scale. Often, when my program ventures outside of a C major scale, it sounds quite bad, but also I just got a very nice melody with a Bb. That's encouraging. I want to go outside of the scale. That's kind of the point. I admit, it sounds better as a B natural, but it also sounds quite good as a Bb. It happened over a G in the cantus firmus, which I'd argue should suggest an Eb.major chord to you, which is the relative major of C.major's parallel minor key. It's not a G.minor. Don't tell me it's a G.minor. Ooh, and now I've gotten an Ab over F that's bearable. Maybe we could read that as an F.minor, another parallel borrowing.

There's, um - I don't know if it's a bug in my code or if it's a psycho-acoustic phenomenon I didn't account for, but right now my software synth get *way louder* on higher notes, and it reminds me of that famous performance of the 20th Century Fox theme on kazoo. I definitely have to do something about that before I show you my code or any rendered melodies.

...

No comments:

Post a Comment