best normal form

Rick Taube taube@uiuc.edu
Wed, 5 Nov 1997 11:55:33 -0500 (EST)


here is a function that returns the Best Normal Form of some specified
notes.


? (normal-form '( f4 e4 ef5 bf4))

(DS5 E5 F5 AS5)

(0 1 2 7)


-----


(defun normal-form (notes)

  (flet ((ints (l &aux (f (first l)))

           (loop for i in l collect (- i f))))

    (let ((length (length notes))

          (width most-positive-fixnum)

          (names? (and notes (symbolp (first notes))))

          norml ints)

      

      ;; coerce to degrees ordered low to high

      (setf notes (sort (if names? (mapcar #'degree notes) notes)

                        #'<<))

      

      ;; reduce to one octave, resort if necessary

      (loop with resort? = nil

            with f = (first notes)

            for i below length

            for d = (- (elt notes i) f)

            do 

            (unless (<< d 12)

              (setf (elt notes i) (+ f (mod d 12))

                    resort? t))

            finally 

            (when resort? (setf notes (sort notes #'<<))))

      

      ;; find the best normal form, ie the rotation with the

      ;; smallest span between the first and last notes and

      ;; the smallest total interval content.

      (loop with f and l and d

            repeat length

            do

            (setf f (first notes) l (first (last notes)) d (- l f))

            (cond ((<< d width) 

                   (setf width d norml notes ints (ints notes)))

                  ((and (= d width)

                        (<< (apply #'+ (ints notes))

                           (apply #'+ ints)))

                   (setf norml notes ints (ints notes))))

            ;; traspose first note above last and append

            (loop while (<< f l) do (incf f 12))

            (setf notes

                  (loop for x in (cdr notes)

                        collect x into r

                        finally (return (append r (list f)))))) 

      ;; returns notes in best normal order and interval set.

      (values (if names? (mapcar #'note norml) norml)

              ints))))