Hi,

About enharmonicity:

2011/6/20 Benkő Pál <benko....@gmail.com>:
>
> with an algebraic pitch representation the test and adjustment would
> look like as follows:
> two pitches are enharmonic iff their interval is in the subspace spanned
> by the enharmonic interval (default: diminished second) and the scale
> period (default: pure octave).  to make it a bit more difficult, the
> scale period coefficient must be integer, while the enharmonic interval
> coefficient may be rational.  the scale period coefficient also gives the
> necessary adjustment.
>

Enharmonicity is just an equivalence relation respecting the abelian
group structure of intervals/transpositions. You can represent it by a
quotient map or by its kernel.

Your approach is representing the kernel, via its generators. This
is complicated.

A much simpler idea is to represent the quotient map, which is a
particularly simple kind of function.

I am attaching a modified version of your code which expresses the
idea. I believe that the code can get even cleaner, but I am leaving
that work to you (or someone else), since I have a very low fluency
in Lisp.

Explaining the code:
{enharmonic-class} corresponds to the quotient map. {et12-class}
and {et31-class} are examples. The important thing is that these
procedures return the same value for enharmonic equivalent pitches
modulo octave.

{enharmonic-octaves} is used in the octave adjustment. It is supposed
to receive a pitch which is enharmonic to an integer number of octaves,
and return that number.

Note how {enharmonic} became trivial. This means that
{enharmonic-from} reduces to a dictionary look-up. I leave to someone
else to reimplement this in the best possible way.


Hope it helps.
If you have any questions, please ask.

Regards,
Felipe
\version "2.15.3"


#(define (extract-pitch-sequence music)
  "Recurse through @var{music}, extracting pitches.
Returns a list of pitch objects, e.g
@code{'((ly:make-pitch 0 2 0) (ly:make-pitch 0 4 0) ... )}
Typically used to construct a scale for input to transposer-factory
(see).
"

  (let ((elements (ly:music-property music 'elements))
	(element (ly:music-property music 'element))
	(pitch (ly:music-property music 'pitch)))

    (cond
     ((ly:pitch? pitch)
      pitch)

     ((pair? elements)
      (map
       (lambda (x) (extract-pitch-sequence x))
       elements))

     ((ly:music? element)
      (extract-pitch-sequence element)))))

#(define (enharmonic pitch0 pitch1 enharmonic-class)
  "Whether two pitches are enharmonic."
  (equal? (enharmonic-class pitch0) (enharmonic-class pitch1)))

%TODO: implement this with a dictionary.
#(define (enharmonic-from scale pitch enharmonic-class enharmonic-octaves)
  "The first enharmonic equivalent of pitch from scale."
  (if (pair? scale)
      (let ((pitch1 (caar scale)))
	(if (enharmonic pitch pitch1 enharmonic-class)
	    ; adjust octave of pitch1
	    (ly:pitch-transpose
	     pitch1 (ly:make-pitch
		     (enharmonic-octaves (ly:pitch-diff pitch pitch1))
                     0 0))
	    (enharmonic-from (cdr scale) pitch enharmonic-class enharmonic-octaves)))
      pitch))

#(define (enharmonize-pitch pitch scale enharmonic-class enharmonic-octaves)
  (let ((p (ly:music-property pitch 'pitch)))
    (if (ly:pitch? p)
	(ly:music-set-property!
	 pitch 'pitch
	 (enharmonic-from scale p enharmonic-class enharmonic-octaves)))
    pitch))

#(define (enharmonize scale music enharmonic-class enharmonic-octaves)
  "Replaces pitches in music by their enharmonic counterpart in scale."
  (let ((s (extract-pitch-sequence scale)))
    (music-map (lambda (x) (enharmonize-pitch x s enharmonic-class enharmonic-octaves)) music)))

enharmonizeMusic =
#(define-music-function (parser location scale music enharmonic-class enharmonic-octaves)
    (ly:music? ly:music? procedure? procedure?)
    (_i "Replace pitches in @var{music} by their enharmonic counterpart in @var{scale}.")
    (enharmonize scale music enharmonic-class enharmonic-octaves))

#(define (frac x) (- x (floor x)))

#(define (alg-alteration pitch)
  "Second coefficient of an algebraic pitch representation modulo octave," 
  " the first one being (ly:pitch-notename pitch)"
  (+ (ly:pitch-alteration pitch)
     (if (> (ly:pitch-notename pitch) 2)
         -1/2 0)))

#(define (et12-value pitch)
  "Auxiliary."
  (* 1/6 (+ (ly:pitch-notename pitch) (alg-alteration pitch))))

#(define (et12-class pitch)
  "pitch modulo octave in ET12, represented as a fraction of the octave"
  (frac (et12-value pitch)))

#(define (et12-octaves pitch)
  "If pitch is equivalent to an integer number of octaves in ET12,"
  " returns that value. Otherwise, return value is unspecified."
  (+ (ly:pitch-octave pitch)
     (et12-value pitch)))

#(define (et31-value pitch)
  "Auxiliary."
  (+ (* 5/31 (ly:pitch-notename pitch)) (* 4/31 (alg-alteration pitch))))

#(define (et31-class pitch)
  "pitch modulo octave in ET31, represented as a fraction of the octave"
  (frac (et31-value pitch)))

#(define (et31-octaves pitch)
  "If pitch is equivalent to an integer number of octaves in ET31,"
  " returns that value. Otherwise, return value is unspecified."
  (+ (ly:pitch-octave pitch)
     (et31-value pitch)))

esMinor = { es f ges as bes ces des }
mus = { dis' feses' c'' aisis' }
mu = { bisis' feses' }

{
  \key es \minor
  \mus
  \enharmonizeMusic \esMinor \mus #et12-class #et12-octaves
  \enharmonizeMusic \esMinor \transpose es es' \mus #et12-class #et12-octaves
  \enharmonizeMusic \esMinor \transpose es' es \mus #et12-class #et12-octaves

  % example for 31-step equal temperament
  \mu
  \enharmonizeMusic
    { deses cis des cisis eses dis es disis } \mu #et31-class #et31-octaves
}
_______________________________________________
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel

Reply via email to