Critique a beginner's code!

Tobias Kunze t@ulysses.Stanford.EDU
Sun, 17 Nov 1996 19:44:39 -0800


 | Nope, sorry. This doesn't give the same output as my
 | original example. The intent was that the rhythm cycle
 | always covered a 7/8 measure (the three randomly choosen
 | subsequences are of the same length). On listening to your
 | revision, I hear something different.

Oops, I misread your example.  But your pattern will still be offset by
an "e." pick-up:

Converted to plain item streams (for better readability) my suggestion
would read:

> (setf x #icx(e.
               #irx((#icx(e. e e) min 4 max 4)
                    (#icx(e e. e) min 4 max 4)
                    (#icx(e e e.) min 4 max 4)
                    for 1)
               e e))
#<CYCLIC-ITEM-STREAM @ #x107e3f62>
> (read-items x 61)
(E.	E E. E		E E               ; group 1 of four (inner stream 2)
 E. 	E E. E 		E E
 E. 	E E. E 		E E
 E. 	E E. E 		E E

 E. 	E. E E 		E E               ; group 2 of four (inner stream 1)
 E. 	E. E E 		E E
 E. 	E. E E 		E E
 E. 	E. E E 		E E

 E. 	E E E. 		E E               ; group 3 of four (inner stream 3)
 E. 	E E E. 		E E
 E. 	[...]
)




If all your random measures should be repeated 4 times, there is a
slightly simpler version using the "repeat" macro:

> (setf y #icx(a
               (repeat #irx(#icx(x1 x2 x3)
                            #icx(y1 y2 y3)
                            #icx(z1 z2 z3)
                            for 1)
               b c))
#<CYCLIC-ITEM-STREAM @ #x107ec82a>
> (read-items y 61)
(A 	Y1 Y2 Y3 	B C
 A 	Y1 Y2 Y3 	B C
 A 	Y1 Y2 Y3 	B C
 A 	Y1 Y2 Y3 	B C

 A 	Z1 Z2 Z3 	B C
 A 	Z1 Z2 Z3 	B C
 A 	Z1 Z2 Z3 	B C
 A 	Z1 Z2 Z3 	B C

 A 	Y1 Y2 Y3 	B C
 A 	Y1 Y2 Y3 	B C
 A	[...]
)


But it is less general.


 | I meant that instead of needing to specify specific MIDI
 | channels, that there could be a system where an algorithm
 | "allocates" a free channel, and "frees" it when it is done
 | with it.

I think you can easily code up such a midi channel wrapper yourself.
just call it in each algorithm when you set the channel slot.  There
is no way to implement something like that to be effective at a later
point in time, since everything has to be a midi message with fixed
channel.  Allocating and freeing channels is further complicated by
the fact that midi messages are queued in the future, ie, the allocation
map at evaluation time doesn't necessarily have be the same at event time.
The only clean way of doing that would be on the synthesizer side, ie,
AFTER the midi bottleneck.

But it's easy to implement channel management that works for all other
cases.


 | To make things even more confusing, when I play your
 | revision, the Vulcan part works correctly! So what's
 | different??

It was coded by me :)


 | You suggest this in the redefined write-event for pnote (inherited from
 | midi-note):
 |
 | >	(setf (slot-value obj 'time) (- (slot-value me 'time) 100))
 | >	(write-event obj file))))
 | >  (call-next-method))  ; Always send the original midi-note (obviously)
 |
 | I thought about this at first, but I wasn't sure that I could do that. I was
 | worried about what happens if this throws the output out of time-increasing
 | order. For example, what if there was already a midi event written starting
 | at (- (slot-value me 'time) 50) ?

You can do that as long as your calculations keep 50ms (or whatever) ahead of
the midi output.  If not, your message will be overdue and output instantly.

Your concept of output is wrong.  The driver actually time-sorts data, and
where not, we have code that does so.  Almost every note written schedules
its note-off at a later time than the next note-on, thus violating the
time order of the output, for instance.  It doesn't matter whether two
messages are sent at the same time, the driver puts merges them such that
they don't overrun the midi interface.


-Tobias