\ io \ \ io_output \ output, or 'side effect', generating component of io. \ \ \ this file contains players, interpreters and instruments which are driven \ by the hp_ components (see: io_hp). One exception to this component \ interaction is that the pulse value is derrived from the pulse.trackers \ specified in the io_input file. \ \ io_input io_matrix io_hp =======> io_output io_top \ ^^^^^^^^ ^^^^^ ^^^^^^^^^ \ pTracker ----------------------------------> player \ \ \ note that the actual interpreters are defined in modules:io_interp; the \ player classes are in modules:io_player; and the midi preset selection is \ in modules:io_patches. \ \ \ see the file io_top for more information. \ \ \ constructor: Han-earl Park \ copyright 2008 buster & friends' C-ALTO Labs \ \ www.busterandfriends.com/io \ \ (Edinburgh, November 1996 - \ (London, August 1997 - \ (Den Haag, October 1997 - \ (Valencia, March 1999 - \ (Southampton, May 2000 - \ (Cork, April 2006 - \ \ (Cork, October 2008 - \ \ REV: 0.0.1 alpha (Southampton, October 2000) \ REV: 0.0.1 beta (Southampton, November 2000) \ REV: 0.0.1 alpha++ (Southampton, July 2004) \ REV: 0.0.1 beta++ (Cork, May 2010) \ \ \ MOD: HeP 03/05/99 Started project afresh! \ This version keeps most of the "intelligence" in the \ objects, while the piece specific elements are kept to a \ minimum. It is also a test for the "laurie" project. \ MOD: HeP 01/26/00 Only load objects:io_patches if io_vl70m is true. \ MOD: HeP 02/07/00 Get rid of conditional compilation of the multiple \ instrument objects -- they all get compiled now. \ MOD: HeP 02/21/00 Add the global control word IO.STANDBY and corresponding \ words to each component. \ MOD: HeP 03/27/00 Scramble the vl70m controllers used by each interpreter. \ MOD: HeP 04/16/00 Load the file myt:ctrl_interpreter. This class should make \ easier the swapping of controllers between interpreters. \ MOD: HeP 04/17/00 Add the modules:io_interp component. \ MOD: HeP 05/02/00 Move players from io_hp. \ Also move the hp_ graphics from io_hp since these qualify \ as 'side effects' of the system. \ MOD: HeP 05/03/00 Maximum of 5 note players and interpreters. \ MOD: HeP 05/05/00 The interpreters' assigned control# are shuffled. \ MOD: HeP 05/31/00 Assume that the static.particle will be held in the first \ element of the space. \ MOD: HeP 06/01/00 Implement SET.OUTPUT and related configuration words. \ MOD: HeP 06/05/00 Use the particle-holder to hookup particles to players and \ interpreters. Thus no longer have to assume that the \ static.particle is held in element# 0 of io-space. \ MOD: HeP 06/06/00 Clear any previous interpreter assignments to particles \ before adding new ones. \ MOD: HeP 06/06/00 Add IO.PAN.INTERP class. See file modules:io_interp. \ MOD: HeP 06/07/00 Implement stereo pan control. \ MOD: HeP 06/08/00 Fix silly stack error in IO.DUR.FUNC. \ MOD: HeP 06/22/00 There's a bug related to START: or STOP:ing io-space. \ As far as I can work out this has to do with OPEN: and \ CLOSE:ing the gr.particles -- these objects don't check if \ window is open before drawing! \ hp_ graphics only load if io_test? and io_screen? are both \ set to true. \ MOD: HeP 06/23/00 No instrument assignment to some interpreters caused an \ invalid object error. See modules:io_interp for the fix. \ MOD: HeP 06/26/00 Able to mute output by setting mute_output? to true -- \ fixes the problem when running without a MIDI interface, \ and the MIDI bytes overwhelms the serial buffer. \ MOD: HeP 07/09/00 Setup patches during compilation. See modules:io_patches. \ MOD: HeP 07/16/00 Add provisions for supporting gm patches. \ MOD: HeP 09/22/00 MIDI.SER.TERM disables MIDI instead of the manual way. \ MOD: HeP 09/26/00 Don't select patches for vl and gm instruments at the same \ time since this'll send a bunch of (conflicting) midi data \ out. \ Trash CHOOSE.PATCH etc from modules:io_patches. \ MOD: HeP 10/14/00 Separate variable io-paused? for indicating paused state. \ This prevents players from starting when we resume after \ a pause. \ REV: 0.0.1 alpha __________________________________________________________ \ MOD: HeP 11/02/00 Update doc and comments. \ MOD: HeP 11/05/00 Use NEXT.ON.DUR@ in the player's duration function. \ MOD: HeP 11/06/00 io's rhythmic sense is pretty good now. \ MOD: HeP 11/10/00 Fades out when performance ends. \ MOD: HeP 11/15/00 IO.INTERP.RESET calls SCRAMBLE.DRUMS if io-dr-instr is \ being used. \ MOD: HeP 11/20/00 Call the alert-matrix in the player's duration function if \ input is not present. \ REV: 0.0.1 beta __________________________________________________________ \ MOD: HeP 01/23/01 IO.OUTPUT.DEFAULT does not pass DEFAULT: messages to \ instruments since this resets midi device id# of \ io-gm-instr and io-vl-instr. \ MOD: HeP 03-23-04 Experimental changes to IO.DUR.FUNC. \ MOD: HeP 03-24-04 Players EXECUTE: ALERT-MATRIX less often (the use of \ ob.fpgravity means that io's chaos is "smoother" and we \ don't need that extra interest injection). \ MOD: HeP 04-09-04 Set instrument/channel volume via the PUT.VOLUME: method. \ FADE.OUT also uses the PUT.VOLUME: method. \ MOD: HeP 04-11-04 Current reverb and pan settings are stored in variables. \ The reverb enable/bypass is done via the instrument's \ open function. \ Set device id# for MIDI output. \ MOD: HeP 05-01-04 Move all includes (and conditional includes) from the \ individual files to load_io. \ MOD: HeP 05-11-04 Updated the description/comment. \ MOD: HeP 05-19-04 Experimental "muti-tasking" version of FADE.OUT which \ keeps output activity alive while executing the fade. \ See modules:io_interp. \ MOD: HeP 05-23-04 The new version of FADE.OUT (in modules:io_interp) results \ in having to reset channel volume in IO.OUTPUT.STANDBY \ instead of at end of IO.OUTPUT.STOP. \ MOD: HeP 07-03-04 Each component's .INIT function no longer calls the \ corresponding .DEFAULT function. Instead these are all \ called at the end of IO.INIT. See file io_top. \ REV: 0.0.1 a ++ __________________________________________________________ \ MOD: HeP 11-09-08 Use table lookup to derive new pulse and pulse groupings. \ MOD: HeP 03-21-09 Add (MODIFY.PATCH) called by the meta-alert-matrix. See: \ io_matrix. \ MOD: HeP 03-24-09 Change to ob.vl.instruments's PUT.PATCH: method (see: \ myt:vl_instrument). \ REV: 0.0.1 b ++ __________________________________________________________ \ Version for performance at Blackrock Castle Observatory, \ Cork, Ireland, May 25, 2010. \ \ \ ToDo: More consistent and comprehensive error reporting. \ ToDo: The funny place to reset volume seems to indicate that we need another \ "state" for io. Something like IO.IDLE ...? anew task-io_output \ reverb and pan variable use-pan? variable use-reverb? \ players 2 constant vl_#players 3 constant gm_#players 5 constant dr_#players ob.objlist io-player-holder ob.io.player io-player-1 ob.io.player io-player-2 ob.io.player io-player-3 ob.io.player io-player-4 ob.io.player io-player-5 \ note playing interpreters ob.objlist note-interp-holder ob.io.note.interp.1 note-interp-1 ob.io.note.interp.2 note-interp-2 ob.io.note.interp.2 note-interp-3 \ controller interpreters ob.objlist ctrl-interp-holder ob.io.ctrl.interp.1 ctrl-interp-1 ob.io.ctrl.interp.2 ctrl-interp-2 ob.io.ctrl.interp.2 ctrl-interp-3 \ drum playing interpreters ob.objlist drum-interp-holder ob.interpreter drum-interp-1 ob.interpreter drum-interp-2 ob.interpreter drum-interp-3 ob.interpreter drum-interp-4 ob.interpreter drum-interp-5 \ stereo pan interpreter ob.io.pan.interp pan-interp \ device drivers variable io-instr \ holds current instrument's address ob.vl.instrument io-vl-instr ob.gm.instrument io-gm-instr ob.gm.drumkit io-dr-instr \ graphics io_test? io_screen? AND .IF ob.gr.space+ io-grspace ob.objlist grparticle-holder ob.objlist grfout-holder .THEN \ players \ pulse & duration : PLAYER.PULSE! ( ticks -- , set all players' pulse value ) many: io-player-holder 0 DO dup i get: io-player-holder put.pulse: [] LOOP drop ; false .IF \ old version : PLAYER.PULSE@ ( -- ticks , player's current pulse value ) input.present? IF pulse@ ?dup IF dup current.object put.pulse: [] ELSE current.object get.pulse: [] THEN ELSE 32 choose IF current.object get.pulse: [] ELSE 2 choose 0= \ MOD: 03-24-04 IF many: alert-matrix choose execute: alert-matrix THEN [ io_max_pulse io_min_pulse - ] literal choose choose io_min_pulse + dup PLAYER.PULSE! THEN THEN ; .THEN \ new version : PLAYER.PULSE@ ( -- ticks , player's current pulse value ) input.present? IF pulse@ ?dup IF dup current.object put.pulse: [] ELSE current.object get.pulse: [] THEN ELSE 32 choose IF current.object get.pulse: [] ELSE 2 choose 0= \ MOD: 03-24-04 IF many: alert-matrix choose execute: alert-matrix THEN choose.pulse dup PLAYER.PULSE! THEN THEN ; \ *** should we take into account the hp_ coord for duration? *** \ old version false .IF : IO.DUR.FUNC ( elm# sh -- dur ) 2drop \ 4 choose IF player.pulse@ 4 choose choose 1+ * ELSE next.on.dur@ ?dup NOT IF player.pulse@ 8 choose choose choose 1+ * THEN THEN ; .THEN : IO.DUR.FUNC ( elm# sh -- dur ) 2drop \ 4 choose IF player.pulse@ choose.pulse.group * ELSE next.on.dur@ ?dup NOT IF player.pulse@ choose.pulse.group * THEN THEN ; \ select players : HOOKUP.PLAYERS ( -- ) many: io-player-holder 0 DO i get: io-player-holder i get: note-interp-holder over put.instrument: [] \ dup empty: [] \ make space for particle i get: particle-holder swap add: [] \ add particle to player LOOP ; : USE.VL.PLAYERS ( -- , setup for use with the vl70m ) vl_#players set.many: io-player-holder hookup.players ; : USE.GM.PLAYERS ( -- , ...a General MIDI device ) gm_#players set.many: io-player-holder hookup.players ; : USE.DR.PLAYERS ( -- , ...GM drumkit ) dr_#players set.many: io-player-holder \ many: io-player-holder 0 DO i get: io-player-holder i get: drum-interp-holder over put.instrument: [] \ dup empty: [] \ make space for particle i get: particle-holder swap add: [] \ add particle to player LOOP ; \ setup & clearup players : IO.PLAYER.INIT ( -- ) sub" io.player.init" \ 5 new: io-player-holder \ io-player-1 add: io-player-holder io-player-2 add: io-player-holder io-player-3 add: io-player-holder io-player-4 add: io-player-holder io-player-5 add: io-player-holder \ many: io-player-holder 0 DO i get: io-player-holder 1 over new: [] 'c io.dur.func swap put.dur.function: [] LOOP ; : IO.PLAYER.TERM ( -- ) sub" io.player.term" \ max.elements: io-player-holder set.many: io-player-holder freeall: io-player-holder \ free: io-player-holder ; : IO.PLAYER.RESET ( -- ) sub" io.player.reset" \ choose.pulse PLAYER.PULSE! ; \ interpreters \ shuffle interpreters : SWAP.CTRL# { interp1 interp2 | elm1 elm2 -- } get.#controllers: interp1 choose -> elm1 get.#controllers: interp2 choose -> elm2 \ elm1 get.control#: interp1 elm2 get.control#: interp2 \ elm1 put.control#: interp1 elm2 put.control#: interp2 ; : SCRAMBLE.CTRL# { | ctrl -- , shuffle controller numbers } 0 -> ctrl \ many: ctrl-interp-holder 0 DO i get: ctrl-interp-holder dup get.#controllers: [] 0 DO dup ctrl i rot put.control#: [] ctrl 1+ -> ctrl LOOP drop LOOP \ ctrl 0 DO many: ctrl-interp-holder choose get: ctrl-interp-holder many: ctrl-interp-holder choose get: ctrl-interp-holder \ SWAP.CTRL# LOOP ; : SWAP.DRUMS { elm1 elm2 -- } elm1 get: drum-interp-holder elm2 get: drum-interp-holder \ elm1 put: drum-interp-holder elm2 put: drum-interp-holder ; : SCRAMBLE.DRUMS ( -- , shuffle drum voices ) many: drum-interp-holder 0 DO many: drum-interp-holder choose many: drum-interp-holder choose \ SWAP.DRUMS LOOP ; \ stereo pan interpreter : ENABLE.PAN ( -- ) true use-pan? ! pan-interp 0 get: particle-holder put.instrument: [] ; : DISABLE.PAN ( -- ) false use-pan? ! 0 0 get: particle-holder put.instrument: [] ; \ enable or bypass reverb : REVERB.FUNC ( instr -- ) get.channel: [] midi.channel! 0 midi.rev.send ; : ENABLE.REVERB ( -- ) true use-reverb? ! 0 io-instr @ put.open.function: [] ; : DISABLE.REVERB ( -- ) false use-reverb? ! 'c reverb.func io-instr @ put.open.function: [] ; : RESET.REVERB ( -- , switch reverb on/off depending on variable ) use-reverb? @ 0= IF io-instr @ reverb.func THEN ; \ select interpreters : CLEAR.INTERP ( -- , clear any interpreter assignment to particles ) many: particle-holder 0 DO 0 i get: particle-holder put.instrument: [] LOOP ; : HOOKUP.INTERP ( -- ) many: ctrl-interp-holder 0 DO i get: ctrl-interp-holder \ i many: io-player-holder + \ account for particles used by players get: particle-holder put.instrument: [] LOOP ; : USE.VL.INTERP ( -- , setup for use with the vl70m ) 3 set.many: ctrl-interp-holder \ 3 put.#controllers: ctrl-interp-1 3 put.#controllers: ctrl-interp-2 2 put.#controllers: ctrl-interp-3 \ scramble.ctrl# \ io-vl-instr put.instrument: ctrl-interp-1 io-vl-instr put.instrument: ctrl-interp-2 io-vl-instr put.instrument: ctrl-interp-3 \ io-vl-instr put.instrument: note-interp-1 io-vl-instr put.instrument: note-interp-2 \ clear.interp hookup.interp \ use-pan? @ IF enable.pan THEN \ io-vl-instr put.instrument: pan-interp ; : USE.GM.INTERP ( -- , ...a General MIDI device ) 2 set.many: ctrl-interp-holder \ 3 put.#controllers: ctrl-interp-1 2 put.#controllers: ctrl-interp-2 \ scramble.ctrl# \ io-gm-instr put.instrument: ctrl-interp-1 io-gm-instr put.instrument: ctrl-interp-2 io-gm-instr put.instrument: ctrl-interp-3 \ io-gm-instr put.instrument: note-interp-1 io-gm-instr put.instrument: note-interp-2 io-gm-instr put.instrument: note-interp-3 \ clear.interp hookup.interp \ use-pan? @ IF enable.pan THEN \ io-gm-instr put.instrument: pan-interp ; : USE.DR.INTERP ( -- , ...GM drumkit ) scramble.drums \ clear.interp ; \ setup & clearup interpreters : IO.INTERP.INIT ( -- ) sub" io.interp.init" \ 3 new: ctrl-interp-holder ctrl-interp-1 add: ctrl-interp-holder ctrl-interp-2 add: ctrl-interp-holder ctrl-interp-3 add: ctrl-interp-holder \ 3 new: note-interp-holder note-interp-1 add: note-interp-holder note-interp-2 add: note-interp-holder note-interp-3 add: note-interp-holder \ 5 new: drum-interp-holder drum-interp-1 add: drum-interp-holder drum-interp-2 add: drum-interp-holder drum-interp-3 add: drum-interp-holder drum-interp-4 add: drum-interp-holder drum-interp-5 add: drum-interp-holder \ io-dr-instr put.instrument: drum-interp-1 io-dr-instr put.instrument: drum-interp-2 io-dr-instr put.instrument: drum-interp-3 io-dr-instr put.instrument: drum-interp-4 io-dr-instr put.instrument: drum-interp-5 \ 'c snare.interp put.on.function: drum-interp-1 'c kick.interp put.on.function: drum-interp-2 'c hihat.interp put.on.function: drum-interp-3 'c cymbal.interp put.on.function: drum-interp-4 'c tom.interp put.on.function: drum-interp-5 ; : IO.INTERP.TERM ( -- ) sub" io.interp.term" \ freeall: ctrl-interp-holder \ free: ctrl-interp-holder free: note-interp-holder free: drum-interp-holder ; : IO.INTERP.RESET ( -- ) sub" io.interp.reset" \ io-instr @ io-dr-instr = IF scramble.drums ELSE scramble.ctrl# THEN ; \ instruments \ select instrument : USE.VL.INSTR ( -- ) io-vl-instr io-instr ! use-reverb? @ IF enable.reverb ELSE disable.reverb THEN \ [ io_4gel? .IF ] portamento.on: io-vl-instr [ .THEN ] ; : USE.GM.INSTR ( -- ) io-gm-instr io-instr ! use-reverb? @ IF enable.reverb ELSE disable.reverb THEN ; : USE.DR.INSTR ( -- ) io-dr-instr io-instr ! ; \ setup & clearup instruments : IO.INSTR.INIT ( -- ) sub" io.instr.init" \ \ *** io_output_chan# put.channel: io-gm-instr \ not necessary since done \ *** io_output_chan# put.channel: io-vl-instr \ in io.output.default \ 127 put.volume: io-gm-instr 127 put.volume: io-vl-instr \ mono: io-gm-instr 2 put.#voices: io-gm-instr \ extended.set: io-dr-instr ; : IO.INSTR.TERM ( -- ) sub" io.instr.term" ; : IO.INSTR.RESET ( -- ) sub" io.instr.reset" \ RESET.PATCHES \ randomize patch selector ; \ graphics io_test? io_screen? AND .IF : HOOKUP.HPGR ( -- ) many: io-space dup set.many: io-grspace \ set number of gr.particles 0 DO i get: grfout-holder \ retrieve fan.out \ dup empty: [] \ reset fan.out \ i get: io-space get.instrument: [] ?dup IF 2dup = NOT \ not already set to fan.out IF over add: [] \ add instrument to fan.out ELSE drop THEN THEN i get: grparticle-holder over add: [] \ add gr.particle i get: io-space put.instrument: [] \ assign as instrument LOOP ; : IO.HPGR.INIT ( -- ) max_#particles new: io-grspace " io hp" put.title: io-grspace ascii H put.key: io-grspace \ 4 5 -1 put.scale: io-grspace \ 'c ob.gr.particle+ max_#particles ?instantiate: grparticle-holder IF max_#particles 0 DO i get: grparticle-holder add: io-grspace LOOP ELSE " Sorry, an error occurred while trying to create graphical particles. Quit and try restarting the program." init.error THEN \ 'c ob.fan.out max_#particles ?instantiate: grfout-holder IF max_#particles 0 DO 2 i get: grfout-holder new: [] LOOP ELSE " Sorry, an error occurred while trying to create graphical fan.out objects. Quit and try restarting the program." init.error THEN ; : IO.HPGR.TERM ( -- ) max.elements: grfout-holder set.many: grfout-holder freeall: grfout-holder \ free: io-grspace free: grparticle-holder free: grfout-holder ; : PRINT... ( -- ) cr many: io-space 0 DO i get: io-space dup ob.name space dup .class: [] tab get.instrument: [] dup ob.name space .class: [] cr LOOP >newline ." Press any key to continue..." key drop cr many: grfout-holder 0 DO i get: grfout-holder print: [] LOOP ; .THEN \ configure output : USE.VL.OUTPUT ( -- ) use.vl.players use.vl.interp use.vl.instr ; : USE.GM.OUTPUT ( -- ) use.gm.players use.gm.interp use.gm.instr ; : USE.DR.OUTPUT ( -- ) use.dr.players use.dr.interp use.dr.instr ; : SET.OUTPUT ( type -- , configure output device ) CASE vl_instr OF use.vl.output ENDOF gm_instr OF use.gm.output ENDOF dr_instr OF use.dr.output ENDOF ENDCASE \ [ io_test? io_screen? AND .IF ] hookup.hpgr [ .THEN ] ; \ called by io.play : SETUP.OUTPUT.PATCH ( -- , select patch for playing ) io-instr @ CASE io-vl-instr OF next.vl.patch put.patch: io-vl-instr ENDOF io-gm-instr OF next.gm.patch put.patch: io-gm-instr ENDOF ENDCASE ; \ meta-alert-matrix function (see: io_matrix) : (MODIFY.PATCH) ( -- ) setup.output.patch reset.reverb ; \ system control : IO.OUTPUT.DEFAULT ( -- ) \ default: io-vl-instr \ interferes with the device id# and channel# \ default: io-gm-instr \ default: io-dr-instr \ use_pan? use-pan? ! use_reverb? use-reverb? ! \ io_instr SET.OUTPUT \ io_output_chan# put.channel: io-vl-instr io_output_chan# put.channel: io-gm-instr io_output_device_id# put.device.id: io-vl-instr ; : IO.OUTPUT.RESET ( -- ) test" IO.OUTPUT.RESET" \ io.player.reset io.interp.reset io.instr.reset ; : IO.OUTPUT.UPDATE ( -- ) io-instr @ update: [] ; : IO.OUTPUT.PANIC ( -- ) io-instr @ dup panic: [] reset: [] ; : IO.OUTPUT.STANDBY ( -- ) choose.pulse PLAYER.PULSE! \ \ the following channel volume reset used to be at end of IO.OUTPUT.STOP, but \ since the new "muti-tasking" version of FADE.OUT, this would sometimes \ bring out unwanted notes after the fade was complete. So move it here... \ io-instr @ io-dr-instr = NOT IF 127 io-instr @ put.volume: [] THEN ; : IO.OUTPUT.START ( -- ) many: io-player-holder 0 DO i get: io-player-holder start: [] LOOP ; : OUTPUT.IMBNF ( -- , it must be nearly finished ) ; : IO.OUTPUT.STOP ( -- ) io-instr @ io-dr-instr = NOT IF pulse@ dup 0= IF drop many: io-player-holder choose get: io-player-holder get.pulse: [] THEN io-instr @ \ fade.out \ pulse instr -- THEN \ many: io-player-holder 0 DO i get: io-player-holder stop: [] LOOP ; : IO.OUTPUT.PAUSE ( -- ) many: io-player-holder 0 DO i get: io-player-holder stop: [] LOOP ; : IO.OUTPUT.RESUME ( -- ) io.playing? IF many: io-player-holder 0 DO i get: io-player-holder start: [] LOOP THEN ; \ setup & clearup : IO.OUTPUT.INIT ( -- ) test" IO.OUTPUT.INIT" \ IO.PATCHES.INIT \ initialize patch selector \ io.instr.init io.interp.init io.player.init \ many: note-interp-holder 0 DO i get: note-interp-holder i get: io-player-holder put.instrument: [] LOOP \ [ io_test? io_screen? AND .IF ] io.hpgr.init [ .THEN ] \ \ io.output.default \ removed MOD: 07-03-04 ; : IO.OUTPUT.TERM ( -- ) test" IO.OUTPUT.TERM" \ IO.PATCHES.TERM \ io.instr.term io.interp.term io.player.term \ [ io_test? io_screen? AND .IF ] io.hpgr.term [ .THEN ] ; if.forgotten io.output.term io_test? .IF : IO.OUTPUT.PRINT ( -- ) >newline ." IO_OUTPUT" cr ." Instrument" cr ." current instr = " io-instr @ ob.name cr >newline ." Print objects?" y/n cr IF >newline ." Print players?" y/n cr IF many: io-player-holder 0 DO i get: io-player-holder print: [] ?pause >newline LOOP THEN >newline ." Print note interpreters?" y/n cr IF many: note-interp-holder 0 DO i get: note-interp-holder print: [] ?pause >newline LOOP THEN >newline ." Print ctrl interpreters?" y/n cr IF many: ctrl-interp-holder 0 DO i get: ctrl-interp-holder print: [] ?pause >newline LOOP THEN >newline ." Print drum interpreters?" y/n cr IF many: drum-interp-holder 0 DO i get: drum-interp-holder print: [] ?pause >newline LOOP THEN >newline ." Print instruments?" y/n cr IF print: io-vl-instr ?pause >newline print: io-gm-instr ?pause >newline print: io-dr-instr ?pause >newline THEN THEN ; .THEN