Common Music pre-Opus 1, part 1
Here's my first attempt at some practical Lisp:
(in-package :cm)
(defvar *major-scale-intervals* '(0 2 4 5 7 9 11 12))
(defvar *minor-scale-intervals* '(0 2 3 5 7 8 11 12))
(defvar *major-scale-steps* '(0 4 2 5 4 7 5 9 7 11 9 12))
(defun scales
(&key (start-root 48)
(end-root 72)
(duration 0.2)
(intervals *major-scale-intervals*))
(let ((collection ())
(scale-length 0)
(scale-notes ()))
(do ((root start-root (1+ root))
(start-time 0 (+ start-time (* scale-length duration))))
((> root end-root) (reverse collection))
(setf scale-notes (scale
:root root
:intervals intervals
:duration duration
:start-time start-time))
(setf scale-length (length scale-notes))
(setf collection (append collection scale-notes)))))
(defun scale (&key (root 60)
(amplitude 1.0)
(duration 0.2)
(start-time 0)
(intervals *major-scale-intervals*))
(apply-notes-to-midi-objects (create-scale-list
:root root
:intervals intervals)
:start-time start-time
:amplitude amplitude
:duration duration))
(defun apply-notes-to-midi-objects
(notes &key
(amplitude 1.0)
(duration 0.2)
(start-time 0))
(let ((collection ()))
(do ((current-note (car notes) (car rest-of-notes))
(rest-of-notes (cdr notes) (cdr rest-of-notes))
(time start-time (+ time duration)))
((null current-note) (reverse collection))
(setf collection (cons (new midi
:channel 1
:keynum current-note
:time time
:duration duration
:amplitude amplitude) collection)))))
(defun create-scale-list (&key (root 60) (intervals *major-scale-intervals*))
(let ((notes (mapcar #'(lambda (x) (+ root x)) intervals)))
(append notes (cdr (reverse notes)))))
The initial aim was to just play a scale, but in the end I've ended up with a function apply-notes-to-midi-object (which is poorly named) that will take a list of keynum values and generate midi events for the given notes in sequence.
All the functions are short and do one thing. On reflecting on them, I am using let variables when I could have probably included them as part of the do loop. I'm happy with what they do, though I might want to add functionality for choosing instruments as well.
The Aftermath
I'm very impressed with the way that Lisp promotes interactive development with the REPL. I wish we had something like this for Java development. I enjoyed the ability to modify and almost instantaneously test and see the results of minor tweaks, leading to more experimental ideas and the encouragement of continual improvement. I look forward to experimenting more with a high level expressive language.
