lilypond-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: transpose pitch by semitones


From: David Kastrup
Subject: Re: transpose pitch by semitones
Date: Tue, 01 Dec 2015 16:09:52 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/25.0.50 (gnu/linux)

Gilles THIBAULT <address@hidden> writes:

>> OK, this one is more straightforward...
> ...but your idea was probably the best :
>
> %%%%%%%%%%%%%%%%%%%%
> #(define (semitones->pitch semitone)
>    (let ((index (modulo semitone 12))
>          (octave (quotient semitone 12)))
>      (apply ly:make-pitch (cons
>       octave
>       (list-ref
>        '((0 0)   ; c
>           (0 1/2) ; cis
>           (1 0)   ; d
>           (1 1/2) ; dis
>           (2 0)   ; e
>           (3 0)   ; f
>           (3 1/2) ; fis %  \jiPitch 2 1
>  
>           (4 0)   ; g
>           (4 1/2) ; gis
>           (5 0)   ; a
>           (5 1/2) ; ais
>           (6 0))   ; b     
>        index)))))

Well, that could be

    (apply ly:make-pitch octave
      (vector-ref #((0 0) (0 1/2) (1 1/2) ...) index)))))

There is no need to use the inefficient `list-ref' here since a vector
works just fine.  And there is no point in putting `octave' into a list
just to have `apply' unpack it again.

At any rate, this scheme does not choose harmonically useful notes: it's
very straightforward.

My Emacs input mode does the pitch assignment from MIDI a bit more
versatily (this is Elisp, so it would need some amendment for working in
GUILE, but that should not be too hard to do).

;; default notenames and modifications:
;;
;; The assignment to Midi names is done using the following table that
;; tries to accomodate some common chords in the C major scale but
;; also melodic A minor.  This does not behave all that robustly under
;; modulation.
;;
;; TODO: should we have a more complete picture of the signature or
;; current mode?  Conversion to notenames may depend on lead tones and
;; lead harmonies, and those are different for c \major and a \minor,
;; for example.

(defconst lily-defaultmidi-names
  [0 0 1 2 2 3 3 4 4 5 6 6]
  "Default assignment of notenames to semitones.")

(defconst lily-defaultmidi-scale
  [0 2 4 5 7 9 11]
  "Semitone offsets for the various notenames.")

(defun lily-midi-to-note (note &optional sharps)
  "Convert NOTE under key SHARPS to an (octave note accidental) triple."
  ;; the trick here with regard to sharps is that we transpose the
  ;; index into `lily-defaultmidi-names' by `sharps' chromatic
  ;; fourths, and transpose the result down again by `sharps' diatonic
  ;; fourths.  We ignore the octaves and accidentals and fix them up
  ;; at the end.
  (let* ((sharps (or sharps lily-midi-keysig 0))
         (inkey (aref lily-defaultmidi-names
                      (mod (+ note (* sharps 5)) 12)))
         (name (mod (+ inkey (* sharps 4)) 7))
         (nameoff (aref lily-defaultmidi-scale name))
         (acc (- (mod (- note nameoff 6) 12) 6))
         (oct (/ (- note nameoff acc 60) 12)))
    (list oct name (* acc 0.5))))


One thing to note here is that the resulting octave depends on the key
of conversion: bis'' and c' can represent the same semitone in different
keys and have a different octave.

This works by first figuring out the proper note name to arrive at.
Then the normal semitone associated with that is looked up, and the
difference makes up both octave and resulting accidental.

The same sequence of semitones can thus lead to different notes
depending on the key of conversion.

lily-defaultmidi-names codifies when to use a flat and when to use a
sharp in C major.  Maybe it should not be a defconst.

-- 
David Kastrup



reply via email to

[Prev in Thread] Current Thread [Next in Thread]