- [Function]
(
set-receiver!
hook stream)
Installs hook to receive stream data as soon as
it arrives from an external application. No values are returned
by set-receiver!
or
by hook. Receiving remains in effect until canceled by
remove-receiver!.
The calling syntax of hook depends on stream:
- If stream is a jackmidi-stream then hook is passed two arguments: The incoming MIDI message and its millisecond timestamp.
- If stream is a portmidi-stream (unsupported in cm-incudine!) then hook is passed two arguments, the values of which depend on the receive mode of stream.
- If stream is a midishare-stream (unsupported in cm-incudine!) then hook is passed one argument, a MidiEv foreign object.
- If stream is an sc-stream then (not yet implemented in cm-incudine!).
- If stream is boolean true then hook is a thunk (a function of no arguments) that will be called every millisecond. The hook is responsible for reading whatever ports are necessary. See receive for information about explicitly reading input from streams.
Implementation and Limitations:
Receiving is implemented in the following Lisp/OS combinations (the following only applies to the original version of cm from 2007!):
- OpenMCL on OS X
- SBCL (0.9.0 or higher) on Linux
- CMUCL (19b or higher) on Linux
- Gauche Scheme (planned)
From the caller's perspective hook appears to receive data asynchronously but actually receiving is always polling in some manner; how this polling is implemented depends on what facilities the host Lisp provides. CM utilizes two different strategies for implementing non-blocking receiving:
- Periodic polling [OpenMCL, CMUCL, SBCL]: the host Lisp provides the ability to periodically call a function in the main Lisp process without blocking the REPL.
- Threaded polling [OpenMCL, Gauche]: each receiver runs in its own native thread.
Examples:
Example 0. Receiving input from Jackmidi (cm-incudine).
;;; open with no latency for interactive work. the input and ;;; output device ids to open will depend on your MIDI setup. (define *midi-in* (jackmidi:open :direction :input) (define *incudine-stream* (new incudine-stream)) ;;; a midi through (set-receiver! (lambda (mm ms) ms (output mm :to *incudine-stream*)) *midi-in*) ;;; play your keyboard...then (remove-receiver! *midi-in*) ;;; make a harmonizer (set-receiver! (lambda (mm ms) ms (if (or (note-on-p mm) (note-off-p mm)) (let ((k (note-on-key mm))) (output mm :to *incudine-stream*) (output (midi-copy-message mm :data1 (+ k 3)) :to *incudine-stream*) (output (midi-copy-message mm :data1 (+ k 7)) :to *incudine-stream*)))) *midi-in*) ;;; play your keyboard...then (remove-receiver! *midi-in*) (jackmidi:close *midi-in*)
Example 1. Receiving input from Portmidi.
;;; open with no latency for interactive work. the input and ;;; output device ids to open will depend on your MIDI setup. (define *pm* (portmidi-open :input 1 :output 3 :latency 0)) ;;; a midi through (set-receiver! (lambda (mm ms) ms (output mm :to *pm*)) *pm*) ;;; play your keyboard...then (remove-receiver! *pm*) ;;; make a harmonizer (set-receiver! (lambda (mm ms) ms (if (or (note-on-p mm) (note-off-p mm)) (let ((k (note-on-key mm))) (output mm :to *pm*) (output (midi-copy-message mm :data1 (+ k 3)) :to *pm*) (output (midi-copy-message mm :data1 (+ k 7)) :to *pm*)))) *pm*) ;;; play your keyboard...then (remove-receiver! *pm*) (portmidi-close)
Example 2. Using a receiver to trigger processes in rts
.
(define (tonto len knum wai amp) (process repeat len output (new midi :time (now) :duration .1 :amplitude amp :keynum (between knum (+ knum 12))) wait wai)) (define (trigger mm ms) ms (if (note-on-p mm) (sprout (tonto 10 (note-on-key mm) .1 (/ (note-on-velocity mm) 127.0))))) (define *midi-in* (jackmidi:open :direction :input) (define *incudine-stream* (new incudine-stream)) (set-receiver! #'trigger *midi-in*) ;;; play your keyboard...then (remove-receiver! *midi-in*) (rts-stop) (portmidi-close)