;;; -*- Mode:LISP; Package:USER; Readtable:ZL; Base:10 -*-
; DDBII
; Lisp program to validate a note name and compute its frequency.


(defvar middle-C 523.2411306)			; frequency of middle C in cycles per second.


(defconstant enharmonic '
  ((("C"   "C#"  "D"   "D#"  "E"   "F"   "F#"  "G"   "G#" "A"   "A#"  "B")	; all enharmonic notes.
    ("B#"  "B##" "C##" "D#"  "D##" "E#"  "E##" "F##" "G#" "G##" "A#"  "A##"))
   (("C"   "Db"  "D"   "Eb"  "E"   "F"   "Gb"  "G"   "Ab" "A"   "Bb"  "B")
    ("Dbb" "Db"  "Ebb" "Fbb" "Fb"  "Gbb" "Gb"  "Abb" "Ab" "Bbb" "Cbb" "Cb"))))


(defun valid-note (note-name)			; returns list of indices to enharmonic, or nil if not found.
  (do ((x 0 (+ x 1))) ((= x 2))
    (do ((y 0 (+ y 1))) ((= y 2))
      (do ((z 0 (+ z 1))) ((= z 12))
        (if (equal (elt (elt (elt enharmonic x) y) z) note-name)
	  (return-from valid-note (list x y z)))))))


(defun compute-frequency ()			              ; main procedure loop...
  (format t "~%Frequency calculation procedure 2.2~%")	      ; invoke by (compute-frequency) after
  (loop						              ; (load "note.lisp").
    (princ "Enter note name (in quotes): ")
    (let* ((note (read))
	   (position (valid-note note)))
      (cond (position
	     (loop
	       (format t "~%Enter displacement from middle-c: ")	       
	       (let* ((displacement (read)))
		 (cond ((numberp displacement)
			(format t "~%frequency of ~A ~D octaves from middle-C is ~G cps~%"
				note
				displacement
				(* middle-c (^ 2.0 (+ (// (third position) 12.0) displacement))))
			(return))
		       (t
			(format t "~%...~A is not a number..." displacement))))))	; the frequency
	    ((equalp note 'quit)
	     (format t "~%see ya soon...~%")
	     (return-from compute-frequency))
	    ((null position)
	     (format t "~%valid note names are:")
	     (print enharmonic))))))
