Critique a beginner's code!

Larry Troxler lt@westnet.com
Fri, 15 Nov 1996 22:41:26 -0500 (EST)


Thanks Rick,Tobias, and the other list members for helping this beginner so
far. Unfortunately for you,  I am paying you back by offering the following
code for you to critique :-). At this stage, I have a number of questions,
mostly minor details, and I thought it would make the most sense simply to
post some code, with my questions embedded in the comments. So, if anyone
has the time to look through this, I would welcome any suggestions, either
specifically regarding the questions I raise, or more generally, pointing to
ways I should be doing things differently. Thanks in advance, everyone!

Also let me know if the line-formatting gets mangled in transit - if so I
will repost.

Larry Troxler
lt@westnet.com

|#


(defparameter *opus-len* 60)		; quit after a minute

;;; The following is so we can use names instead of numbers for MIDI program
;;; changes.

(defparameter *patches* '((Org400 8) (Vulcan-Harp 21)))

;;; This maintains the current MIDI program (patch) for each channel

(defparameter *cur-chan-pat* (make-array 16 :initial-element nil))    

;;; "pnote" is a MIDI note which includes a patch parameter. If the "patch"
;;; parameter is different from the patch currently being played on a given 
;;; channel, then write-event will send the new program change before the 
;;; note. 

(defobject pnote (midi-note)
  ((patch :initarg :patch :initform nil :accessor pnote-patch)))


;;; modeled after the detuned MIDI note class - the guts haven't been tested
;;; thuroughly but hopefully you understand the concept here.

(defmethod write-event ((me pnote) file)
  (let ((patchnum (second (assoc (pnote-patch me) *patches*))))
    ;; unless same patch on this channel,
    (unless (eql patchnum (aref *cur-chan-pat* (slot-value me 'channel)))
      ;; register new patch
      (setf (aref *cur-chan-pat* (slot-value me 'channel)) patchnum)
      ;; and send the program change, 100ms ahead of time (this assumes 
      ;; scheduling is in ms mode (as opposed to floats).
      (let ((obj (object midi-message	
			 message (make-program-change (slot-value me 'channel)
						      patchnum))))
	(setf (slot-value obj 'time) (- (slot-value me 'time) 100))
	(write-event obj file))))
  (call-next-method))  ; Always send the original midi-note (obviously)


;;; Now for the specific task at hand.
;;; Following are the two algorithms that will be run for this experiment.

#|
QUESTIONS

Q. syntax is a bit awkward - why can't symbolic values be directly used in
   algo inits? 
A. because there is no way for the algorithm expander to know how to 
   convert the data.  Hence, it's always unconverted ("raw").
Q. would be nicer to us symbolic intervals here - how do i do that?
A. symbolic intervals make only sense in a chromatic scale.  intervals
   etc, however, operates on any scale.
Q. there's got to be a better way than the mix/max bit. What I really want is
   to choose a new subseq at a rythmic rate of E*7*2*2 = once every 'root 
   cycle (see 'root stream below). How should I do that?
A. use the for option as shown below
|#

;;; The "Org400" part

(defun do-Org400 (chan)
  (algorithm Org400 pnote (end (- *opus-len* 1) patch 'Org400 channel chan
			       amplitude (amplitude 'mf) duration (rhythm '32))
    (setf note (item (intervals 7 2 0 linked-to 'root)))
    ;; variations on a 7/8 rhythm.
    (setf rhythm (item (rhythms e.
				(rhythms (rhythms e. e e for 12)
					 (rhythms e  e. e for 12)
					 (rhythms e e e. for 12)
					 in random for 1)
				e e)))))


;; The "Vulcan-Harp" part

(defun do-Vulcan-Harp (chan)
  (algorithm Vulcan-Harp pnote (patch 'Vulcan-Harp channel chan
				      rhythm (rhythm 'e) end (- *opus-len* 1)
				      duration (rhythm '32))
    ;; emphasize the 7/8 meter
    (setf amplitude (item (amplitudes mp pp pp pp p pp pp)))  
    (setf note (transpose (item (intervals 0 5 10 linked-to 'root)) -12))))
   
     
;;; Now, ladies and gentlemen, whithout further adieu(sp?), here's the music!

(in-tempo 120 'q)    

(merge merge-1 ()
  ;; "root-selector" sets the root note for the two defun'd algos below.
  (mute root-selector (end *opus-len* 
			   rhythm (rhythm 'e*7*2)) ; 2 7/8 measures per root
    (item (notes c4 b3 named 'root))) ; thus, 4 7/8 measures per cycle
  ;; The two parts for this creation:
  ;; The argument is a MIDI channel #.
  (do-Org400 1)
  (do-Vulcan-Harp 2))
     
#|
QUESTIONS

Q. Eventually it would be nice to have a channel allocate/free setup. Any
   prior art in this area? 
A. I don't understand the question.
Q. One problem with this merge is that the 'root item selection seems to
   happen *after* the item selections of the two threads, making the change
   of root hapen after the beat instead of on it. What determines the order 
   of evaluation and how should I fix this problem?
A. your rhythm stream in Org400 starts with an initial offset of "e.", and 
   then intersperses two more "e"'s after each inner cycle.  I like it
   though.  This example shows you that it works:

Stella [Top-Level]: (merge foo ()
		      (mute bar-1 (rhythm 1)
			(item (items 0 1 2 3 named 'eek) 
                              :kill t))
		      (mute bar-2 (rhythm .5)
			(print (item (intervals 3 5 linked-to 'eek)
                                     :kill 5))))

#<MERGE: Foo>
Stella [Top-Level]: run foo 0

3 
5 
4 
6 
5 
7 
6 
8 
6 
8 
Stella [Top-Level]: 

|#


;;;
;;; -*- EOF -*-


--PART-BOUNDARY=.19611161745.ZM18107.stanford.edu--