;;; -*- Mode:LISP; Package:(COMPOSER GLOBAL); Readtable:CL; Base:10 -*- (defconst *truenote-octaves* 8) (defconst *truenote-table* nil "Table of defstructs for truenotes.") (defstruct (truenote (:print-function print-truenote)) string-for-printing ; i.e. "A7" level ; i.e. 7 key-symbol ; i.e. :A table-backpointer ; points back to including table ) (defun print-truenote (structure stream printdepth) printdepth (format stream "#" (truenote-string-for-printing structure))) (defconst *key-symbols* '(:C :C\#/D@ :D :D\#/E@ :E :F :F\#/G@ :G :G\#/A@ :A :A\#/B@ :B)) (defun init-truenote-table () (setq *truenote-table* (make-array '(8 12))) (dotimes (octave *truenote-octaves*) (do ((l *key-symbols* (cdr l)) (count 0 (add1 count))) ((null l)) (aset (make-truenote :string-for-printing (format nil "~a ~D" (car l) octave) :level octave :key-symbol (car l) :table-backpointer *truenote-table*) *truenote-table* octave count)))) (defstruct (note (:type :list) :named predicate) value ; Points to a truenote duration ; a positive integer (1 2 4 8 16 ...) grace-note ; Cons ( . ) accent ; Cons ( . ( ...)) attributes ; An Alist of attributes (( . ( ...)) ...) ) (defstruct (note-group (:type :list) :named (:constructor INTERNAL-MAKE-NOTE-GROUP) :predicate) type ; A keyword symbol describing what type of grouping it is note-list ; A list of NOTE structures property-list ; Type-specific Properties ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; Note group type specifications ;;; ;;; (defconst *standard-note-group-properties* `((:upper-length-limit . (integer 1)) (:lower-length-limit . (integer 1)))) (defconst *note-group-element-type-spec* `(or (satisfies note-group-p) (satisfies note-p))) (defmacro define-note-group (symbol &body group-properties) "Define a note group type." (check-type symbol symbol) (let (real-properties) (dolist (prop group-properties) (typecase prop (list (if (and (eq (length prop) 2) (typep (second prop) (cdr (assq (car prop) *standard-note-group-properties*)))) (push prop real-properties) (compiler:warn 'invalid-property-spec :error "Invalid note group designator: ~s" prop))) (t (compiler:warn 'invalid-property-spec :error "Invalid note group property spec entry: ~S" prop)))) `(progn 'compile (putprop ',symbol ',(cons :note-group-properties real-properties) :note-group-properties)))) ;;; here are the actual definitions (define-note-group triplet (:upper-length-limit 3) (:lower-length-limit 3)) (define-note-group doublet (:upper-length-limit 2) (:lower-length-limit 2)) (define-note-group run) (define-note-group slur) (define-note-group chord) (define-note-group trill (:upper-length-limit 1) (:lower-length-limit 1)) (defun make-note-group (note-list type) (check-type type symbol) (let ((plist (get type :note-group-properties))) (check-arg type plist "a defined note group type") (check-arg note-list (and (if (get plist :upper-length-limit) (<= note-list (get plist :upper-length-limit)) t) (if (get plist :lower-length-limit) (<= note-list (get plist :lower-length-limit)) t)) "within the number of notes limit defined by the note group type") (dolist (elem note-list) (unless (typep elem *note-group-element-type-spec*) (ferror nil "Invalid note-group element: ~S" elem))) (internal-make-note-group :type type :note-list note-list)))