Just Intonation – Semantic Encoding with LilyPond

Recently we have discussed LilyPond’s capabilities for engraving contemporary music. It seems many composers consider extended notation with the computer a daunting task and prefer using graphical approaches for it, like drawing tools in graphical programs or even post-processing scores in graphics programs like Inkscape or Illustrator. However, while this may seem straightforward, I’ve always felt it’s conceptually inferior, mostly because this approach is a one-way street: once you have edited the resulting PDF in a drawing program you can’t ever go back to editing the content of the score. LilyPond gives you the option of encoding what you mean, and while it is admittedly a complex task to create extended notation with it, I definitely think this feature alone makes it worth the trouble. Of course you have to consider that a composer doesn’t necessarily have to do the programming himself, there will usually be generous help from the community. And developing a library specifically addressing contemporary notation needs will (hopefully) be an extremely powerful asset for the LilyPond toolkit.

In two posts I’m going to demonstrate you one example that clearly makes this point – encoding and displaying Just Intonation.

Just Intonation – Some Background

One of the composers involved in the mentioned discussion uses “Just intonation”, actually a very old concept but one that became fruitful in composition (again) only recently in a tendency of composition called spectralism. This is a concept about pitch and harmony that is fundamentally different from what we are used to in Western music, but I can of course only touch the surface of it here. One main aspect is that the basis for pitches is not the octave and an arbitrary equal division of it, be it the common twelve semitones or other systems that divide the whole tone in three, four, six or eight subdivisions – or the octave in an arbitrary number of steps, for example 19 (see Steve Altoft’s website for an example of practical use). Instead, just intonation retrieves its pitch material from a fundamental tone and its harmonics. Probably you all know this scale:

Idealized harmonic scale (click to enlarge)

Idealized harmonic scale (click to enlarge)

If we take the c (in LilyPond terminology) as the fundamental, this represents the ratios of 1/1, 2/1, 3/1 throughout 8/1, in other words the first eight partials of the harmonic series. However, many of these harmonics are not quite the same as the shown pitches of the tempered scale we commonly use in Western music. Notating them like in the above example is actually a pretty fishy compromise, and composers have struggled a lot about how to deal with the issue. One common approach is to use the tempered pitch and add a textual information giving the deviation in “cent” – which is an equal division of a semitone in 100, or of an octave in 1.200 steps. The above example would thus be notated as:

Harmonic series with cent deviations (click to enlarge)

Harmonic series with cent deviations (click to enlarge)

Another example is a kind of scale over the middle c, using consecutive ratios of 10/9, 9/8, 8/7 down to 2/1. This “scale” could look like this:

Consecutive ratios (click to enlarge)

Consecutive ratios (click to enlarge)

If you think that this can be applied to arbitrary fundamental pitches you can see that just intonation has a distinct – and extraordinarily rich – harmonic and melodic repertoire to draw from.

Implementing Just Intonation in LilyPond

OK, all of this could be notated without much difficulties with LilyPond’s regular tools, and also with other notation programs – simply add a text with the cent deviation and/or notate it like regular string harmonics. But what if LilyPond were able to let you encode pitches through what they are – harmonics over fundamentals – and properly visualize it in a configurable manner, automatically doing the right thing? Well, this is what we are going to see now. There will be a long way until it is usable in real-world scores, actually it’s just a proof-of-concept worth an afternoon’s work. But it clearly shows where this might lead to and why text based tools are inherently more powerful than their graphical competitors.

A User Interface

What do we want? We want to encode pitches as harmonics over a fundamental, so we need to provide a base pitch, a ratio, and optionally a duration. For our example we will implement all this as a “music function” (see an earlier post) for more in-depth information). Integrating just intonation so we can use it like regular pitches would be nice but this is out of scope for the proof-of-concept. Another decision I made is that the fundamental note is not given to that function but has to be set separately – otherwise we’d have to pass it to each pitch individually.

We start with a dummy interface function to see what we’re going to achieve:

% A dummy interface for notating pitches in just intonation
jiPitch =
#(define-music-function (dur ratio)
   ((ly:duration?) fraction?)
   #{ #})

{
  \jiPitch 3/2
  \jiPitch 2 4/3
}

This function expects an optional duration and a fraction as its arguments and for now returns an empty music expression. Compiling this snippet returns a score with only the first c', demonstrating that the interface works.

Initially we will have our resulting pitches as ratios over the middle c' – which is Lilypond’s “zero” pitch. Supplying the 3/2 and 4/3 ratios should result in – approximately – g' and f', the perfect fifth and fourth over the middle c. What we want to achieve is LilyPond notating the closest pitch in tempered tuning plus a text specifying the “cent” deviation.

The Basic Calculations

Ratio To Cents

The first thing we do is converting the ratio in a cent value over the fundamental. We expect two values quite near 700 and 500 – the cent correspondences to a fifth and a fourth. The mathematical function is that for any ratio f1/f2 the corresponding cent value I is I = 1200 * ln (f1/f2) / ln(2), which can easily be expressed in a Scheme function. As we are interested in the step and not in the total cent value we do the multiplication with 12 instead of 1200. This will result in a floating point number whose integer and fractional parts represent the chromatic steps and the (per)cent portion:

% Take the ratio of a partial and return the corresponding cent value
% relative to a fundamental
#(define (ratio->cent ratio)%
   (* 12 (/ (log ratio) (log 2))))

This gives us the expected results:

(ratio->cent 3/2) => 7.01955000865387
(ratio->cent 4/3) => 4.98044999134612

Steps and deviation

In order to display the resulting pitch and deviation we need to split that number into its integer and (processed) fractional parts. We want to round the deviation to be expressed in simple cent values – and we want to display the closest pitch, thus flipping around some values when the cent deviation is greater than 50. I won’t go into details about these Scheme functions as this is not a Scheme tutorial. But actually it’s quite basic arithmetics:

% Take a fraction and return a list with 
% - the pitch in semitones
% - the cent deviation above or below (rounded)
#(define (ratio->step-deviation ratio)
   (let*
    ;; calculate cent value over the fundamental
    ((step-cent (ratio->cent ratio))
     ;; split that in the step and the cent part
     (step (inexact->exact (round step-cent)))
     (cent-deviation (inexact->exact (round (* 100 (- step-cent step))))))
    (cons step cent-deviation)))

Now we get

(ratio->step-deviation 3/2) => (7 . 2)
(ratio->step-deviation 4/3) => (5 . -2)

correctly indicating 2 cent above and below the 7th and 5th semitones.

Prospect

Basically we now have all the information we need for properly displaying pitches specified in just intonation. In the second post we’ll see a few appraoches how we can do that to provide automatic rendering of just intonation pitches in LilyPond. Stay tuned …

5 thoughts on “Just Intonation – Semantic Encoding with LilyPond

  1. David Kastrup

    The whole calculation looks rather error-prone because it tries to calculate the step and the deviation in different ways and independently.

    What’s wrong with:

    #(define (ratio->step-deviation ratio)
       (let*
        ;; calculate cent value over the fundamental
        ((step-cent (ratio->cent ratio))
         ;; split that in the step and the cent part
         (step (inexact->exact (round step-cent)))
         (cent-deviation (inexact->exact (round (* 100 (- step-cent step))))))
        (cons step cent-deviation)))

    Well, apart from the quirky variable names…

    Reply
    1. Urs Liska

      Nothing’s wrong with that.
      I’d rather say this was just one parenthesis too much for me to digest …

      I have updated the post and keep the old function here for reference (to have context for your comment):

      % Take a fraction and return a list with 
      % - the pitch in semitones
      % - the cent deviation above or below (rounded)
      #(define (ratio->step-deviation ratio)
         (let*
          ;; calculate cent value over the fundamental
          ((step-cent (ratio->cent ratio))
           ;; split that in the step and the cent part
           (step (inexact->exact (round step-cent)))
           (cent (inexact->exact (round (* 100 (- step-cent (floor step-cent))))))
           ;; if cent > 50 flip it around to be negative
           (cent-deviation
            (if (> cent 50)
                (- cent 100)
                cent)))
          (cons step cent-deviation)))

      Thanks for the improvement.

      Reply
  2. David Kastrup

    By the way, (/ (numerator ratio) (denominator ratio)) is a rather complicated way to write ratio in the ratio->cent function.

    Reply
    1. Urs Liska

      OMG. Don’t know through which detours I arrived at that code and didn’t notice the redundancy. Thanks for spotting, I have updated the post.

      Reply
  3. Graham Breed

    As discussed in another place, I have code very similar to this, and good enough for full scores. It also took more than afternoon of work. This is where I keep the code:

    https://bitbucket.org/x31eq/microlily

    I never worked out how to generate a note directly in Scheme, but I like the idea of specifying a pitch and then altering it anyway. It means you have a bit more control over how the pitch will be displayed. Otherwise, the “ji12” is much like you describe, but with lots of hashes that I don’t think you need any more.

    It still works for me in 2.18.2 but others have reported problems in Windows. I haven’t looked into them. It could be as simple as the line endings on text files (I don’t know what else should be different).

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *