Jump to content

Custom tuning and MIDI rendering

From LilyPond wiki

For custom tuning in MIDI output, you define both the pitches of the basic scale (c d e f) and the pitch change for each alteration. Both the basic scale and the alteration contribute to the pitch adjustment in the MIDI output.

MIDI implements tuning as a "pitch-bend" adjustment from equal-temperament, changing with every note. Each MIDI channel has its own pitch-bend. You can put each voice on a separate MIDI channel, so each voice can have an independent pitch-bend.

You can use Scheme expressions to generate the pitches and alterations, or you can type the pitches as a list of numbers, in terms of cents or semitones.

Here is an example where you can hear the difference between quarter-comma meantone, 24-tone equal-temperament, etc.

\version "2.24.0"

%% http://lsr.di.unimi.it/LSR/Item?id=785

% The tempered fifth in quarter-comma meantone is (from any mathematical
  %  music theory reference book) 0.5805 of an octave, which is 696.6 cents
FIFTH = # 5805/10000  % or equivalently, # 6966/12000
  % 31-equal-temperament has
  %  FIFTH = # 18/31
  % 12-equal-temperament has
  %  FIFTH = # 7/12
  % 53-equal-temperament has
  %  FIFTH = # 31/53
  %
  % When you experiment, stay within the range 1/2 < FIFTH < 3/5
  % so that two fifths <c d'> is more than an octave,
  % and five fifths <c b''> is less than three octaves.
  %
#(define fracpart (lambda (x) (- x (floor x))))
#(define tones-from-c (lambda (nfifths) (* 6 (fracpart (* FIFTH nfifths)))))
  %
  % This is a list of pitches, in units of an equal tempered whole tone,
  %  for the notes c d e f g a b, relative to c
scalepitches = #(list->vector (map tones-from-c '(0 2 4 -1 1 3 5)))

  % One may skip the Scheme code and simply enter the pitches in cents.
  % For quarter-comma meantone, these can be found in a book:
  % scalepitches = ##(0 193/200 386/200 503/200 697/200 890/200 1083/200)

#(ly:set-default-scale (ly:make-scale scalepitches))

%% LSR-editor's remark
%% To avoid bleed over we not re-set/define SHARP, FLAT, DOUBLE-FLAT and 
%% DOUBLE-SHARP, but use different identifiers --Harm

#(define MSHARP (tones-from-c 7))
  % Alternatively, for quarter-comma meantone, MSHARP = # 76/200
#(define MFLAT (- MSHARP))
#(define DOUBLE-MSHARP (* MSHARP 2))
#(define DOUBLE-MFLAT (* MFLAT 2))

%% The former 2.18.-code:
%%    % Reload the note names (c, cis, des, d, etc.) ...
%%    #(ly:load "define-note-names.scm") \language "nederlands"
%% does not work in 2.20.
%% Thus we redefine 'language-pitch-names', 'note-names-language' and 'language'

#(define language-pitch-names
  (map
    (lambda (language)
      (cons 
        (car language)
        (map
          (lambda (pitch) 
            (cons 
              (car pitch)
              (ly:make-pitch
                (ly:pitch-octave (cdr pitch))
                (ly:pitch-notename (cdr pitch))
                (let ((alt (ly:pitch-alteration (cdr pitch))))
                  (case alt
                    ((-1/2) MFLAT)
                    ((1/2) MSHARP)
                    ((1) DOUBLE-MSHARP)
                    ((-1) DOUBLE-MFLAT)
                    (else alt))))))
          (cdr language))))
    language-pitch-names))
  
#(define-public (note-names-language str)
  "Select note names language."
  (let ((alist (assoc-get (string->symbol str)
                          language-pitch-names
                          '())))
    (if (pair? alist)
        (begin
          (ly:debug "Using `~a' note names..." str)
          (set! pitchnames alist)
          (ly:parser-set-note-names alist))
        (ly:warning "Could not find language `~a'.  Ignoring." str))))

language =
#(define-void-function (language) (string?)
   "Set note names for language @var{language}."
   (note-names-language language))

\language "nederlands"

  % and the table of glyphs for alterations ...
alterationList = #`(
  (,NATURAL . "accidentals.natural")
  (,MFLAT . "accidentals.flat")
  (,MSHARP . "accidentals.sharp")
  (,DOUBLE-MSHARP . "accidentals.doublesharp")
  (,DOUBLE-MFLAT . "accidentals.flatflat")
)
  % and the table of alterations in any modes (major, minor, dorian) you use
minor = #`(
  (0 . ,NATURAL)
  (1 . ,NATURAL)
  (2 . ,MFLAT)
  (3 . ,NATURAL)
  (4 . ,NATURAL)
  (5 . ,MFLAT)
  (6 . ,MFLAT)
)
  % so that these tables are indexed to pitches from our tuning.

  % keyAlterationOrder needs to know our pitches to typeset key signatures.
  % Each object that draws accidentals to know the size of our alterations.
\layout{
  \context {
    \Score
  keyAlterationOrder = #`(
    (6 . ,MFLAT) (2  . ,MFLAT) (5 . ,MFLAT) (1  . ,MFLAT) (4  . ,MFLAT) (0  . ,MFLAT) (3  . ,MFLAT)
    (3 . ,MSHARP) (0 . ,MSHARP) (4 . ,MSHARP) (1 . ,MSHARP) (5 . ,MSHARP) (2 . ,MSHARP) (6 . ,MSHARP)
    (6 . ,DOUBLE-MFLAT) (2 . ,DOUBLE-MFLAT) (5 . ,DOUBLE-MFLAT ) (1 . ,DOUBLE-MFLAT) (4 . ,DOUBLE-MFLAT) (0 . ,DOUBLE-MFLAT) (3 . ,DOUBLE-MFLAT)
    (3 . ,DOUBLE-MSHARP) (0 . ,DOUBLE-MSHARP) (4 . ,DOUBLE-MSHARP) (1 . ,DOUBLE-MSHARP) (5 . ,DOUBLE-MSHARP) (2 . ,DOUBLE-MSHARP) (6 . ,DOUBLE-MSHARP)
    )
    \override KeySignature.alteration-glyph-name-alist = \alterationList
    \override Accidental.alteration-glyph-name-alist = \alterationList
    \override AccidentalCautionary.alteration-glyph-name-alist = \alterationList
    \override TrillPitchAccidental.alteration-glyph-name-alist = \alterationList
    \override AmbitusAccidental.alteration-glyph-name-alist = \alterationList
  }
}
  % MIDI implements microtones as a pitch bend, with one bend per channel.
  % These lines below assign one channel to each Voice, in case there are
  % multiple voices on a staff, so that each voice can have its correct
  % pitch bend.
\midi {
  \context {
    \Staff
    \remove "Staff_performer"
    \remove "Key_performer" % avoid midi bug, issue 748
  }
  \context {
    \Voice
    midiInstrument = "drawbar organ"
    \consists "Staff_performer"
  }
  \tempo 4 = 30
}

  % The end of a Chorale from Bach's St Matthew Passion.
  %
  % The original choral music, in B-minor, involves notes such as
  % E-sharp and D-sharp that do not exist on an organ, those keys
  % being tuned to F and E-flat, respectively.
  %
  % Therefore we transpose to D minor to make it playable on the organ.
  %
\score { 
  \new PianoStaff \with { instrumentName = "Organ" } \transpose b d' <<
    \partial 4
    \new Staff \relative c'' {
      \key b\minor
      s1*0^\markup\huge"transposed from St. Matthew Passion, J. S. Bach"
      << {
        d4 | cis  b  e  d8 cis | cis2 b
      } \\ {
        fis4 | e8 fis gis ais b4 b | b ais fis2
      } >>
    }
    \new Staff \relative c' {
      \key b\minor
      s1*0_\markup { \line { $(format #f "Fifths tempered by ~,1F cents relative to perfect fifth"
      (* (- FIFTH 0.5849625) 1200)) }}
      << {
        \clef bass
        a8 b | cis dis e4 b8 cis d4 | gis, cis dis2
      } \\ {
        fis,8 gis | a4 gis g fis | eis fis b,2
      } >>
    }
  >>
  \layout{}
  \midi{}
}