home *** CD-ROM | disk | FTP | other *** search
/ Computer Music Interactif…cial Edition 1999 Winter / cd 3.iso / pc / Mac / Shares / Midishare™1.68 / Development Tools / Common Lisp / MCL_68k / Tutorial.lisp < prev   
Encoding:
Text File  |  1996-09-09  |  24.2 KB  |  570 lines

  1. ;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
  2. ;;===================================================================================
  3. ;;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
  4. ;;
  5. ;;             MIDISHARE MINI TUTORIAL
  6. ;;
  7. ;;      This file is a small guided tour for using MidiShare with MCL2.0.
  8. ;;
  9. ;; INSTRUCTIONS :
  10. ;; If MidiShare is not installed, you need to copy the "MidiShare™ 1.68ß" file
  11. ;; into your system folder (actually the control panel folder) and reboot.
  12. ;;
  13. ;; Then you need to load the "MidiShare.lisp" file (Load cmd in the Eval menu). 
  14. ;; It contains the lisp interface to MidiShare.
  15. ;;
  16. ;; Now you are ready for the guided tour. Just follows the instructions for each
  17. ;; tests starting from test 1. Don't skip any test because some of them depend of 
  18. ;; previous ones.
  19. ;;
  20. ;;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
  21. ;;===================================================================================
  22. ;;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
  23.  
  24.  
  25.  
  26. ;;===================================================================================
  27. ;; test 1 : Check if MidiShare is installed
  28. ;;===================================================================================
  29. ;; First of all, an application have to make sure that MidiShare is in memory. 
  30. ;; This checking is done thanks to the MidiShare function. The result is T if 
  31. ;; MidiShare is installed and NIL otherwise. Check the Listener window for the
  32. ;; result.
  33.  
  34.  (midishare)                                               ; <== EVALUATE THIS EXPRESSION.
  35.  
  36. ;; Check the Listener window for the result.It must be T. If it is not, you must 
  37. ;; install MidiShare an reboot.
  38.  
  39. ;;===================================================================================
  40. ;; test 2 : current version of MidiShare
  41. ;;===================================================================================
  42. ;; MidiGetVersion gives the version number of MidiShare. The result must be
  43. ;; 168 or greater.
  44.  
  45.  (midigetversion)                                          ; <== EVALUATE THIS EXPRESSION.
  46.  
  47.  
  48. ;;===================================================================================
  49. ;; test 3 : how many client applications are running ?
  50. ;;===================================================================================
  51. ;; The MidiCountAppls gives the number of active Midi applications on. The result 
  52. ;; here will probably be 0. It means that no MidiShare client applications are running.
  53.  
  54.  (midicountappls)                                          ; <== EVALUATE THIS EXPRESSION.
  55.  
  56.  
  57. ;;===================================================================================
  58. ;; test 4 : register Common Lisp as a client application
  59. ;;===================================================================================
  60. ;; MidiOpen allows the recording of some information relative to the application 
  61. ;; context (its name, the value of the global data register, etc...), to allocate a 
  62. ;; reception FIFO and to attribute a unique reference number to the application. 
  63. ;; In counterpart to any MidiOpen call, the application must call the MidiClose 
  64. ;; function before leaving, by giving its reference number as an argument. 
  65. ;; MidiShare can thus be aware of the precise number of active Midi applications. 
  66.  
  67.  (defparameter *refnum* (midiopen "Common Lisp"))         ; <== EVALUATE THIS EXPRESSION.
  68.  
  69.  
  70. ;;===================================================================================
  71. ;; test 5 : how many client applications are running now?
  72. ;;===================================================================================
  73. ;; Now lets check again for the number of MidiShare client applications on activity.
  74.  
  75.  (midicountappls)                                         ; <== EVALUATE THIS EXPRESSION.
  76.  
  77. ;; The result is probably 2. Actually when the first client opens, a pseudo application 
  78. ;; with name "MidiShare" and reference number 0 is also started. You can think of it as 
  79. ;; the Midi drivers of MidiShare. For this reason we have a count of 2 client 
  80. ;; applications instead of 1. This pseudo application "MidiShare" cannot be closed
  81. ;; directly. It is closed automatically when the very last client application closes. 
  82.  
  83.  
  84. ;;===================================================================================
  85. ;; test 6 : list MidiShare client applications
  86. ;;===================================================================================
  87. ;; We are now going to list all the client applications on activity.
  88.  
  89. (defun list-appls ()
  90.   (format t "List of MidiShare client applications ~%")
  91.   (dotimes (i (MidiCountAppls))
  92.     (let ((ref (MidiGetIndAppl (1+ i))))
  93.       (format t 
  94.               " ~2d : reference number ~2d, name : '~a' ~%" 
  95.               (1+ i) ref (MidiGetName ref)))))          ; <== EVALUATE THIS DEFINITION
  96.  
  97. ;; NOTE : The MidiGetIndAppl function allows to know the reference number of any 
  98. ;; application by giving its order number (a number between 1 and MidiCountAppls).
  99. ;; The MidiGetName function gives the name of a client application from its
  100. ;; reference number.
  101.  
  102.  (list-appls)                                            ; <== EVALUATE THIS EXPRESSION.
  103.  
  104. ;; In the Listener window the result will be :
  105. ;;    ? List of MidiShare client applications 
  106. ;;      1 : reference number  0, name : 'MidiShare' 
  107. ;;      2 : reference number  1, name : 'Common Lisp' 
  108. ;;    NIL
  109.  
  110. ;; NOTE : The reference number of the pseudo application "MidiShare" is always 0.
  111.  
  112. ;;===================================================================================
  113. ;; test 7 : search for a client application with name "Common Lisp"
  114. ;;===================================================================================
  115. ;; Knowing an application name, it is possible to find its reference number using
  116. ;; the MidiGetNamedAppl function. If more than one client applications have the same
  117. ;; name the result is the reference number of the first one (the one with the smallest
  118. ;; reference number).
  119.  
  120.  (MidiGetNamedAppl "Common Lisp")            ; <== EVALUATE THIS EXPRESSION.
  121.  
  122. ;; The result here is 1, the same value stored in the *REFNUM* variable (see Test 4).
  123.  
  124.  *REFNUM*                        ; <== EVALUATE THIS EXPRESSION.
  125.  
  126. ;;===================================================================================
  127. ;; test 8 : search for a client application with name "XYZ"
  128. ;;===================================================================================
  129. ;; If we look for a non existant name the result is -4. This means that no application
  130. ;; with that name was found. Negative reference numbers are used as error codes.
  131.  
  132.  (MidiGetNamedAppl "XYZ?!")                ; <== EVALUATE THIS EXPRESSION.
  133.  
  134.  
  135. ;;===================================================================================
  136. ;; test 9 : change the name of client application "Common Lisp" to "MCL"
  137. ;;===================================================================================
  138. ;; MidiSetName allows to change the name of a client application.
  139.  
  140.  (MidiSetName (MidiGetNamedAppl "Common Lisp") "MCL")    ; <== EVALUATE THIS EXPRESSION.
  141.  
  142.  (list-appls)                        ; <== EVALUATE THIS EXPRESSION.
  143.  
  144. ;; In the Listener window the result will be :
  145. ;;    ? List of MidiShare client applications 
  146. ;;      1 : reference number  0, name : 'MidiShare' 
  147. ;;      2 : reference number  1, name : 'MCL' 
  148. ;;    NIL
  149.  
  150. ;;===================================================================================
  151. ;; test 10 : connect MCL to MidiShare
  152. ;;===================================================================================
  153. ;; For an application to be able to transmit and receive events, it must first connect
  154. ;; to some source and destination applications. The MidiConnect function is used to 
  155. ;; connect or disconnect a source and a destination. The function takes 3 arguments :
  156. ;; the reference number of the source, the reference number of the destination and a
  157. ;; boolean (T to connect and NIL to disconnect).
  158.  
  159.  (MidiConnect *refnum* 0 t)                ; <== EVALUATE THIS EXPRESSION.
  160.  
  161. ;; Now MCL will be able to send events to the pseudo application "MidiShare", i.e. the
  162. ;; Midi drivers.
  163.  
  164. ;;===================================================================================
  165. ;; test 11 : test if MCL is connected to MidiShare
  166. ;;===================================================================================
  167. ;; We can test the connections using MidiIsConnected.
  168.  
  169.  (MidiIsConnected *refnum* 0)                ; <== EVALUATE THIS EXPRESSION.
  170.  
  171. ;; The result is T meaning that MCL sends to MidiShare
  172.  
  173.  
  174.  (MidiIsConnected 0 *refnum*)                ; <== EVALUATE THIS EXPRESSION.
  175.  
  176. ;; The result is NIL meaning that MidiShare does not send to MCL
  177.  
  178.  
  179. ;;===================================================================================
  180. ;; test 12 : list the destinations of an application
  181. ;;===================================================================================
  182. ;; We can list all the destinations of an application by using MidiIsConnected for 
  183. ;; each possible destination
  184.  
  185.  (defun list-dest (ref1)
  186.    (format t 
  187.            "List of the destinations of '~a' (ref num = ~d) ~%" 
  188.            (MidiGetName ref1) ref1)
  189.    (dotimes (i (MidiCountAppls))
  190.      (let ((ref2 (MidiGetIndAppl (1+ i))))
  191.        (if (MidiIsConnected ref1 ref2)
  192.          (format t " --> '~a' (ref num = ~d) ~%"
  193.                  (MidiGetName ref2)  
  194.                  ref2)))))                ; <== EVALUATE THIS DEFINITION
  195.  
  196.  (list-dest *refnum*)                    ; <== EVALUATE THIS EXPRESSION.
  197.  
  198. ;; In the Listener window the result will be :
  199. ;;    ? List of the destinations of 'MCL' (ref num = 1) 
  200. ;;     --> 'MidiShare' (ref num = 0) 
  201. ;;    NIL
  202.  
  203.  
  204. ;;===================================================================================
  205. ;; test 13 : list the sources of an application
  206. ;;===================================================================================
  207. ;; We can list all the sources of an application by using MidiIsConnected for 
  208. ;; each possible source
  209.  
  210.  (defun list-src (ref1)
  211.    (format t 
  212.            "List of the sources of '~a' (ref num = ~d) ~%" 
  213.            (MidiGetName ref1) ref1)
  214.    (dotimes (i (MidiCountAppls))
  215.      (let ((ref2 (MidiGetIndAppl (1+ i))))
  216.        (if (MidiIsConnected ref2 ref1)
  217.          (format t " <-- '~a' (ref num = ~d) ~%"
  218.                  (MidiGetName ref2)  
  219.                  ref2)))))                ; <== EVALUATE THIS DEFINITION
  220.  
  221.  (list-src 0)                        ; <== EVALUATE THIS EXPRESSION.
  222.  
  223. ;; In the Listener window the result will be :
  224. ;;    ? List of the sources of 'MidiShare' (ref num = 0) 
  225. ;;      <-- 'MCL' (ref num = 1) 
  226. ;;    NIL
  227.  
  228.  
  229. ;;===================================================================================
  230. ;; test 14 : send a note with a pitch, a velocity and a duration in milliseconds
  231. ;;===================================================================================
  232. ;; We are now ready to send a note event. Be sure to have a Midi equipment connected
  233. ;; to the Modem port.
  234.  
  235.  (defun send-note (pitch)
  236.    (let ((event (MidiNewEv typeNote)))    ; ask for a new note event
  237.      (unless (%null-ptr-p event)    ; if the allocation was succesfull
  238.        (chan event 0)            ; set the midi channel to 0 (means channel 1)
  239.        (port event 0)            ; set the destination port to Modem
  240.        (xfield event 0 pitch)        ; set the pitch field
  241.        (xfield event 1 64)        ; set the velocity field
  242.        (xfield event 2 1000)        ; set the duration field to 1 second
  243.        (MidiSendIm *refnum* event))    ; send the note immediatly
  244.      ))                            ; <== EVALUATE THIS DEFINITION
  245.  
  246.  (send-note 60)                        ; <== EVALUATE THIS EXPRESSION.
  247.  
  248. ;; The note was sent to your Midi equipment. Actually two messages where sent, a keyOn
  249. ;; and a keyOn with velocity 0 after the duration of the note.
  250.  
  251. ;; IMPORTANT NOTE :
  252. ;; MidiShare includes its own memory manager to store events and sequences. MidiNewEv 
  253. ;; allocates the required memory to store the note event and returns a pointer. 
  254. ;; This space is automatically disposed by MidiShare when the event is sent. This means 
  255. ;; that once you have sent an event you MUST NOT use it anymore. In particular you 
  256. ;; MUST NOT send it a second time, free it or make a copy of it.
  257.  
  258. ;;===================================================================================
  259. ;; test 15 : send multiple notes
  260. ;;===================================================================================
  261. ;; Here is an example of how to send several copies of an event
  262.  
  263.  (defun send-multiple-notes (n pitch delay)
  264.    (let ((event (MidiNewEv typeNote))    ; ask for a new note event
  265.          (date (MidiGetTime)))        ; remember the current time
  266.      (unless (%null-ptr-p event)    ; if the allocation was succesful
  267.        (chan event 0)            ; set the midi channel to 0 (means channel 1)
  268.        (port event 0)            ; set the destination port to Modem
  269.        (xfield event 0 pitch)        ; set the pitch field
  270.        (xfield event 1 64)        ; set the velocity field
  271.        (xfield event 2 (- delay 1))    ; set the duration field
  272.  
  273.        (dotimes (i n)            ; loop for the requested number of events
  274.          (MidiSendAt *refnum*         ; send a copy of the original note
  275.                      (MidiCopyEv event)     
  276.                      (+ date (* i delay))))
  277.        (MidiFreeEv event) )        ; dispose the original note
  278.      ))                            ; <== EVALUATE THIS DEFINITION
  279.  
  280.  (send-multiple-notes 10 72 1000)            ; <== EVALUATE THIS EXPRESSION.
  281.  
  282.  (progn (send-multiple-notes 6 60 800)
  283.         (send-multiple-notes 8 72 600))            ; <== EVALUATE THIS EXPRESSION.
  284.  
  285. ;; NOTE : Events can be sent in the future. MidiShare internal scheduler takes care of 
  286. ;; sending them at the right time according to the specified date.
  287.  
  288. ;;===================================================================================
  289. ;; test 16 : send 'hello' lyric
  290. ;;===================================================================================
  291. ;; MidiShare defines several types of events. Some of them correspond to real Midi
  292. ;; messages, some others (like notes) are translated to Midi messages and some others
  293. ;; correspond to Midi File 1.0 data. Here is an example of sending Midi File Lyrics
  294. ;; data to another application. For this test you need to launch 'msDisplay' application
  295. ;; (in the 'MidiShare suite' folder) 
  296.  
  297. ;; Then we connect MCL to msDisplay
  298.  (MidiConnect *refnum* (MidiGetNamedAppl "msDisplay") t) ; <== EVALUATE THIS EXPRESSION.
  299.  
  300. (defun send-lyric (aString)
  301.   (let ((event (MidiNewEv typeLyric)))
  302.     (unless (%null-ptr-p event)
  303.       (chan event 0)
  304.        (port event 0)
  305.        (text event aString)        
  306.        (MidiSendIm *refnum* event)) ))            ; <== EVALUATE THIS DEFINITION
  307.  
  308.  (send-lyric "Hello")                    ; <== EVALUATE THIS EXPRESSION.
  309.  
  310. ;; msDisplay shows the 'Hello' message in its window
  311.  
  312. ;;===================================================================================
  313. ;; test 17 : send a text event
  314. ;;===================================================================================
  315. ;; Here is a more general way to send Midi File textual events 
  316.  
  317. (defun send-text (aType aString)
  318.   (let ((event (MidiNewEv aType)))
  319.     (unless (%null-ptr-p event)
  320.       (chan event 0)
  321.        (port event 0)
  322.        (text event aString)        
  323.       (MidiSendIm *refnum* event)) ))            ; <== EVALUATE THIS DEFINITION
  324.  
  325. (progn
  326.   (send-text typeText "Hello")
  327.   (send-text typeCopyright "Mozart")
  328.   (send-text typeSeqName "Concerto")
  329.   (send-text typeInstrName "Piano")
  330.   (send-text typeLyric "Hiiiiiii")
  331.   (send-text typeMarker "mark 1")
  332.   (send-text typeCuePoint "Reverb here"))        ; <== EVALUATE THIS EXPRESSION.
  333.  
  334.  
  335. ;;===================================================================================
  336. ;; test 18 : send an SMPTE offset
  337. ;;===================================================================================
  338.  
  339. (defun send-smpte-offset (format hours minutes seconds frames subframes)
  340.   (let ((event (MidiNewEv typeSMPTEOffset)))
  341.     (unless (%null-ptr-p event)
  342.       (chan event 0)
  343.       (port event 0)
  344.  
  345.       (xfield event 0 format)        
  346.       (xfield event 1 hours)        
  347.       (xfield event 2 minutes)        
  348.       (xfield event 3 seconds)        
  349.       (xfield event 4 frames)        
  350.       (xfield event 5 subframes)    
  351.     
  352.       (MidiSendIm *refnum* event)) ))            ; <== EVALUATE THIS DEFINITION
  353.  
  354. (send-smpte-offset Smpte25Fr 10 24 59 12 00)        ; <== EVALUATE THIS EXPRESSION.
  355.  
  356.  
  357. ;;===================================================================================
  358. ;; test 19 : delay and transpose incoming notes
  359. ;;===================================================================================
  360. ;; We are now going to see how to receive events. Every client application have a 
  361. ;; reception fifo where incoming events are stored. Applications can be informed
  362. ;; in real-time of incoming events using a receive alarm. But from Lisp we need to do 
  363. ;; some polling because Lisp code can't be called at interrupt level.
  364. ;; In this example, note, keyOn and keyOff events are transposed and sent with a delay. 
  365. ;; Other received events are deleted. The program loops until the mouse is clicked.
  366. ;; Be sure to have a Midi keyboard connected to the modem port.
  367.  
  368. (defun transform (transpose delay)
  369.   (MidiConnect *refnum* 0 t)            ; connect MCL to MidiShare
  370.   (MidiConnect 0 *refnum* t)            ; connect MidiShare to MCL
  371.   (MidiFlushEvs *refnum*)            ; flush old events in the rcv fifo
  372.   (loop (if (mouse-down-p) (return))        ; loop until mouse clicked
  373.         (do ((event (MidiGetEv *refnum*) (MidiGetEv *refnum*)))    ;read all the events
  374.             ((%null-ptr-p event))        ; in the rcv fifo
  375.           (if (member (type event) (list typeNote typeKeyOn typeKeyOff))
  376.             (progn                    ; note, keyOn and KeyOff events
  377.               (pitch event (+ transpose (pitch event)))    ;   are transposed
  378.               (date event (+ delay (date event)))    ;   delayed
  379.               (MidiSend *refnum* event))        ;   and sent.
  380.             (MidiFreeEv event))))            ; other events are deleted
  381.   (MidiConnect 0 *refnum* nil)            ; break the connection from MidiShare to MCL     
  382.   )                            ; <== EVALUATE THIS DEFINITION
  383.  
  384. (transform 12 1000)                    ; <== EVALUATE THIS EXPRESSION.
  385.  
  386.  
  387. ;;===================================================================================
  388. ;; test 20 : some background processing
  389. ;;===================================================================================
  390. ;; In this example we install a background mechanism to receive incoming events.
  391. ;; We replace the ccl:event-dispatch function with a new one that looks for events in 
  392. ;; the rcv fifo and call a function stored in *rcv-task* to process the event.
  393.  
  394. ;; The background mechanism
  395. (progn
  396.   (defvar *rcv-task* nil)
  397.   (defvar old-event-dispatch (symbol-function 'ccl:event-dispatch))
  398.   
  399.   (defun rcv-event-dispatch (&optional idle level)
  400.     (if *rcv-task*
  401.       (do ((event (MidiGetEv *refnum*) (MidiGetEv *refnum*)))
  402.           ((%null-ptr-p event))
  403.         (funcall *rcv-task* event))
  404.       (MidiFlushEvs *refnum*))
  405.     (funcall old-event-dispatch idle level))
  406.   
  407.   (defun install-rcv ()
  408.     (setq *rcv-task* nil)
  409.     (let ((*warn-if-redefine-kernel* nil))
  410.       (setf (symbol-function 'ccl:event-dispatch) #'rcv-event-dispatch)))
  411.   
  412.   (defun remove-rcv ()
  413.     (let ((*warn-if-redefine-kernel* nil))
  414.       (setf (symbol-function 'ccl:event-dispatch) old-event-dispatch)))
  415.   
  416.   (defun receive (foo)
  417.     (setq *rcv-task* foo)) 
  418.   )                            ; <== EVAL THESE DEFINITIONS
  419.  
  420.  
  421. ;; Some transformations
  422.  
  423. (defun transpodelay (transpose delay)
  424.   #'(lambda (event)
  425.       (if (member (type event) (list typeNote typeKeyOn typeKeyOff))
  426.             (progn
  427.               (pitch event (+ transpose (pitch event)))
  428.               (date event (+ delay (date event)))
  429.               (MidiSend *refnum* event))
  430.             (MidiFreeEv event))))            ; <== EVALUATE THIS DEFINITION
  431.  
  432. (defun echo (delay attenuation)
  433.   #'(lambda (event)
  434.       (when (or (and (= (type event) typeKeyOn) (> (vel event) 0))
  435.                 (= (type event) typeNote))
  436.         (do ((dt (+ (date event) delay) (+ dt delay))
  437.              (vl (- (vel event) attenuation) (- vl attenuation))
  438.              (note ))
  439.             ((or (< vl 1)(> vl 127)))
  440.           (unless (%null-ptr-p (setq note (MidiNewEv typeNote)))
  441.             (port note (port event))
  442.             (chan note (chan event))
  443.             (pitch note (pitch event))
  444.             (vel note vl)
  445.             (dur note (- delay 1))
  446.             (MidiSendAt *refnum* note dt))))
  447.       (MidiFreeEv event)))                ; <== EVALUATE THIS DEFINITION
  448.  
  449. ;; install the background mechanism
  450. (install-rcv)                        ; <== EVALUATE THIS EXPRESSION.
  451.  
  452. ;; install a receive transformation and play on the keyboard
  453. (receive (transpodelay 12 125))                ; <== EVALUATE THIS EXPRESSION.
  454.  
  455. ;; another receive transformation
  456. (receive (echo 125 4))                    ; <== EVALUATE THIS EXPRESSION.
  457.  
  458. ;; no transformations (incoming events are deleted)
  459. (receive ())                        ; <== EVALUATE THIS EXPRESSION.
  460.  
  461. ;; remove the background mechanism
  462. (remove-rcv)                        ; <== EVALUATE THIS EXPRESSION.
  463.  
  464.  
  465.  
  466. ;;===================================================================================
  467. ;; test 21 : lisp scheduling
  468. ;;===================================================================================
  469. ;; A powerful feature of MidiShare is the ability to call functions in the future
  470. ;; by using the MidiTask function. The idea is to pass MidiShare the address of a
  471. ;; function, the arguments and the date of the actual call. MidiShare collects
  472. ;; all these informations into a special event, schedules this event and, at 
  473. ;; the specified date, calls the function with its arguments. We can't use MidiTask with
  474. ;; a Lisp function because the call is done at interrupt level. We have to use MidiDTask.
  475. ;; It works like MidiTask but the call is not done by MidiShare. At the specified date
  476. ;; the DTask is added to a private queue of the client application. The application
  477. ;; need to call MidiExec1DTask to call the next pending DTask.
  478. ;; We have a second problem with lisp. We need to protect the arguments of the task from
  479. ;; the garbage collector. The idea is to store the arguments in a Lisp table and to pass 
  480. ;; the index of the argument in the table instead of the real argument. 
  481.  
  482. ;; protection of arguments from garbage collecting
  483.  
  484. (progn
  485.   (defvar *gc-protect-tbl*)
  486.   
  487.   (defun make-protection-tbl (size)
  488.     (setq *gc-protect-tbl* (make-array size))
  489.     (dotimes (i size) (setf (aref *gc-protect-tbl* i) (+ i 1)))
  490.     (setf (aref *gc-protect-tbl* (- size 1)) nil))
  491.   
  492.   (defun protect (task)
  493.     (let ((index (aref *gc-protect-tbl* 0)))
  494.       (when index
  495.         (setf (aref *gc-protect-tbl* 0) (aref *gc-protect-tbl* index))
  496.         (setf (aref *gc-protect-tbl* index) task)
  497.         index)))
  498.   
  499.   (defun refere (index)
  500.     (let ((task (aref *gc-protect-tbl* index)))
  501.       (setf (aref *gc-protect-tbl* index) (aref *gc-protect-tbl* 0))
  502.       (setf (aref *gc-protect-tbl* 0) index)
  503.       task))
  504.   )                            ; <== EVAL THESE DEFINITIONS
  505.  
  506. ;; Background mechanism to evaluate the scheduled tasks
  507.  
  508. (progn
  509.   (defun scheduler-event-dispatch (&optional idle level)
  510.     (dotimes (i (MidiCountDTasks *refnum*)) 
  511.       (MidiExec1DTask *refnum*))
  512.     (funcall old-event-dispatch idle level))
  513.   
  514.   (defun install-scheduler ()
  515.     (make-protection-tbl 500)
  516.     (let ((*warn-if-redefine-kernel* nil))
  517.       (setf (symbol-function 'ccl:event-dispatch) #'scheduler-event-dispatch)))
  518.   
  519.   (defun remove-scheduler ()
  520.     (let ((*warn-if-redefine-kernel* nil))
  521.       (setf (symbol-function 'ccl:event-dispatch) old-event-dispatch)))
  522.   
  523.   (defpascal eval-task (:long date :word refnum :long index :long arg2 :long arg3)
  524.     (declare (ignore date refnum arg2 arg3))
  525.     (let ((task (refere index)))
  526.       (when (consp task) (apply (car task) (cdr task)))))
  527.   
  528.   (defun schedule-task (date &rest task)
  529.     (MidiDTask eval-task date *refnum* (protect task) 0 0))
  530.   )                            ; <== EVAL THESE DEFINITIONS
  531.  
  532.  
  533. ;; macro to schedule expressions
  534.  
  535. (progn
  536.   (defmacro at (date form)
  537.     `(schedule-task ,date ,(symbol-function (car form)) ,@(cdr form)))
  538.   
  539.   (defmacro after (delay form)
  540.     `(schedule-task (+ (MidiGetTime) ,delay) ,(symbol-function (car form)) ,@(cdr form)))
  541.   )                            ; <== EVAL THESE DEFINITIONS
  542.  
  543. ;; usage
  544.  
  545. (install-scheduler)                    ; <== EVALUATE THIS EXPRESSION
  546.  
  547. (after 2000 (print "Wake up !!!!"))            ; <== EVALUATE THIS EXPRESSION
  548.  
  549. (defun multi-print (n delay msg)
  550.   (let ((d (MidiGetTime)))
  551.     (dotimes (i n)
  552.       (at (+ d (* i delay)) (print msg)))))        ; <== EVALUATE THIS DEFINITION
  553.  
  554. (multi-print 10 1000 "hello")                ; <== EVALUATE THIS EXPRESSION
  555.  
  556. (progn
  557.   (multi-print 10 1000 "hello")
  558.   (multi-print 5 2000 "goodby"))            ; <== EVALUATE THIS EXPRESSION
  559.  
  560. (remove-scheduler)                    ; <== EVALUATE THIS EXPRESSION
  561.   
  562.  
  563.  
  564.  
  565. ;;===================================================================================
  566. ;; test 22 : close MidiShare Session
  567. ;;===================================================================================
  568.  
  569.  
  570. (MidiClose *refnum*)