Mercurial > hg > amuse
changeset 63:32314fefc706
Moved amuse-harmony to new package
darcs-hash:20070628135353-f76cc-be6cba65bf7ba97bae481ab6630880d5707e8c7a.gz
author | David Lewis <d.lewis@gold.ac.uk> |
---|---|
date | Thu, 28 Jun 2007 14:53:53 +0100 |
parents | 9cdd79c34442 |
children | c8f1b0ab0007 |
files | amuse.asd utils/harmony/chord-labelling.lisp utils/harmony/classes.lisp utils/harmony/evaluation.lisp utils/harmony/gamma.lisp utils/harmony/methods.lisp utils/harmony/package.lisp utils/package.lisp utils/utils.lisp |
diffstat | 9 files changed, 1 insertions(+), 2013 deletions(-) [+] |
line wrap: on
line diff
--- a/amuse.asd Thu Jun 28 08:04:20 2007 +0100 +++ b/amuse.asd Thu Jun 28 14:53:53 2007 +0100 @@ -18,15 +18,7 @@ ((:file "package") (:file "utils" :depends-on ("package")) (:file "n-grams" :depends-on ("package")) - (:file "midi-output" :depends-on ("package" "utils")) - (:module harmony - :depends-on ("utils") - :components - ((:file "package") - (:file "classes" :depends-on ("package")) - (:file "gamma" :depends-on ("package")) - (:file "chord-labelling" - :depends-on ("gamma" "package" "classes")))))) + (:file "midi-output" :depends-on ("package" "utils")))) (:module implementations :components ((:module midi
--- a/utils/harmony/chord-labelling.lisp Thu Jun 28 08:04:20 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,684 +0,0 @@ -(in-package #:amuse-harmony) - -;; This file contains functions for performing harmonic analysis and -;; chord labelling. At the moment it's quite crude. -;; -;; Probability can be estimated based on a function that must take a -;; window on the music (i.e. an anchored period and a composition (? -;; or perhaps a 'constituent' in future?). The functionality below is -;; a cut-down version of its predecessors and only models one pitch -;; model, derived by combining dirichlet distributions on the local -;; distribution of pitch-class durations in terms of -;; chord-note:non-chord-note ratios and relative weighting of chord -;; notes. -;; -;; * Chord objects contain details of chord types including the -;; intervals of their constituents and any putative distributional -;; information or note profiles or templates. -;; -;; * Chordset objects gather chord-types together for a given -;; experiment. They have a slot for priors for historical reasons, but -;; at the moment this is unused - I'm using other structures for this. -;; -;; * likelihoods are currently alists with a host of methods. (FIXME: -;; this doesn't seem very clever) -;; - -;; FIXME: this is in the wrong place -(defparameter *keys* (make-array 12 :initial-contents '(:c :c# :d :eb :e :f :f# :g :ab :a :bb :b))) - -(defparameter *path-options* - ;; Each of these is a set of division-of-the-bar options for each - ;; metrical type. - ;; - ;; FIXME: behaviour if the time-signature numerator is absent from - ;; the alist is undefined. - '((4 (1 1 1 1) (1 1 2) (1 2 1) (1 3) - (2 1 1) (2 2) (3 1) (4)) - ;; (4 (1 1 1 1) (1 1 2) (2 1 1) (2 2) (4)) - (2 (1 1) (2)) - (3 (1 1 1) (1 2) (2 1) (3)) - (6 (3 3) (6)) - (5 (1 1 1 1 1) (1 1 1 2) (1 1 2 1) (1 1 3) - (1 2 1 1) (1 2 2) (1 3 1) (1 4) - (2 1 1 1) (2 1 2) (2 2 1) (2 3) - (3 1 1) (3 2) (4 1) (5)) - (9 (3 3 3) (3 6) (6 3) (9)) - (12 (3 3 3 3) (3 3 6) (3 6 3) (3 9) - (6 3 3) (6 6) (9 3) (12)))) - -#+nil -(defparameter *default-models* '(:constant-prior :gamma)) -#+nil -(defparameter *default-models* '(:scaled-prior :gamma :naive-bass)) -;; #+nil -(defparameter *default-models* '(:scaled-prior :gamma)) -#+nil -(defparameter *default-models* '(:scaled-prior :gamma :metrical-prior)) - -;;; ACCESSORS -;; Nearly empty now. And not much point in what's left -(defgeneric normalised-distribution (chord &optional total)) -(defmethod normalised-distribution ((chord chord) &optional (total 1)) - ;; normalised distributions will be reused, so it makes sense to - ;; store them. - ;; FIXME: Are these ever going to be useful again? - (unless (assoc total (slot-value chord 'normalised-distribution)) - (setf (slot-value chord 'normalised-distribution) - (acons total (normalise-vector (slot-value chord 'distribution) total) - (slot-value chord 'normalised-distribution)))) - (cdr (assoc total (slot-value chord 'normalised-distribution)))) - -;;; Object definitions - -#+nil -(defparameter *major-ratios* (mapcar (lambda (x) (/ x 217)) - '(180 1 1 1 20 1 1 8 1 1 1 1))) -#+nil -(defparameter *major-ratios* (mapcar (lambda (x) (/ x 20)) - '(5 1 2 1 3 1 1 2 1 1 1 1))) -(defparameter *major-ratios* #(0.72 0.02 0.02 0.02 0.08 0.02 0.02 0.02 0.02 0.02 0.02 0.02)) -#+nil -(defparameter *minor-ratios* (mapcar (lambda (x) (/ x 302)) - '(280 1 1 4 1 1 1 9 1 1 1 1))) -(defparameter *minor-ratios* #(0.78 0.02 0.02 0.02 0.02 0.02 0.02 0.02 0.02 0.02 0.02 0.02)) -#+nil -(defparameter *minor-ratios* (mapcar (lambda (x) (/ x 20)) - '(7 1 2 2 1 1 1 1 1 1 1 1))) -#+nil -(defparameter *sus-ratios* (mapcar (lambda (x) (/ x 21)) - '(10 1 1 1 1 1 1 1 1 1 1 1))) -(defparameter *sus-ratios* #(0.78 0.02 0.02 0.02 0.02 0.02 0.02 0.02 0.02 0.02 0.02 0.02)) - -(defparameter *dim-ratios* (copy-seq *sus-ratios*)) -(defparameter *aug-ratios* (copy-seq *sus-ratios*)) - -;; Chords -(defparameter *major-chord* - (make-instance 'chord - :label :major - :notes '(0 4 7) - :bass-likelihoods (make-array 12 - :initial-contents *major-ratios*) - :min-distribution (make-array 4 :initial-contents '(35 25 20 20)) - :distribution (make-array 12 - :initial-contents '(6 1 2 1 5 2 1 5 1 2 2 2)))) - -(defparameter *minor-chord* - (make-instance 'chord :label :minor :notes '(0 3 7) - :min-distribution (make-array 4 :initial-contents '(35 25 20 20)) - :bass-likelihoods (make-array 12 - :initial-contents *minor-ratios*) - :distribution (make-array 12 - :initial-contents '(6 1 2 5 1 2 1 5 2 1 2 1)))) - -(defparameter *diminished-chord* - (make-instance 'chord :label :dim :notes '(0 3 6) - :min-distribution (make-array 4 :initial-contents '(35 25 20 20)) - :bass-likelihoods (make-array 12 - :initial-contents *dim-ratios*) - :distribution (make-array 12 - :initial-contents '(6 1 1 5 1 1 5 1 1 4 1 1)))) - -(defparameter *diminished-chord-short* - (make-instance 'chord :label :dim :notes '(0 3 6 9) - :min-distribution (make-array 4 :initial-contents '(35 25 20 20)) - :distribution (make-array 3 - :initial-contents '(6 1 1)))) - -(defparameter *augmented-chord* - (make-instance 'chord :label :aug :notes '(0 4 8) :min-distribution #(35 25 20 20) - :bass-likelihoods (make-array 12 - :initial-contents *aug-ratios*) - :distribution (make-array 12 - :initial-contents '(6 1 1 1 5 1 1 1 5 1 1 1)))) - -(defparameter *augmented-chord-short* - (make-instance 'chord :label :aug :notes '(0 4 8) - :distribution (make-array 4 - :initial-contents '(6 1 1 1)))) - -(defparameter *suspended4th-chord* - (make-instance 'chord :label :sus4 :notes '(0 5 7) - :min-distribution (make-array 4 :initial-contents '(35 25 20 20)) - :bass-likelihoods (make-array 12 - :initial-contents *sus-ratios*) - :distribution (make-array 12 - :initial-contents '(6 1 2 3 3 5 1 5 1 1 2 2)))) - -(defparameter *suspended9th-chord* - (make-instance 'chord :label :sus9 :notes '(0 2 7) - :min-distribution (make-array 4 :initial-contents '(35 25 20 20)) - :bass-likelihoods (make-array 12 - :initial-contents *sus-ratios*) - :distribution (make-array 12 - :initial-contents '(6 1 5 2 2 2 1 5 1 2 2 1)))) - -;; CHORDSETS -(defparameter *full-set* - (make-instance 'chordset :chords (list *major-chord* *minor-chord* - *diminished-chord* *augmented-chord* - *suspended4th-chord* *suspended9th-chord*))) - -(defparameter *full-set-variable-length* - (make-instance 'chordset :chords (list *major-chord* *minor-chord* - *diminished-chord-short* *augmented-chord-short* - *suspended4th-chord* *suspended9th-chord*))) - -(defparameter *partial-set* - (make-instance 'chordset :chords (list *major-chord* *minor-chord* - *diminished-chord* *augmented-chord*))) - -(defparameter *partial-set-variable-length* - (make-instance 'chordset - :chords (list *major-chord* *minor-chord* - *diminished-chord-short* - *augmented-chord-short*))) - -(defparameter *minimal-set* - (make-instance 'chordset :chords (list *major-chord* *minor-chord*))) - -#+nil -(defparameter *chord-proportions* ;; guess - (list (cons *major-chord* 17/30) (cons *minor-chord* 10/30) - (cons *diminished-chord* 1/60) (cons *augmented-chord* 1/60) - (cons *suspended4th-chord* 1/30) (cons *suspended9th-chord* 1/30))) - -#+nil -(defparameter *chord-proportions* ;; another guess - (list (cons *major-chord* 1/3) (cons *minor-chord* 1/3) - (cons *diminished-chord* 1/30) (cons *augmented-chord* 1/60) - (cons *suspended4th-chord* 1/5) (cons *suspended9th-chord* 1/12))) -#+nil -(defparameter *chord-proportions* ;; flat - (list (cons *major-chord* 1/6) (cons *minor-chord* 1/6) - (cons *diminished-chord* 1/6) (cons *augmented-chord* 1/6) - (cons *suspended4th-chord* 1/6) (cons *suspended9th-chord* 1/6))) - -(defparameter *chord-proportions* - ;; observed - ;; FIXME: This seriously impairs dim and aug. Do they ever get - ;; diagnosed now? - (list (cons *major-chord* 546/917) (cons *minor-chord* 312/917) - (cons *diminished-chord* 2/917) (cons *augmented-chord* 1/917) - (cons *suspended4th-chord* 44/917) (cons *suspended9th-chord* 12/917))) - - -;; First steps to chord labelling -(defun get-chord-likelihoods-for-model (anchored-period music - &key (model :gamma) - (chordset *full-set*)) - ;; Currently expects and returns an alist of (identifier - ;; . likelihood) (unnormalised, but can use normalise-likelihoods) - (ecase model - (:constant-prior - ;; results are divided by number of chords in chordset (times 12) - (constant-prior-likelihoods anchored-period - music - chordset)) - (:scaled-prior - ;; results are divided by preset chord weightings (times 12) - (scaled-prior-likelihoods anchored-period - music - chordset)) - (:naive-bass - (naive-bass-prior-likelihoods anchored-period - music - chordset)) - (:metrical-prior - (metrical-prior-likelihoods anchored-period - music - chordset)) - (:gamma - ;; dirichlet-based likelihood calculation - (3ple-gamma-likelihoods anchored-period - music - chordset)))) - -;; LIKELIHOOD-CALCULATION FUNCTIONS -(defgeneric metrical-prior-likelihoods (anchored-period music chordset)) -(defmethod metrical-prior-likelihoods ((anchored-period anchored-period) - music chordset) - (let* ((metrical-level (metrical-level-for-likelihood anchored-period music)) - (p (if (= metrical-level 1) - 0.51 - 0.07))) - (loop for chord in (chords chordset) - nconc (loop for i from 0 to 11 - collect (cons (list i chord) - (/ p (* 12 (length (chords chordset))))))))) - -(defgeneric constant-prior-likelihoods (anchored-period music chordset)) -(defmethod constant-prior-likelihoods ((anchored-period anchored-period) - music chordset) - ;; returns a flat distribution totalling 1 - (loop for chord in (chords chordset) - nconc (loop for i from 0 to 11 - collect (cons (list i chord) - (/ 1 (* 12 (length - (chords chordset)))))))) - -(defgeneric scaled-prior-likelihoods (anchored-period music chordset &key prior-alist)) -(defmethod scaled-prior-likelihoods ((anchored-period anchored-period) music chordset - &key (prior-alist *chord-proportions*)) - ;; returns a distribution based on the relative likelihood of chord types - (loop for chord in (chords chordset) - nconc (loop for i from 0 to 11 - collect (cons (list i chord) - (/ (cdr (assoc chord prior-alist)) - 12))))) - -(defgeneric naive-bass-prior-likelihoods (anchored-period music chordset)) -(defmethod naive-bass-prior-likelihoods ((anchored-period anchored-period) music chordset) - (let ((pc (bass-note anchored-period music))) - (loop for chord in (chords chordset) - nconc (loop for i from 0 to 11 - collect (cons (list i chord) - (/ (aref (bass-likelihoods chord) - (mod (+ i pc) 12)) - (length (chords chordset)))))))) - -(defgeneric 3ple-gamma-likelihoods (anchored-period music chordset)) -(defmethod 3ple-gamma-likelihoods ((anchored-period anchored-period) music chordset) - ;; Ask Christophe about what this one does - this function just - ;; provides data to his dirichlet likelihood functions. Currently - ;; limited to triads, this has two distributions for relative - ;; strengths of chord notes and for the relation between chord and - ;; non-chord notes. - (let ((pitch-classes (normalised-pitch-class-distribution anchored-period music)) - (metrical-level (metrical-level-for-likelihood anchored-period music)) - (likelihoods)) - (dolist (chord (chords chordset) likelihoods) - (let ((chord-likelihoods (subseq (min-distribution chord) 0 3)) - (non-chord (aref (min-distribution chord) 3))) - (loop for offset from 0 to 11 - do (setf likelihoods - (set-likelihood - likelihoods chord offset - (3ple-likelihood (chromatic-rotate pitch-classes (- offset)) - chord-likelihoods - non-chord - (main-notes chord) - metrical-level 1 - (get-alphas chord metrical-level :version :learned) - (get-betas chord metrical-level :version :learned))))))))) - -#+nil -(defun get-alphas (chord metrical-level &key (version :map)) - ;; MP values, map commented - (cond - ((or (eq *major-chord* chord) - (eq *minor-chord* chord)) - (cond - ((< metrical-level 1) - (case version - (:map #(3.7812 2.4955 2.1525)) - (:ml #(4.0398 2.6624 2.2942)) - (:learned #(2.0475 1.365 1.1374999)))) - (t - (case version - (:map #(3.6626 1.5234 2.3395)) - (:ml #(3.9119 1.6193 2.4955)) - (:learned #(2.0475 1.365 1.1374999)))))) - (t - (case version - (:map #(3.5110 2.0252 1.2963)) - (:ml #(4.0822 2.3459 1.4874)) - (:learned #(2.0475 1.365 1.1374999)))))) - -;; New, corrected ground truth -(defun get-alphas (chord metrical-level &key (version :map)) - ;; MP values, map commented - (cond - ((or (eq *major-chord* chord) - (eq *minor-chord* chord)) - (cond - ((< metrical-level 1) - (case version - (:map #(3.7397 2.4923 2.0187)) - (:ml #(3.9434 2.6253 2.1239)) - (:learned #(2.0475 1.365 1.1374999)))) - (t - (case version - (:map #(3.2620 1.3882 2.2542)) - (:ml #(3.5200 1.4889 2.4293)) - (:learned #(2.0475 1.365 1.1374999)))))) - (t - (case version - (:map #(3.1963 1.8187 1.3340)) - (:ml #(3.6371 2.0621 1.2799)) - (:learned #(2.0475 1.365 1.1374999)))))) - -#+nil -(defun get-betas (chord metrical-level &key (version :map)) - (cond - ((eq version :learned) - (cond - ((> metrical-level 1/2) - #(0.97 12)) - ((= metrical-level 1/2) - #(0.97 6)) - (t #(0.97 4)))) - ((or (eq *major-chord* chord) - (eq *minor-chord* chord)) - (if (< metrical-level 1) - (if (eq version :map) - #(0.6987 3.1724) - #(0.7164 3.2640)) - (if (eq version :map) - #(1.3677 5.9215) - #(1.4454 6.2843)))) - (t - (if (eq version :map) - #(0.9358 5.2212) - #(1.0431 5.8530))))) - -;; With new, corrected ground truth -(defun get-betas (chord metrical-level &key (version :map)) - (cond - ((eq version :learned) - (cond - ((> metrical-level 1/2) - #(0.97 12)) - ((= metrical-level 1/2) - #(0.97 6)) - (t #(0.97 4)))) - ((or (eq *major-chord* chord) - (eq *minor-chord* chord)) - (if (< metrical-level 1) - (if (eq version :map) - #(0.7041 3.3448) - #(0.7190 3.4260)) - (if (eq version :map) - #(1.3838 6.4581) - #(1.4872 6.9785)))) - (t - (if (eq version :map) - #(0.9558 5.0847) - #(1.0551 5.6740))))) - - -(defun chromatic-rotate (vector offset) - ;; transpose an n-member (chromatic) vector by an integral number of - ;; steps (semitones) - (let* ((size (length vector)) - (result (make-array size))) - (dotimes (i size result) - (setf (aref result i) (aref vector (mod (- i offset) size)))))) - -(defgeneric metrical-level-for-likelihood (anchored-period music)) -(defmethod metrical-level-for-likelihood (anchored-period (music composition)) - ;; metrical level is a function of time signature and window size - ;; and is used to modify the gamma function. - (let ((time-sigs (get-applicable-time-signatures anchored-period music))) - (cond - ((= (length time-sigs) 1) - (/ (duration anchored-period) - (crotchets-in-a-bar (first time-sigs)))) - ((null time-sigs) - ;; If, for some reason, we have no time-signature, midi specs - ;; say assume 4/4. - (/ (duration anchored-period) 4)) - (t - (loop for sig in time-sigs - sum (/ (duration (period-intersection sig - anchored-period)) - (crotchets-in-a-bar sig))))))) - -;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Hypothesis comparison / level navigation -;; - -(defun chord-labels (anchored-period music - &key (chordset *full-set*) - (models *default-models*)) - (let ((harmonic-analysis (best-level anchored-period music :chordset chordset :models models)) - (best-likelihood) (chord-labels)) - (do ((path (first harmonic-analysis) (cdr path)) - (likelihoods (second harmonic-analysis) (cdr likelihoods))) - ((null path) (reverse chord-labels)) - (dolist (likelihood (car likelihoods)) - (when (or (null best-likelihood) - (> (likelihood-likelihood likelihood) - (likelihood-likelihood best-likelihood))) - (setf best-likelihood likelihood))) - (push (cons (first path) (likelihood-chord best-likelihood)) chord-labels) - (setf best-likelihood nil)))) - -(defun best-level (anchored-period music - &key (chordset *full-set*) - (models *default-models*)) - ;; Takes a period for the largest time-unit being considered and - ;; returns the highest probability subdivision, its likelihood - ;; values (and the probability of that subdivision, but that's a bit - ;; of a coincidence and may want not to happen) - (best-level-hypothesis (make-metrical-divisions anchored-period music) - music :chordset chordset :models models)) - -(defgeneric make-metrical-divisions (anchored-period music)) -(defmethod make-metrical-divisions ((anchored-period anchored-period) - (music composition)) - ;; Prepares a set of divisions of the period based on time-sig and a - ;; pre-set list of options for each possible time-sig numerator. - (let ((time-sigs (get-applicable-time-signatures anchored-period music))) - (if - (< (length time-sigs) 2) - (let ((candidates)) - ;; get an appropriate set of divisions. Not sure this is right - ;; - it relies on bar position being irrelevant. Is this true? - ;; This isn't really clear from this code, but if there are no - ;; time-signatures, make-divisions-with-timesigs has a test for - ;; it and will pretend it's 4/4. - (dolist (divisions (make-divisions-with-time-signature anchored-period (car time-sigs)) - candidates) - (do ((time (onset anchored-period) (cut-off (car candidate-set))) - (divisions divisions (cdr divisions)) - (candidate-set)) - ((null divisions) (push (reverse candidate-set) candidates)) - (push (make-anchored-period (timepoint time) (first divisions)) - candidate-set)))) - ;; otherwise, there are lots. Run this function once for each - ;; time-signature. - (loop for time-sig in time-sigs - nconc (make-metrical-divisions (period-intersection anchored-period time-sig) - music))))) - -(defgeneric make-divisions-with-time-signature (period time-signature)) -(defmethod make-divisions-with-time-signature ((period period-designator) - (time-signature basic-time-signature)) - (let* ((numerator (time-signature-numerator time-signature)) - (denominator (time-signature-denominator time-signature)) - (path-options (cdr (assoc numerator *path-options*)))) - (loop for divisions in path-options - collect (period-fill period divisions denominator)))) - -(defmethod make-divisions-with-time-signature ((period period-designator) - time-signature) - ;; not a known time-signature type. Assume 4/4 - (let ((path-options (cdr (assoc 4 *path-options*)))) - (loop for divisions in path-options - collect (period-fill period divisions 4)))) - -(defun period-fill (period path-options denominator) - ;; take a division of the ?bar and then repeat it until the period - ;; is filled. - ;; - ;; Perhaps this and surrounding function need to make more use of - ;; time interface? - (let ((duration-list) - ;; Multiply path-options by unit of meter. - (path-options (map 'list - #'(lambda (x) (* x (/ 4 denominator))) - path-options))) - (do* ((circular-path path-options (or (cdr circular-path) - path-options)) - (current-duration (car circular-path) (car circular-path)) - (prev-remaining (duration period) remaining) - (remaining (- (duration period) current-duration) (- remaining current-duration))) - ((<= remaining 0) (reverse (cons prev-remaining duration-list))) - (push current-duration duration-list)))) - -(defun best-level-hypothesis (division-hypotheses music - &key (chordset *full-set*) - (models *default-models*)) - ;; Rather messy wrapper for level-hypothesis-likelihoods. Should - ;; probably make this a structure or something, but use looks like - ;; being quite limited. might revisit. - (first (sort (level-hypothesis-likelihoods division-hypotheses - music - :chordset chordset - :models models) - #'> :key #'third))) - -(defun level-hypothesis-likelihoods (division-hypotheses music - &key (chordset *full-set*) - (models *default-models*)) - ;; This function takes the candidate windows being considered (as - ;; lists of anchored periods) and, for each, works out likelihoods - ;; and the most probable hypothesis. This should come from taking - ;; the likelihoods and dividing by the product of the internal sums - ;; (don't ask!) - (let ((hypothesis-likelihoods)) - (dolist (hypothesis division-hypotheses hypothesis-likelihoods) - (let ((likelihoods (map 'list - #'(lambda (x) - (get-chord-likelihoods x music models chordset)) - hypothesis))) - (push (list hypothesis likelihoods (combined-likelihoods-sum likelihoods)) - hypothesis-likelihoods))))) - -(defun get-chord-likelihoods (anchored-period music models chordset) - (let ((model-likelihoods - (loop for model in models - collect (get-chord-likelihoods-for-model anchored-period - music - :model model - :chordset chordset)))) - (combine-multimodel-likelihoods model-likelihoods))) - -(defun combine-multimodel-likelihoods (likelihoods-list) - (cond - ((= (length likelihoods-list) 1) - (car likelihoods-list)) - (t - (let ((combined-likelihoods)) - (dolist (reference-likelihood (car likelihoods-list) combined-likelihoods) - (setf combined-likelihoods - (set-likelihood combined-likelihoods - (likelihood-chordtype reference-likelihood) - (likelihood-pitch-class reference-likelihood) - (apply #'* (loop for model-likelihoods in likelihoods-list - collect (likelihood-likelihood - (assoc (car reference-likelihood) - model-likelihoods - :test #'equal))))))))))) - -;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Likelihood (structure) manipulation and access methods -;; -;; FIXME: Explain this?? Is it ((pc :type) . p(chord))? - -(defgeneric set-likelihood (likelihoods offset chord likelihood)) -(defmethod set-likelihood ((likelihoods list) chord offset likelihood) - (acons (list offset chord) likelihood likelihoods)) - -(defgeneric get-likelihood (likelihoods offset chord)) -(defmethod get-likelihood ((likelihoods list) offset chord) - (assoc (list offset chord) likelihoods :test #'equal)) - -(defgeneric best-n-likelihoods (n likelihoods)) -(defmethod best-n-likelihoods (n (likelihoods list)) - (let ((ranked (ordered-likelihoods likelihoods))) - (subseq ranked 0 n))) - -(defgeneric ordered-likelihoods (likelihoods)) -(defmethod ordered-likelihoods ((likelihoods list)) - (sort (copy-seq likelihoods) #'> :key #'cdr)) - -(defgeneric pretty-display-likelihoods (likelihoods)) -(defmethod pretty-display-likelihoods ((likelihoods list)) - (dolist (p likelihoods) - (format *standard-output* "~%~A~C~A~C~A" - (likelihood-key p) #\Tab - (chord-label (likelihood-chordtype p)) #\Tab - (likelihood-likelihood p)))) - -(defgeneric likelihood-key (likelihood)) -(defmethod likelihood-key ((likelihood list)) - (aref *keys* (first (first likelihood)))) - -(defgeneric likelihood-pitch-class (likelihood)) -(defmethod likelihood-pitch-class ((likelihood list)) - (first (first likelihood))) - -(defgeneric likelihood-chordtype (likelihood)) -(defmethod likelihood-chordtype ((likelihood list)) - (second (first likelihood))) - -(defgeneric likelihood-chord (likelihood)) -(defmethod likelihood-chord ((likelihood list)) - (first likelihood)) - -(defgeneric likelihood-likelihood (likelihood)) -(defmethod likelihood-likelihood ((likelihood list)) - (cdr likelihood)) - -(defgeneric likelihoods-sum (likelihoods)) -(defmethod likelihoods-sum ((likelihoods list)) - (loop for likelihood in likelihoods - sum (likelihood-likelihood likelihood))) - -(defgeneric combined-likelihoods-sum (combined-likelihoods)) -(defmethod combined-likelihoods-sum ((combined-likelihoods list)) - ;; Will be needed for hypothesis comparison - sums the likelihoods - ;; for all chords within a window for multiple likelihood - ;; calculations - (apply #'* (map 'list #'(lambda (window) - (loop for likelihood in window - sum (likelihood-likelihood likelihood))) - combined-likelihoods))) - -(defgeneric normalise-likelihoods (likelihoods)) -(defmethod normalise-likelihoods ((likelihoods list)) - (let ((p-sum (sum-likelihoods likelihoods))) - (if (= p-sum 1) - likelihoods - (scale-likelihoods likelihoods (/ 1 p-sum))))) - -(defgeneric scale-likelihoods (likelihoods scale-factor)) -(defmethod scale-likelihoods ((likelihoods list) (scale-factor number)) - (map 'list #'(lambda (x) - (cons (first x) - (* (cdr x) scale-factor))) - likelihoods)) - -(defgeneric sum-likelihoods (likelihoods)) -(defmethod sum-likelihoods ((likelihoods list)) - (apply #'+ (map 'list #'cdr likelihoods))) - -;; Probably useless vestigial stuff from here - -(defun vector-list-apply (predicate vector-list &optional other-args) - (let ((result-list)) - (dolist (vector vector-list (reverse result-list)) - (push (make-array (array-dimensions vector)) result-list) - (loop for i from 0 to (1- (length vector)) - do (setf (aref (first result-list) i) - (apply predicate (cons (aref vector i) other-args))))))) - -(defun vector-sum (vector) - (loop for i from 0 to (1- (length vector)) - sum (aref vector i))) - -(defun make-flat-result (chordset) - (map 'list #'(lambda (x) - (make-array (length (distribution x)) - :initial-element 0)) - (chords chordset))) - -(defun key-name (pitch-class) - (if pitch-class - (aref #("C" "C#" "D" "Eb" "E" "F" "F#" "G" "G#" "A" "Bb" "B") (mod pitch-class 12)) - nil)) \ No newline at end of file
--- a/utils/harmony/classes.lisp Thu Jun 28 08:04:20 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -(in-package #:amuse-harmony) - -;; This file contains classes for use in the harmony module. There are -;; two sets of classes here: those used in the chord-labelling itself, -;; and those used for label-evaluation and comparison with ground -;; truth. - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; CHORD LABELLING -;; -;; Chord objects are the fundamental object for identifying and naming -;; chord types they also give info about pitch classes and -;; distribution. - -(defclass chord () - ((label :accessor chord-label - :initarg :label) - (main-notes :accessor main-notes - :initarg :notes) - (bass-likelihoods :accessor bass-likelihoods - :initarg :bass-likelihoods) - (min-distribution :accessor min-distribution - :initarg :min-distribution) - (distribution :accessor distribution - :initarg :distribution) - (normalised-distribution :initarg :normalised-distribution - :initform nil))) - -;; A chordset is a gathering of chords for experiment. There are slots -;; for priors, but I'm not using them at the moment (see below for -;; current, not very good, method). - -(defclass chordset () - ((chords :accessor chords - :initarg :chords - :initform nil) - (priors :accessor priors - :initarg :priors - :initform nil))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; EVALUATION -;; - -(defclass labelled-chord-period (anchored-period) - ((root-pitch-class :writer %labelled-chord-root - :reader labelled-chord-root - :initarg :root) - (chord-type :writer %labelled-chord-type - :reader labelled-chord-type - :initarg :chord-type) - (bass :writer %labelled-chord-bass - :reader labelled-chord-bass - :initarg :bass))) -
--- a/utils/harmony/evaluation.lisp Thu Jun 28 08:04:20 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,882 +0,0 @@ -;; Stuff to compare: path (per model); correct window (per model); correct window|bass; correct window|bass - -(in-package #:amuse-harmony) - -(defparameter *test-pieces* '()) -(defparameter *test-set* '()) -(defparameter *dm-note-names* '("c" "c+" "d" "e-" "e" "f" "f+" "g" "g+" "a" "b-" "b")) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Ground-truth data types and functions -;; - -(clsql:def-view-class truth-chord () - ((cat_id - :type integer - :accessor cat_id) - (onset_bar - :type integer - :accessor start-bar) - (onset_beat - :type integer - :accessor start-beat) - (root - :type string) - (bass - :type string) - (chord-type - :type string - :accessor chord-type) - (file - :accessor harmony-file - :db-kind :join - :db-info (:join-class file - :home-key cat_id - :foreign-key cat_id - :set nil))) - (:base-table geerdes_harmony)) - -(defun all-chords () - #.(clsql:locally-enable-sql-reader-syntax) - (map 'list #'car (clsql:select 'truth-chord :where 1)) - #.(clsql:restore-sql-reader-syntax-state)) - -(defun list-harmonised-pieces () - (remove-if #'(lambda (x) (member x '(886 5205 10586 3473 6993))) - (remove-duplicates (map 'list - #'cat_id - (all-chords))))) - -(defun harmonised-bars (id) - ;; List bars which have harmonies from piece with cat_id=id - (let* ((piece-chords (piece-chord-list id))) - (when piece-chords - (let* ((chord-starts (map 'list #'start-bar piece-chords)) - (bar-max (1+ (apply #'max chord-starts))) - (bar-min (apply #'min chord-starts)) - (bar-numbers)) - (setf piece-chords (sort piece-chords #'chord-time->)) - (do ((bar bar-min (1+ bar))) - ((= bar bar-max) bar-numbers - #+nil (make-array (length bar-numbers) - :initial-contents (reverse bar-numbers))) - (let ((harmony (get-applicable-chord (* 4 bar) piece-chords))) - (when (and harmony - (> (length (chord-type harmony)) 0)) - (push bar bar-numbers)))))))) - -(defun get-applicable-chord (beats chords &key (pre-sorted t)) - ;; Return the chord that would be sounding at a given point in - ;; time. If this is being applied many times, it makes sense to sort - ;; the data before providing it, hence the &key argument. - (unless pre-sorted - (setf chords (sort chords #'chord-time->))) - (find-if #'(lambda (x) (or (< (start-bar x) - (floor beats 4)) - (and (= (start-bar x) - (floor beats 4)) - (<= (start-beat x) - (1+ (mod beats 4)))))) - chords)) - -(defun chord-time-> (chord1 chord2) - (or (> (start-bar chord1) - (start-bar chord2)) - (and (= (start-bar chord1) - (start-bar chord2)) - (> (start-beat chord1) - (start-beat chord2))))) - -(defun chord-time-< (chord1 chord2) - (or (< (start-bar chord1) - (start-bar chord2)) - (and (= (start-bar chord1) - (start-bar chord2)) - (< (start-beat chord1) - (start-beat chord2))))) - -(defun harmonised-pieces-bars-alist () - (let ((harmonised)) - (dolist (piece (list-harmonised-pieces) harmonised) - (setf harmonised (acons piece (harmonised-bars piece) harmonised))))) - -(defun all-bars () - (let ((harmonised)) - (dolist (piece (list-harmonised-pieces) harmonised) - (dolist (bar (harmonised-bars piece)) - (push (cons piece bar) harmonised))))) - -(defun random-bars (target) - (let* ((selection) (pieces-bars (make-array 1 :adjustable T :fill-pointer T))) - (dolist (piece (list-harmonised-pieces)) - (loop for bar in (harmonised-bars piece) - do (vector-push-extend (cons piece bar) pieces-bars))) - (loop for i from 0 to (1- target) - do (progn - (rotatef (aref pieces-bars i) - (aref pieces-bars - (+ i (random (- (1- (length pieces-bars)) - i))))) - (push (aref pieces-bars i) selection))) - selection)) - -(defun write-piece-bars (list filename) - (with-open-file (stream filename :direction :output :if-exists :supersede) - (dolist (bar list) - (format stream "~A~C~A~%" (car bar) #\Tab (cdr bar))))) - -(defun read-piece-bars (filename) - (let ((bars)) - (with-open-file (stream filename) - (do ((line (read-line stream nil nil) (read-line stream nil nil))) - ((null line) bars) - (let* ((s (make-string-input-stream line)) - (piece (read s)) - (bar (read s))) - (push (cons piece bar) bars)))))) - -(defun get-window-sizes (piece bar &optional (odd-divisions nil)) - (let ((bar-chords (sort (remove-if #'(lambda (x) (or (not (= (cat_id x) piece)) - (not (= (start-bar x) bar)))) - (all-chords)) - #'chord-time-<))) - (if odd-divisions - (get-window-sizes-2 bar-chords) - (get-window-sizes-1 bar-chords)))) - -(defun get-window-sizes-2 (bar-chords) - (let ((path) (prev-chord)) - (dolist (beat-chord bar-chords) - (cond - (prev-chord - (push (- (start-beat beat-chord) - (start-beat prev-chord)) - path)) - ((and (not prev-chord) - (> (start-beat beat-chord) 1)) - (push (- (start-beat beat-chord) 1) - path))) - (setf prev-chord beat-chord)) - (if prev-chord - (push (- 5 (start-beat prev-chord)) - path) - (setf path '(4))) - (reverse path))) - -(defun get-window-sizes-1 (bar-chords) - (let ((path) (prev-chord)) - (dolist (beat-chord bar-chords) - (cond - ((and (not prev-chord) - (= (start-beat beat-chord) 4)) - ;; Three beats of preceding chord or no chord - (return-from get-window-sizes-1 '(2 1 1))) - ((and (not prev-chord) - (> (start-beat beat-chord) 1)) - ;; not the first chord of bar - just the first to sound - (push (1- (start-beat beat-chord)) path)) - ((and (= (start-beat beat-chord) 4) - (< (start-beat prev-chord) 3)) - ;; Last chord of bar, but its predecessor spans half-bar - ;; break. - (return-from get-window-sizes-1 (reverse (concatenate 'list - (list 1 1 (- 3 (start-beat prev-chord))) - path)))) - ((= (start-beat beat-chord) 4) - ;; last chord of bar - (return-from get-window-sizes-1 (reverse (concatenate 'list - (list 1 1) - path)))) - (prev-chord - (push (- (start-beat beat-chord) (start-beat prev-chord)) - path))) - (setf prev-chord beat-chord)) - (if prev-chord - (if (= (start-beat prev-chord) 2) - '(1 1 2) - (reverse (cons (- 5 (start-beat prev-chord)) path))) - '(4)))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun get-test-piece (cat-id) - (unless (assoc cat-id *test-pieces*) - (setf *test-pieces* (acons cat-id - (get-composition (make-geerdes-cat-identifier cat-id)) - *test-pieces*))) - (cdr (assoc cat-id *test-pieces*))) - -(in-package "AMUSE-GEERDES") - -(defclass harmonic-evaluation-period (anchored-period) - ((cat-id :accessor %cat-id - :initarg :cat-id) - (file-id :accessor %file-id - :initarg :file-id) - (composition :accessor %composition - :initarg :composition) - (bar-number :accessor %bar-number - :initarg :bar-number) - (composition-bar-number :accessor %c-bar-number - :initarg :comp-bar-number) - (ground-truth-chords :accessor %gt-chords - :initarg :gt-chords - :initform nil) - (derived-windows :accessor %d-windows - :initarg :d-windows - :initform nil) - (derived-chords :accessor %d-chords - :initarg :d-chords - :initform nil) - (derived-likelihoods :accessor %d-likelihoods - :initarg :d-likelihoods - :initform nil))) - -(defun reset-harmonisation (harmonisation) - (setf (%d-chords harmonisation) nil - (%d-windows harmonisation) nil - (%d-likelihoods harmonisation) nil)) - -(defun derived-likelihoods (harmonisation &key (models *default-models*) - (chordset *full-set*)) - (if (%d-likelihoods harmonisation) - (%d-likelihoods harmonisation) - (let* ((possible-window-combinations (make-metrical-divisions harmonisation - (%composition harmonisation))) - (possible-windows (remove-duplicates (apply #'nconc possible-window-combinations) - :test #'period=))) - (setf (%d-likelihoods harmonisation) - (loop for window in possible-windows - collect (cons window - (get-chord-likelihoods window - (%composition harmonisation) - models chordset))))))) -(defun ground-truth-window-sizes (harmonisation) - (let* ((chords (ground-truth-chords harmonisation)) - (bar-number (%bar-number harmonisation)) - (beats (nconc (map 'list #'(lambda (x) - (if (= (start-bar x) - bar-number) - (start-beat x) - 1)) - chords) - (list (1+ (duration harmonisation)))))) - (map 'list #'- (cdr beats) beats))) - - - -(defun ground-truth-chords (harmonisation) - (if (%gt-chords harmonisation) - (%gt-chords harmonisation) - (let ((piece-chords (sort (piece-chord-list (%cat-id harmonisation)) - #'chord-time->)) (gt-chords)) - (setf (%gt-chords harmonisation) - (dolist (pc piece-chords gt-chords) - (cond - ((= (start-bar pc) (%bar-number harmonisation)) - (push pc gt-chords) - (when (= (start-beat pc) 1) - (return gt-chords))) - ((< (start-bar pc) (%bar-number harmonisation)) - (return (cons pc gt-chords))))))))) - -(defun derived-window-sizes (harmonisation &key (chordset *full-set*) models) - (declare (ignore models)) - (unless (%d-windows harmonisation) - (let ((level (best-level harmonisation (%composition harmonisation) - :chordset chordset))) - (setf (%d-chords harmonisation) - (loop for likelihoods in (second level) - collect (likelihood-chord - (car (best-n-likelihoods 1 likelihoods)))) - (%d-windows harmonisation) (first level)))) - (map 'list #'duration (%d-windows harmonisation))) - -(defun derived-windows (harmonisation &key (chordset *full-set*) models) - (declare (ignore models)) - (unless (%d-windows harmonisation) - (let ((level (best-level harmonisation (%composition harmonisation) - :chordset chordset))) - (setf (%d-chords harmonisation) - (loop for likelihoods in (second level) - collect (likelihood-chord - (car (best-n-likelihoods 1 likelihoods)))) - (%d-windows harmonisation) (first level)))) - (%d-windows harmonisation)) - -(defun derived-chords (harmonisation &key (chordset *full-set*) models) - (declare (ignore models)) - (cond - ((%d-chords harmonisation) - (%d-chords harmonisation)) - (t (let ((level (best-level harmonisation (%composition harmonisation) - :chordset chordset))) - (setf (%d-windows harmonisation) (first level) - (%d-chords harmonisation) - (loop for likelihoods in (second level) - collect (likelihood-chord (car (best-n-likelihoods 1 likelihoods))))))))) - -(defparameter *harmonic-evaluation-period-cache* nil) -(defun get-harmonic-evaluation-period (bar-number &key cat-id file-id composition) - (cond - (cat-id (get-harmonic-evaluation-period-by-cat-id bar-number cat-id)) - (file-id (get-harmonic-evaluation-period-by-file-id bar-number file-id)) - (composition (get-harmonic-evaluation-period-by-composition bar-number composition)))) - -(defun get-harmonic-evaluation-period-by-cat-id (bar-number cat-id) - (let ((harmonisation (find-if #'(lambda (x) - (and (= (%cat-id x) - cat-id) - (= (%bar-number x) - bar-number))) - *harmonic-evaluation-period-cache*))) - (unless harmonisation - (setf harmonisation - (make-harmonic-evaluation-period bar-number - (get-test-piece cat-id))) - (push harmonisation *harmonic-evaluation-period-cache*)) - harmonisation)) - -(defun get-harmonic-evaluation-period-by-file-id (bar-number file-id) - (let ((harmonisation (find-if #'(lambda (x) - (and (= (%file-id x) - file-id) - (= (%bar-number x) - bar-number))) - *harmonic-evaluation-period-cache*))) - (unless harmonisation - (let* ((composition (get-composition (make-geerdes-identifier file-id)))) - (setf harmonisation (make-harmonic-evaluation-period bar-number - composition)) - (push harmonisation *harmonic-evaluation-period-cache*))) - harmonisation)) - -(defun get-harmonic-evaluation-period-by-composition (bar-number composition) - (let ((harmonisation (find-if #'(lambda (x) - (and (eq (%composition x) - composition) - (= (%bar-number x) - bar-number))) - *harmonic-evaluation-period-cache*))) - (unless harmonisation - (setf harmonisation (make-harmonic-evaluation-period bar-number - composition)) - (push harmonisation *harmonic-evaluation-period-cache*)) - harmonisation)) - -(defun make-harmonic-evaluation-period (bar-number composition) - (with-slots (cat_id id) - (%db-entry composition) - (let ((harmonisation (make-instance 'harmonic-evaluation-period - :cat-id cat_id - :file-id id - :composition composition - :bar-number bar-number - :comp-bar-number (1+ bar-number))) - (period (whole-bar-period (1+ bar-number) composition))) - (setf (timepoint harmonisation) (timepoint period) - (duration harmonisation) (duration period)) - harmonisation))) - -(defun verify-chord (truth-chord derived-chord) - (let ((gt-root (pitch-class-from-gt (slot-value truth-chord 'root))) - (gt-label (chord-from-gt (chord-type truth-chord))) - (d-root (first derived-chord)) - (d-label (chord-label (second derived-chord)))) - (samechordp gt-root gt-label d-root d-label))) - -(defun get-chord (anchored-period harmonisation) - (likelihood-chord (first (best-n-likelihoods 1 - (cdr (assoc anchored-period - (derived-likelihoods harmonisation) - :test #'period=)))))) - -(defun ground-truth-window-beats (harmonisation) - (let ((beat 1) (beat-list '(1))) - (dolist (size (ground-truth-window-sizes harmonisation) (reverse (cdr beat-list))) - (push (incf beat size) beat-list)))) - -(defun find-matching-period (onset duration period-list) - (find-if #'(lambda (x) (and (= onset (timepoint x)) - (= duration (duration x)))) - period-list)) - -(defun position-matching-period (onset duration period-list) - (position-if #'(lambda (x) (and (= onset (timepoint x)) - (= duration (duration x)))) - period-list)) - -(defun compare-paths (bar piece &key (chordset *full-set*) models controlp mergep) - ;; FIXME: Recreate controlp and mergep - (declare (ignore controlp mergep models)) - (let* ((harmonisation (get-harmonic-evaluation-period bar :cat-id piece)) - (gtw-beats (ground-truth-window-beats harmonisation)) - (gtw-sizes (ground-truth-window-sizes harmonisation)) - (score (loop for i from 0 to (1- (length gtw-beats)) - count (find-matching-period (+ (1- (nth i gtw-beats)) - (timepoint harmonisation)) - (nth i gtw-sizes) - (derived-windows harmonisation - :chordset chordset))))) - (values (= score (length gtw-sizes)) score (length gtw-sizes)))) - -(defun compare-paths-and-harmonies (bar piece &key (chordset *full-set*) models controlp mergep) - ;; FIXME: Recreate controlp and mergep - (declare (ignore controlp mergep models)) - (let* ((harmonisation (get-harmonic-evaluation-period bar :cat-id piece)) - (gtw-beats (ground-truth-window-beats harmonisation)) - (gtw-sizes (ground-truth-window-sizes harmonisation)) - (g-chords (ground-truth-chords harmonisation)) - (d-chords (derived-chords harmonisation)) - (d-windows (derived-windows harmonisation :chordset chordset)) - (score (loop for i from 0 to (1- (length gtw-beats)) - count (let ((matchesp (position-matching-period (+ (1- (nth i gtw-beats)) - (timepoint harmonisation)) - (nth i gtw-sizes) - d-windows))) - (and matchesp - (verify-chord (nth i g-chords) - (nth matchesp d-chords))))))) - (values (= score (length gtw-sizes)) score (length gtw-sizes)))) - -(defun compare-harmonies-with-gt-windows (bar piece &key (chordset *full-set*) models) - (declare (ignore chordset models)) - (let* ((harmonisation (get-harmonic-evaluation-period bar :cat-id piece)) - (ground-truth (ground-truth-chords harmonisation)) - (score 0)) - (do ((gt-chords ground-truth (cdr gt-chords))) - ((null gt-chords) (values (= score (length ground-truth)) - score - (length ground-truth))) - (when (verify-chord (first gt-chords) - (get-chord (make-anchored-period (+ (timepoint harmonisation) - (1- (start-beat (first gt-chords)))) - (- (if (second gt-chords) - (start-beat (second gt-chords)) - (duration harmonisation)) - (1- (start-beat (first gt-chords))))) - harmonisation)) - (incf score))))) - -(defun compare-harmonies-by-beat (bar piece &key (chordset *full-set*) models) - (declare (ignore chordset models)) - (let* ((harmonisation (get-harmonic-evaluation-period bar :cat-id piece)) - (score (loop for i from 1 to (duration harmonisation) - count (verify-chord (get-gt-chord-by-beat i harmonisation) - (get-derived-chord-by-beat i harmonisation))))) - (values (= score (duration harmonisation)) score (duration harmonisation)))) - -(defun get-gt-chord-by-beat (beat harmonisation) - (let ((chords (ground-truth-chords harmonisation))) - (do ((gt (reverse chords) (cdr gt))) - ((null gt) (car chords)) - (when (<= (start-beat (car gt)) beat) - (return-from get-gt-chord-by-beat - (car gt)))))) - -(defun get-derived-chord-by-beat (beat harmonisation) - (let ((total-beat (+ (1- beat) - (timepoint harmonisation))) - (chords (derived-chords harmonisation)) - (windows (derived-windows harmonisation))) - (nth (position-if #'(lambda (x) (and (>= total-beat - (timepoint x)) - (< total-beat - (timepoint (cut-off x))))) - windows) - chords))) - -(defparameter *param-estimation-numbers* - (list (cons :major (make-array '(4 21))) - (cons :minor (make-array '(4 21))) - (cons :dim (make-array '(4 21))) - (cons :aug (make-array '(4 21))) - (cons :sus4 (make-array '(4 21))) - (cons :sus9 (make-array '(4 21))))) - -(defun incf-stats (size type offset piece bar distribution) - (let* ((param (cdr (assoc type *param-estimation-numbers*))) - (chord-notes (main-notes (find type (chords *full-set*) :key #'chord-label))) - (full-total (reduce #'+ distribution)) - (chord-sum (loop for scale-deg in chord-notes - sum (aref distribution (mod (+ offset scale-deg) 12))))) - (when (= full-total 0) - (return-from incf-stats)) - (when (> chord-sum full-total) - (format *standard-output* - "~%Piece ~D, Bar ~D, distribution ~D, ~D ~D~%" - piece bar distribution offset type) - (error "BRRRRROKEN!")) - (when (<= chord-sum 1/4) - (format *standard-output* - "~%Piece ~D, Bar ~D, distribution ~D, ~D ~D~%" - piece bar distribution offset type) - (return-from incf-stats)) - #+nil (when (<= (aref distribution (mod (+ offset (nth 1 chord-notes)) 12)) 1/40) - (format *standard-output* "~%******Piece ~D, Bar ~D. distribution ~D, ~D ~D" - piece bar distribution offset type) - (return-from incf-stats)) - (when (<= (aref distribution offset) 1/10) - (format *standard-output* "~%Piece ~D, Bar ~D. distribution ~D, ~D ~D" - piece bar distribution offset type) - (return-from incf-stats)) - (let* ((chord-ratio (/ chord-sum full-total)) - (chord-squared (* chord-ratio chord-ratio)) - (chord-logged (log chord-ratio)) - (non-chord (- 1 chord-ratio)) - (non-chord-squared (* non-chord non-chord)) - (non-chord-logged (log non-chord)) - (d1 (/ (aref distribution (mod (+ offset (nth 0 chord-notes)) 12)) - chord-sum)) - (d1-squared (* d1 d1)) - (d1-logged (log d1)) - (d3 (/ (aref distribution (mod (+ offset (nth 1 chord-notes)) 12)) - chord-sum)) - (d3-squared (* d3 d3)) - (d3-logged (log d3)) - (d5 (/ (aref distribution (mod (+ offset (nth 2 chord-notes)) 12)) - chord-sum)) - (d5-squared (* d5 d5)) - (d5-logged (log d5))) - ;; n - (incf (aref param size 0)) - (incf (aref param size 1) chord-ratio) - (incf (aref param size 2) chord-squared) - (if (= chord-ratio 0) - (incf (aref param size 4)) - (incf (aref param size 3) chord-logged)) - (incf (aref param size 5) non-chord) - (incf (aref param size 6) non-chord-squared) - (if (= non-chord 0) - (incf (aref param size 8)) - (incf (aref param size 7) non-chord-logged)) - (incf (aref param size 9) d1) - (incf (aref param size 10) d1-squared) - (if (= d1 0) - (incf (aref param size 12)) - (incf (aref param size 11) d1-logged)) - (incf (aref param size 13) d3) - (incf (aref param size 14) d3-squared) - (if (= d3 0) - (incf (aref param size 16)) - (incf (aref param size 15) d3-logged)) - (incf (aref param size 17) d5) - (incf (aref param size 18) d5-squared) - (if (= d5 0) - (incf (aref param size 20)) - (incf (aref param size 19) d5-logged))))) - -(defun parameter-estimation-figures (&key (test-set *test-set*)) - (let ((size 0) (beat 0) - (c-type) (offset 0) - (harmonisation) (bar 0) (piece 0)) - (dolist (test test-set) - (setf piece (car test) - bar (cdr test) - harmonisation (get-harmonic-evaluation-period bar :cat-id piece)) - (do ((windows (ground-truth-chords harmonisation) (cdr windows)) - (window-sizes (ground-truth-window-sizes harmonisation) (cdr window-sizes)) - (window-beats (ground-truth-window-beats harmonisation) (cdr window-beats))) - ((not windows)) - (setf size (1- (first window-sizes)) - c-type (chord-from-gt (chord-type (first windows))) - offset (pitch-class-from-gt (slot-value (first windows) 'root)) - beat (+ (1- (first window-beats)) (timepoint harmonisation))) - (incf-stats size c-type offset piece bar - (pitch-class-distribution (make-anchored-period beat (1+ size)) - (%composition harmonisation))))))) - - -(defun write-numbers-to-file (pathname) - (with-open-file (s pathname :direction :output :if-exists :supersede) - (dolist (acns *param-estimation-numbers*) - (let ((c-type (car acns)) (data (cdr acns))) - (dotimes (i 4) - (format s "~D chords, ~D beats: ~D~C~D~C~D~C~D~C~D~C~D~C~D~C~D~C~D~C~D~C~D~C~D~C~D~C~D~C~D~C~D~C~D~C~D~C~D~C~D~C~D~%" - c-type (1+ i) - (aref data i 0) #\Tab - (float (aref data i 1)) #\Tab (float (aref data i 2)) #\Tab - (float (aref data i 3)) #\Tab (aref data i 4) #\Tab - (float (aref data i 5)) #\Tab (float (aref data i 6)) #\Tab - (float (aref data i 7)) #\Tab (aref data i 8) #\Tab - (float (aref data i 9)) #\Tab (float (aref data i 10)) #\Tab - (float (aref data i 11)) #\Tab (aref data i 12) #\Tab - (float (aref data i 13)) #\Tab (float (aref data i 14)) #\Tab - (float (aref data i 15)) #\Tab (aref data i 16) #\Tab - (float (aref data i 17)) #\Tab (float (aref data i 18)) #\Tab - (float (aref data i 19)) #\Tab (aref data i 20))))))) - -(defun write-numbers-to-file-2 (pathname) - (with-open-file (s pathname :direction :output :if-exists :supersede) - (dolist (acns *param-estimation-numbers*) - (let ((c-type (car acns)) (data (cdr acns))) - (dotimes (i 4) - (format s ";; ~D chords - ~D beats ~%((~D ~D) (~D ~D) (~D ~D) (~D ~D) ~D)~%((~D ~D ~D) (~D ~D ~D) (~D ~D ~D) (~D ~D ~D) ~D)~%" - c-type (1+ i) - ;; chord / non-chord (x, x^2, log(x), left out, n) - (float (aref data i 1)) (float (aref data i 5)) - (float (aref data i 2)) (float (aref data i 6)) - (float (aref data i 3)) (float (aref data i 7)) - (aref data i 4) (aref data i 8) - (aref data i 0) - ;; 1 / 3 / 5 (x, x^2, log(x), left out, n) - (float (aref data i 9)) (float (aref data i 13)) (float (aref data i 17)) - (float (aref data i 10)) (float (aref data i 14)) (float (aref data i 18)) - (float (aref data i 11)) (float (aref data i 15)) (float (aref data i 19)) - (aref data i 12) (aref data i 16) (aref data i 20) - (aref data i 0))))))) - -(defun test-paths (&key (test-set *test-set*)) - (let ((bars 0) (grand-score 0) (grand-total 0)) - (loop for test in test-set - do (when (goodness-test (cdr test) (car test)) - (multiple-value-bind (match score total) - (compare-paths (cdr test) (car test)) - (when match (incf bars)) - (incf grand-score score) - (incf grand-total total)))) - (values bars grand-score grand-total))) - -(defparameter *ignored* nil) -(defun goodness-test (bar piece) - ;; exclusions bin - (let* ((harmonisation (get-harmonic-evaluation-period bar :cat-id piece)) - (sounding-sum (reduce #'+ (pitch-class-distribution harmonisation (%composition harmonisation)))) - (pcd)) - ;; Too little sounding - (unless (> sounding-sum 1/4) - (push (list bar piece) *ignored*) - (return-from goodness-test nil)) - ;; per gt window tests - (do ((window-beats (ground-truth-window-beats harmonisation) (cdr window-beats)) - (window-sizes (ground-truth-window-sizes harmonisation) (cdr window-sizes)) - (gt-chords (ground-truth-chords harmonisation) (cdr gt-chords))) - ((null window-beats) t) - (setf pcd (pitch-class-distribution (make-anchored-period (+ (timepoint harmonisation) - (1- (first window-beats))) - (first window-sizes)) - (%composition harmonisation))) - (unless (and (> (reduce #'+ pcd) 1) - (> (aref pcd (pitch-class-from-gt (slot-value (first gt-chords) 'root))) - 1/16)) - (push (list bar piece) *ignored*) - (return-from goodness-test nil))))) - -(defun test-harmonies (&key (test-set *test-set*)) - ;; FIXME: whole bars only at the mo! - (loop for test in test-set - count (when (goodness-test (cdr test) (car test)) - (compare-harmonies-with-gt-windows (cdr test) (car test))))) - -(defun test-paths-and-chords (&key (test-set *test-set*)) - (let ((bars 0) (grand-score 0) (grand-total 0)) - (loop for test in test-set - do (when (goodness-test (cdr test) (car test)) - (multiple-value-bind (match score total) - (compare-paths-and-harmonies (cdr test) (car test)) - (when match (incf bars)) - (incf grand-score score) - (incf grand-total total)))) - (values bars grand-score grand-total))) - -(defun test-harmonies-by-window (&key (test-set *test-set*)) - (let ((bars 0) (grand-score 0) (grand-total 0)) - (loop for test in test-set - do (when (goodness-test (cdr test) (car test)) - (multiple-value-bind (match score total) - (compare-harmonies-with-gt-windows (cdr test) (car test)) - (when match (incf bars)) - (incf grand-score score) - (incf grand-total total)))) - (values bars grand-score grand-total))) - -(defun test-harmonies-by-beat (&key (test-set *test-set*)) - (let ((bars 0) (grand-score 0) (grand-total 0)) - (loop for test in test-set - do (when (goodness-test (cdr test) (car test)) - (multiple-value-bind (match score total) - (compare-harmonies-by-beat (cdr test) (car test)) - (when match (incf bars)) - (incf grand-score score) - (incf grand-total total)))) - (values bars grand-score grand-total))) - -(defgeneric whole-bar-period (bar-number composition)) -(defmethod whole-bar-period (bar-number (composition geerdes-midi-composition)) - (multiple-value-bind (beat-no timesig) - (bar-number-to-beats bar-number composition) - (make-anchored-period (timepoint beat-no) (crotchets-in-a-bar timesig)))) - -(defgeneric bar-number-to-beats (bar-number composition)) -(defmethod bar-number-to-beats (bar-number (composition geerdes-midi-composition)) - (do* ((time-sig-list (time-signatures composition) (cdr time-sig-list)) - (current-sig (car time-sig-list) (car time-sig-list)) - (beats-per-bar (make-floating-period (crotchets-in-a-bar current-sig)) - (make-floating-period (crotchets-in-a-bar current-sig))) - (bars-left bar-number)) - ((time>= (cut-off current-sig) - (time+ (onset current-sig) - (duration* beats-per-bar bars-left))) - (values (time+ (onset current-sig) - (duration* beats-per-bar bars-left)) - current-sig)) - (decf bars-left (duration/ current-sig beats-per-bar)))) - -(defun samechordp (root1 label1 root2 label2) - (or (and (= root1 root2) - (eq label1 label2)) - (and (eq label1 :sus4) - (eq label2 :sus9) - (= root2 (mod (+ root1 5) 12))) - (and (eq label2 :sus4) - (eq label1 :sus9) - (= root1 (mod (+ root2 5) 12))))) - -(defun chord-from-gt (string) - (cdr (assoc string '(("maj" . :major) ("min" . :minor) - ("dim" . :dim) ("aug" . :aug) - ("sus4" . :sus4) ("sus9" . :sus9)) - :test #'equal))) - -(defun pitch-class-from-gt (string) - (position-if #'(lambda (x) (string-equal x string)) - *dm-note-names*)) - -(defun piece-chord-list (id) - (remove-if #'(lambda (x) - (not (= (cat_id x) id))) - (all-chords))) - -(defun get-gt-bar-chords (piece bar) - (let ((bar-beats (* 4 bar)) - (chord-list (sort (piece-chord-list piece) - #'chord-time->))) - (loop for i from bar-beats to (+ 3 bar-beats) - collect (gt-chord-to-list (get-applicable-chord i chord-list))))) - -(defun gt-chord-to-list (chord) - (list (pitch-class-from-gt (slot-value chord 'root)) - (chord-from-gt (chord-type chord)))) - -(defun explore-parameters (&key (alpha-scale '(0.4 3)) (beta '(4 14))) - (let* ((original-alpha *alpha*) - (original-betas *betas*) - (results (loop for i from (first alpha-scale) to (second alpha-scale) by 0.3 - collect (progn - (setf *alpha* (map 'vector - #'(lambda (x) - (* x i)) - original-alpha)) - (print *alpha*) - (list i (explore-betas beta)))))) - (setf *alpha* original-alpha - *betas* original-betas) - results)) - -(defun explore-betas (beta) - (let* ((b1 (first beta)) - (bn (second beta)) - (n (- bn b1))) - (assert (equal (array-dimensions *results*) - (list n n n n 4))) - (loop for semi from 0 to (1- n) by 2 - do (progn - (format t "|~D |" (+ semi b1)) - (loop for dotted-minim from 0 to (1- n) by 2 - do (loop for minim from 0 to (1- n) by 2 - do (loop for crotchet from 0 to (1- n) by 2 - do (progn - (setf *harmonic-evaluation-period-cache* nil - *betas* (list (cons 1 (+ b1 semi)) - (cons 3/4 (+ b1 dotted-minim)) - (cons 1/2 (+ b1 minim)) - (cons 1/4 (+ b1 crotchet)))) - (unless (> (aref *results* semi dotted-minim minim crotchet 0) - 0) - (multiple-value-bind (dull score total) - (test-paths) - (declare (ignore dull)) - (setf (aref *results* semi dotted-minim minim crotchet 0) - (/ score total)))) - (unless (> (aref *results* semi dotted-minim minim crotchet 1) - 0) - (multiple-value-bind (dull score total) - (test-paths-and-chords) - (declare (ignore dull)) - (setf (aref *results* semi dotted-minim minim crotchet 1) - (/ score total)))) - (unless (> (aref *results* semi dotted-minim minim crotchet 2) - 0) - (multiple-value-bind (dull score total) - (test-harmonies-by-window) - (declare (ignore dull)) - (setf (aref *results* semi dotted-minim minim crotchet 2) - (/ score total)))) - (unless (> (aref *results* semi dotted-minim minim crotchet 3) - 0) - (multiple-value-bind (dull score total) - (test-harmonies-by-beat) - (declare (ignore dull)) - (setf (aref *results* semi dotted-minim minim crotchet 3) - (/ score total)))))))))) - *results*)) - -(defparameter *results* (make-array '(10 10 10 10 4) :element-type 'ratio)) - -(defun explore-parameters-to-file (pathname &key (alpha-scale '(0.4 3)) (beta '(4 14))) - (with-open-file (stream pathname :direction :output :if-exists :supersede) - (let* ((original-alpha *alpha*) - (original-betas *betas*)) - (loop for i from (first alpha-scale) to (second alpha-scale) by 0.3 - do (progn - (setf *alpha* (map 'vector - #'(lambda (x) - (* x i)) - original-alpha)) - (print *alpha*) - (explore-betas-to-stream beta stream))) - (setf *alpha* original-alpha - *betas* original-betas)))) - -(defun explore-betas-to-stream (beta stream) - (let* ((b1 (first beta)) - (bn (second beta)) - (n (- bn b1)) (tb #\tab)) - (loop for semi from 0 to (1- n) by 2 - do (progn - (format *standard-output* "|~D |" (+ semi b1)) - (finish-output) - (loop for dotted-minim from 0 to (1- n) by 2 - do (loop for minim from 0 to (1- n) by 2 - do (loop for crotchet from 0 to (1- n) by 2 - do (progn - (format stream "~D~C~D~C~D~C~D~C~D~C" - (aref *alpha* 0) tb - (+ b1 semi) tb - (+ b1 dotted-minim) tb - (+ b1 minim) tb - (+ b1 crotchet) tb) - (setf *harmonic-evaluation-period-cache* nil - *betas* (list (cons 1 (+ b1 semi)) - (cons 3/4 (+ b1 dotted-minim)) - (cons 1/2 (+ b1 minim)) - (cons 1/4 (+ b1 crotchet)))) - (multiple-value-bind (dull score total) - (test-paths) - (declare (ignore dull)) - (format stream "~D~C" (/ score total) tb)) - (multiple-value-bind (dull score total) - (test-paths-and-chords) - (declare (ignore dull)) - (format stream "~D~C" (/ score total) tb)) - (multiple-value-bind (dull score total) - (test-harmonies-by-window) - (declare (ignore dull)) - (format stream "~D~C" (/ score total) tb)) - (multiple-value-bind (dull score total) - (test-harmonies-by-beat) - (declare (ignore dull)) - (format stream "~D~C~%" (/ score total) tb)) - (finish-output stream))))))))) -
--- a/utils/harmony/gamma.lisp Thu Jun 28 08:04:20 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,233 +0,0 @@ -;; CSR's gamma functions. I don't really know what's happening here, -;; but it seems to work... Called from within harmonic_analysis.lisp - -(in-package #:amuse-harmony) - -(let ((p (make-array - 9 :element-type 'double-float - :initial-contents ; cf. Lanczos' Approximation on Wikipedia. - '(0.99999999999980993d0 676.5203681218851d0 -1259.1392167224028d0 - 771.32342877765313d0 -176.61502916214059d0 12.507343278686905d0 - -0.13857109526572012d0 9.9843695780195716d-6 - 1.5056327351493116d-7))) - (c (make-array 8 :element-type 'double-float - :initial-contents - (mapcar (lambda (x) (float x 1d0)) - '(1/12 -1/360 1/1260 -1/1680 1/1188 -691/360360 - 1/156 -3617/122400))))) - (defun gamma/posreal (x) - (declare (type (double-float (0d0)) x)) - (locally - (declare (optimize speed)) - (labels ((corr (x) - (declare (type double-float x)) - (let ((y (/ 1.0 (* x x)))) - (do* ((i 7 (1- i)) - (r (aref c i) (+ (* r y) (aref c i)))) - ((<= i 0) (the (double-float (0d0)) (exp (/ r x)))) - (declare (type double-float r))))) - (lngamma/lanczos (x) - (declare (type (double-float 0.5d0) x)) - (let ((x (1- x))) - (do* ((i 0 (1+ i)) - (ag (aref p 0) (+ ag (/ (aref p i) (+ x i))))) - ((>= i 8) - (let ((term1 (* (+ x 0.5d0) (log (/ (+ x 7.5d0) #.(exp 1d0))))) - (term2 (+ #.(log (sqrt (* 2 pi))) (log ag)))) - (+ term1 (- term2 7.0d0)))) - (declare (type (double-float (0d0)) ag))))) - (gamma/xgthalf (x) - (declare (type (double-float 0.5d0) x)) - (cond - ((= x 0.5d0) 1.77245385090551602729817d0) - ((< x 5d0) (exp (lngamma/lanczos x))) - ;; the GNU scientific library suggests a third branch - ;; for x < 10, but in fact for our purposes the - ;; errors are under control. - (t - (let* ((p (expt x (* x 0.5))) - (e (exp (- x))) - (q (* (* p e) p)) - (pre (* #.(sqrt 2) #.(sqrt pi) q (/ (sqrt x))))) - (* pre (corr x))))))) - (if (> x 0.5d0) - (gamma/xgthalf x) - (let ((g (gamma/xgthalf (- 1d0 x)))) - (declare (type double-float g)) - (/ pi g (sin (* pi x))))))))) - -(defun beta (alpha) - (let ((sum 0) - (product 1)) - (dotimes (i (length alpha) (/ product (gamma/posreal (float sum 1d0)))) - (let ((alpha_i (aref alpha i))) - (setq product (* product (gamma/posreal (float alpha_i 1d0)))) - (incf sum alpha_i))))) - -;; this is p(d|c_i), where the chord model is represented as a -;; Dirichlet distribution with parameters alpha_i -#+nil -(defun likelihood (observations alpha) - ;; observations is a 12d vector summing to 1, alpha summing to 3 - (assert (< (abs (1- (loop for d across observations sum d))) - (* single-float-epsilon 12))) - (let ((pairs (loop for alpha_i across alpha - for d_i across observations - if (zerop alpha_i) - do (unless (zerop d_i) - (return-from likelihood 0.0d0)) - else - collect (cons alpha_i d_i)))) - ;; now all the CARs of pairs have strictly positive alpha_i - ;; values, and the d_i are the corresponding observations. - (let ((alpha_prime (map 'simple-vector #'car pairs))) - (* (/ (beta alpha_prime)) - (reduce #'* pairs :key (lambda (pair) - (expt (cdr pair) (1- (car pair))))))))) - - -(defun likelihood (observations alpha &optional (power 1)) - (assert (< (abs (1- (loop for d across observations sum d))) - (* single-float-epsilon 12))) - (let* ((alpha_0 (loop for a across alpha sum a)) - (alpha_0+1 (1+ alpha_0)) - (alpha_00+1 (* alpha_0+1 power)) - (alpha_00 (- alpha_00+1 1)) - (alpha (map 'simple-vector - (lambda (x) (* x (/ alpha_00 alpha_0))) - alpha)) - (quantum (expt 0.0005 power))) ; sake of argument - (let ((pairs - (loop for d_i across observations - for alpha_i across alpha - if (zerop alpha_i) - do (unless (zerop d_i) - (return-from likelihood 0.0d0)) - else collect (cons d_i alpha_i)))) - (assert (= (length pairs) 12)) - (let ((alpha_prime (map 'simple-vector #'cdr pairs))) - (* (/ (beta alpha_prime)) (/ quantum) - (reduce #'* pairs :key - (lambda (pair) - (if (zerop (car pair)) - (* (/ (cdr pair)) (expt quantum (cdr pair))) - (* quantum (expt (car pair) (1- (cdr - pair)))))))))))) - -(defun 4ple-likelihood (pitch-classes chord-probabilities intervals level sum) - (let ((observations)) - (dolist (interval intervals) - (push (aref pitch-classes interval) observations) - (decf sum (aref pitch-classes interval))) - (push sum observations) - (likelihood/fourwise (make-array (length observations) :initial-contents (reverse observations)) - chord-probabilities level))) - -(let ((alpha_0s '((1 . 12) (1/2 . 9) (1/4 . 30)))) - (defun likelihood/fourwise (observations alpha &optional (power 1)) - (assert (< (abs (1- (loop for d across observations sum d))) - (* single-float-epsilon 4))) - (let* ((alpha_0 (loop for a across alpha sum a)) - (alpha (map 'simple-vector - (lambda (x) (* x (/ (cdr (assoc power alpha_0s)) - alpha_0))) - alpha)) - (quantum (expt 0.0005 power))) ; sake of argument - (let ((pairs - (loop for d_i across observations - for alpha_i across alpha - if (zerop alpha_i) - do (unless (zerop d_i) - (return-from likelihood/fourwise 0.0d0)) - else collect (cons d_i alpha_i)))) - (assert (= (length pairs) 4)) - (let ((alpha_prime (map 'simple-vector #'cdr pairs))) - (* (/ (beta alpha_prime)) (/ quantum) - (reduce #'* pairs :key - (lambda (pair) - (if (zerop (car pair)) - (* (/ (cdr pair)) (expt quantum (cdr pair))) - (* quantum (expt (car pair) (1- (cdr pair))))))))))))) - -(defparameter *alpha-scale* 1) -(defparameter *beta-scale* 1) -(defparameter *alpha* #(2.925 1.95 1.625)) -(defparameter *beta* #(0.87 4.46)) -(defparameter *minimal-betas* '((1 . 8.75) (1/2 . 3) (1/4 . 2.5))) -(defparameter *full-betas-1* '((1 . 14) (1/2 . 6) (1/4 . 8))) ;; gets 144|51 -(defparameter *full-betas* '((1 . 14) (3/4 . 6) (1/2 . 6) (1/4 . 8))) ;; guess... -(defparameter *betas* *full-betas*) - -(defun 3ple-likelihood (pitch-classes chord-probabilities non-chord intervals level sum - &optional (alpha *alpha*) (beta *beta*)) - (declare (ignore chord-probabilities non-chord)) - (let ((observations)) - (dolist (interval intervals) - (push (aref pitch-classes interval) observations) - (decf sum (aref pitch-classes interval))) - (push sum observations) - (when (= sum 1) ;; So there are no chord notes at all... ?? move to likelihood function - (return-from 3ple-likelihood 0)) - (likelihood/threeplusonewise (make-array (length observations) :initial-contents (reverse observations)) - alpha beta - level) - #+nil (likelihood/threeplusonewise (make-array (length observations) :initial-contents (reverse observations)) - (map 'vector #'(lambda (x) (* x *alpha-scale*)) - #(2.925 1.95 1.625)) - #+nil (map 'vector #'(lambda (x) (* x *beta-scale*)) - #(0.87 4.46)) - (make-array 2 :initial-contents (list 0.87 *beta-scale*)) - level))) - -;; #(0.87 4.46) worked - -;;; observations is a non-relativistic four-vector of proportions: -;;; #(tonic mediant dominant other). -;;; -;;; alpha is a three-vector of Dirichlet parameters for proportions of -;;; tonic, mediant, dominant of the total chord notes. -;;; -;;; beta is a two-vector of Dirichlet parameters for proportions of -;;; non-chord vs chord notes. -;;; -;;; suggested values: -;;; alpha: #(2.925 1.95 1.625) ; (tonic, mediant, dominant) -;;; beta: #(0.44 1.76) ; (non-chord, chord) -;;; -;;; model: -;;; p(tmdo|c) = p(o|c)p(tmd|oc) -;;; where -;;; p(o|c) ~ Beta(0.44,1.76) (i.e. p({o,o'}|c) ~ Dir({0.44,1.76}) -;;; and -;;; p(tmd|oc) ~ (1-o) Dir({2.925,1.95,1.625}) -(defun likelihood/threeplusonewise - (observations alpha beta &optional (power 1)) - (assert (< (abs (1- (loop for d across observations sum d))) - (* single-float-epsilon 4))) - (let* ((quantum (expt 0.00000005 power))) ; sake of argument - (let* ((o (aref observations 3)) - (pairs - (loop repeat 3 - for d_i across observations - for alpha_i across alpha - if (zerop alpha_i) - do (unless (zerop d_i) - (return-from likelihood/threeplusonewise 0.0d0)) - else collect (cons (/ d_i (- 1 o)) alpha_i)))) - (assert (= (length pairs) 3)) - (assert (< (abs (1- (loop for (d) in pairs sum d))) (* - single-float-epsilon 3))) - (flet ((key (pair) - (if (zerop (car pair)) - (* (/ (cdr pair)) (expt quantum (cdr pair))) - (* quantum (expt (car pair) (1- (cdr pair))))))) - (let ((alpha_prime (map 'simple-vector #'cdr pairs))) - (* - ;; p(o|c) - (/ (beta beta)) (/ quantum) - (reduce #'* (list (cons o (aref beta 0)) - (cons (- 1 o) (aref beta 1))) - :key #'key) - ;; p(tmd|oc) - (/ (beta alpha_prime)) (/ quantum) - (reduce #'* pairs :key #'key)))))))
--- a/utils/harmony/methods.lisp Thu Jun 28 08:04:20 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -(in-package #:amuse-harmony) - -(defgeneric get-ground-truth-periods (anchored-period composition) - (:method (ap c) (declare (ignore ap c)) nil)) - -(defgeneric get-ground-truth-chord (ground-truth-period composition) - (:method (gtp c) (declare (ignore gtp c)) nil)) -
--- a/utils/harmony/package.lisp Thu Jun 28 08:04:20 2007 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -(cl:defpackage #:amuse-harmony - (:use #:common-lisp #:amuse #:amuse-utils) - (:export #:chord - #:chordset - #:likelihood - #:get-chord-likelihoods-for-model - #:chromatic-rotate - #:chord-labels - #:best-level - ))
--- a/utils/package.lisp Thu Jun 28 08:04:20 2007 +0100 +++ b/utils/package.lisp Thu Jun 28 14:53:53 2007 +0100 @@ -2,11 +2,6 @@ (:use #:common-lisp #:amuse #:midi) (:export #:pitchedp #:unpitchedp - #:midi-pitch-distribution - #:pitch-class-distribution - #:normalised-midi-pitch-distribution - #:normalised-pitch-class-distribution - #:normalise-vector #:bar-number #:bar-onset #:bass-note @@ -21,8 +16,6 @@ #:get-channel-for-midi #:get-pitch-for-midi #:get-velocity-for-midi - #:vector-correlation - #:krumhansl-key-finder #:levenshtein-distance #:beats-to-seconds #:get-n-grams
--- a/utils/utils.lisp Thu Jun 28 08:04:20 2007 +0100 +++ b/utils/utils.lisp Thu Jun 28 14:53:53 2007 +0100 @@ -31,62 +31,6 @@ (timepoint object1)) object2)) - -;; Pitch methods - -(defgeneric sounding-events (anchored-period sequence)) -(defmethod sounding-events ((anchored-period anchored-period) - (sequence composition)) - (let ((sounding)) - (sequence:dosequence (event sequence (reverse sounding)) - (cond - ((time>= event (cut-off anchored-period)) - (return-from sounding-events (reverse sounding))) - ((period-intersection anchored-period event) - (push event sounding)))))) - -(defgeneric midi-pitch-distribution (anchored-period composition)) -(defmethod midi-pitch-distribution ((anchored-period anchored-period) - composition) - (let ((pitches (make-array 128 :initial-element 0))) - (dolist (event (remove-if #'unpitchedp (sounding-events anchored-period composition)) pitches) - (let ((overlap (period-intersection anchored-period event))) - (if overlap - (incf (aref pitches (midi-pitch-number event)) - (duration overlap)) - (if (= (duration event) 0) - (format t "~%Note ~D beats in has no duration" (timepoint event)) - (error "This function has gone wrong - looking for overlaps that don't exist"))))))) - -(defgeneric pitch-class-distribution (anchored-period composition)) -(defmethod pitch-class-distribution ((anchored-period anchored-period) - composition) - (let ((pitches (make-array 12 :initial-element 0))) - (dolist (event (remove-if #'unpitchedp (sounding-events anchored-period composition)) pitches) - (let ((overlap (period-intersection anchored-period event))) - (if overlap - (incf (aref pitches (pitch-class event)) - (duration overlap)) - (if (= (duration event) 0) - (format t "~%Note ~D beats in has no duration" (timepoint event)) - (error "This function has gone wrong - looking for overlaps that don't exist"))))))) - -(defun normalised-midi-pitch-distribution (object1 object2) - (normalise-vector (midi-pitch-distribution object1 object2))) -(defun normalised-pitch-class-distribution (object1 object2) - (normalise-vector (pitch-class-distribution object1 object2))) -(defun normalise-vector (vector &optional (target-sum 1)) - (let ((total (loop for i from 0 to (1- (length vector)) - sum (aref vector i)))) - (cond - ((= total target-sum) - vector) - ((= total 0) - (make-array (length vector) - :initial-element (/ target-sum (length vector)))) - (t - (map 'vector #'(lambda (x) (* x (/ target-sum total))) vector))))) - ;; Not as simple as it seems - have to take into account numbering ;; practices and leading silences in representations where bar number ;; isn't part of the explicit structure. @@ -95,73 +39,6 @@ (defgeneric bass-note (anchored-period composition)) -(defun vector-correlation (vector1 vector2) - ;; useful for Krumhansl-Schmukler-like calculations - (assert (= (length vector1) (length vector2))) - (let* ((n (length vector1)) - (sum-x (loop for i from 0 to (1- n) - sum (aref vector1 i))) - (sum-y (loop for i from 0 to (1- n) - sum (aref vector2 i))) - (equation-bl (sqrt (- (* n - (loop for i from 0 to (1- n) - sum (expt (aref vector1 i) 2))) - (expt sum-x 2)))) - (equation-br (sqrt (- (* n - (loop for i from 0 to (1- n) - sum (expt (aref vector2 i) 2))) - (expt sum-y 2)))) - (equation-b (* equation-br equation-bl)) - (equation-tr (* sum-x sum-y)) - (equation-t 0) - (results-array (make-array n))) - (if (= equation-b 0) - (make-array 12 :initial-element 0) - (do ((i 0 (1+ i))) - ((= i n) results-array) - (setf equation-t (- (* n (loop for j from 0 to (1- n) - sum (* (aref vector1 (mod (+ i j) n)) - (aref vector2 j)))) - equation-tr) - (aref results-array i) (/ equation-t equation-b)))))) - - -(defparameter *krumhansl-schmuckler-minor-key* - (make-array 12 :initial-contents '(6.33 2.68 3.52 5.38 2.6 3.53 2.54 4.75 3.98 2.69 3.34 3.17))) - -(defparameter *krumhansl-schmuckler-major-key* - (make-array 12 :initial-contents '(6.35 2.23 3.48 2.33 4.38 4.09 2.52 5.19 2.39 3.66 2.29 2.88))) - -(defun krumhansl-key-finder (anchored-period composition - &key (major *krumhansl-schmuckler-major-key*) - (minor *krumhansl-schmuckler-minor-key*)) - (let* ((key) (best-score -1) - (pitches (pitch-class-distribution anchored-period composition)) - (majors (vector-correlation pitches major)) - (minors (vector-correlation pitches minor))) - (loop for i from 0 to 11 - do (when (> (aref majors i) best-score) - (setf key (list i :major) - best-score (aref majors i)))) - (loop for i from 0 to 11 - do (when (> (aref minors i) best-score) - (setf key (list i :minor) - best-score (aref minors i)))) - (values key (key->midi-key-signature key anchored-period)))) - -(defvar *line-of-fifths* (list 1 8 3 10 5 0 7 2 9 4 11 6)) - -(defun key->midi-key-signature (key anchored-period) - (let* ((tonic (car key)) - (mode (cadr key)) - (sharps (- (ecase mode - (:major (position tonic *line-of-fifths*)) - (:minor (position (mod (- tonic 9) 12) *line-of-fifths*))) - 5)) - (mode (ecase mode (:major 0) (:minor 9)))) - (amuse:make-midi-key-signature sharps mode - (timepoint anchored-period) - (duration anchored-period)))) (defun levenshtein-distance (s1 s2 &key (insertion-cost 1) (insertion-function) (deletion-cost 1)