\ vl_instrument \ \ class of hmsl instrument designed to drive the yamaha vl70m. Like its \ superclass, gm.instrument, the vl.instrument class adds additional \ methods to, and deviates from the behavior of hmsl's cannonical \ instrument class. \ \ \ description: \ \ vl_default_chan# ( -- n , value of default midi channel number ) \ \ the vl.instrument bypasses the midi-allocator for channel assignment, and \ any instance will default to the above channel number. We're assuming that \ the vl70m is kept on a specific channel. \ \ RAW.NOTE.ON: ( note# vol -- ) \ \ RAW.NOTE.ON: is redefined to additionally sends midi breath control. \ \ CONTROL: ( n ctrl -- , send control message ) \ CONTROL.ENV: ( n dtime ctrl -- , generate control envelope ) \ \ PUT.CONTROL#: ( CC# ctrl -- , assign midi controller number ) \ GET.CONTROL#: ( ctrl -- CC# ) \ \ PUT.CONTROL.RANGE: ( depth ctrl -- , set controller depth ) \ GET.CONTROL.RANGE: ( ctrl -- depth ) \ \ the above methods alter the vl70m's physical modelling synthesizer. \ \ the instrument automatically assigns default control settings and, if the \ file vl_sysex has been loaded, these are setup via midi system exclusive \ messages to the vl70m. Thus PUT.CONTROL#: method is more useful when \ vl_sysex is _not_ loaded. \ \ in each case, the "ctrl" argument is the controller selector. Constants \ (vl_pressure, vl_growl, etc) have been defined for use as selectors with \ these methods. The constant vl_#ctrl returns number of controllers. \ \ \ Code: Han-earl Park \ Copyright 2001 Buster & Friends C-ALTO Labs \ (Valencia, September 1998 - \ (Southampton, October 2000 - \ \ MOD: HeP 08/09/98 Started project. \ Hardware problems: The serial connection between my mac \ and the vl module doesn't seem to work with either the \ apple midi manager or hmsl's custom driver. It does work \ with oms though. \ MOD: HeP 11/09/98 Mysterious crashes with control.env: (which didn't work \ anyway), but no further crashes since rewriting it. \ MOD: HeP 11/14/98 All controllers tested and found to work. Sysex setup \ works with no problems. \ MOD: HeP 12/11/98 Fix error with preset: method (0 used to select 128). \ ob.vl.scale specific methods are loaded when sysex stuff \ is in dictionary. \ MOD: HeP 02/01/99 put.preset: sends the same message to the fl.scale and \ vl.fx classes if loaded. \ MOD: HeP 02/02/99 Renamed vl.preset as vl.patch, and add the put.patch \ method. Thus no need to override put.preset: \ Instrument sends a reset: message to vl.scale and vl.fx \ obejects for each put.preset: message. \ MOD: HeP 02/05/99 The value of vl_auto_setup? only affects whether the \ objects sends sysex for the reset: method. \ MOD: HeP 02/08/99 Get rid of the constant vl_auto_setup? altogether. \ MOD: HeP 02/16/99 Add UPDATE: that replaces the function of RESET: method. \ RESET: now acts as a kind of panic messgae, and sets the \ device to a "null" state. \ Send PUT.CHANNEL: and OPEN: to new vl.fx and vl.scale \ objects. \ MOD: HeP 07/27/99 Get rid of BEND.ENV: \ MOD: HeP 07/30/99 Add file midi_plus which has words that call standard \ or semi-standard midi controllers. \ MOD: HeP 08/29/99 The value env_resolution moved to the gm_instrument file. \ Get rid of the methods that are now part of the \ gm.instrument (which vl.instrument inherits from). \ Trash the ?mono: and ?poly: methods. \ MOD: HeP 11/12/99 Get rid of the ob.vl.instr synonym. \ MOD: HeP 11/16/99 UPDATE: finally updates control values!!! \ MOD: HeP 11/19/99 PUT.PATCH: sets range of controllers. \ MOD: HeP 11/23/99 Simpler warning is sysex is not loaded with this file. \ MOD: HeP 02/20/00 Move the semi-standard midi controller messages \ (put.brightness: etc) from the vl.instrument to the \ gm.instrument class. \ MOD: HeP 02/29/00 Default offset is an octave above middle C. \ MOD: HeP 04/11/00 Use VL.REPORT.ERROR instead of repeating the error code. \ MOD: HeP 04/15/00 Move the CONTROL: method to the gm.instrument class, we \ then override this method for the vl.instrument. \ CONTROL.ENV: remains defined in the vl.instrument since \ it's harder to make that work with the gm.instrument. \ MOD: HeP 10/03/00 Redefine OPEN: (see file gm_instrument for more details). \ MOD: HeP 10/10/00 UPDATE: sets midi.volume to 127. \ Update comments & docs. \ MOD: HeP 01/23/01 Implement midi device id# stuff. \ MOD: HeP 03-24-09 PUT.PATCH: only updates preset if different from current \ setting. include? task-midi_plus myt:midi_plus include? task-gm_instrument myt:gm_instrument anew task-vl_instrument 1 value vl_default_chan# \ midi channel if not manually assigned \ selectors for vl70m's "physical modelling" controllers 0 constant vl_pressure 1 constant vl_growl 2 constant vl_throat 3 constant vl_scream 4 constant vl_embouchure 5 constant vl_tonguing 6 constant vl_damping 7 constant vl_absorption \ initial default values for controllers 127 value vl_default_pressure 64 value vl_default_growl 64 value vl_default_throat 64 value vl_default_scream 64 value vl_default_embouchure 127 value vl_default_tonguing 127 value vl_default_damping 127 value vl_default_absorption 8 constant vl_#ctrl \ number of available controllers method CONTROL.ENV: method PUT.CONTROL#: method GET.CONTROL#: method PUT.CONTROL.RANGE: method GET.CONTROL.RANGE: method PUT.BANK: method GET.BANK: :class OB.VL.INSTRUMENT iv-vl-bank# \ 72 put.offset: self \ octave above middle C \ \ use "unassigned" midi controllers 012 vl_pressure iv-vl-ctrl# ! 013 vl_growl iv-vl-ctrl# ! 014 vl_throat iv-vl-ctrl# ! 015 vl_scream iv-vl-ctrl# ! 016 vl_embouchure iv-vl-ctrl# ! 017 vl_tonguing iv-vl-ctrl# ! 018 vl_damping iv-vl-ctrl# ! 019 vl_absorption iv-vl-ctrl# ! \ \ max controller depth 127 vl_pressure iv-vl-ctrl-range ! 127 vl_growl iv-vl-ctrl-range ! 127 vl_throat iv-vl-ctrl-range ! 127 vl_scream iv-vl-ctrl-range ! 127 vl_embouchure iv-vl-ctrl-range ! 127 vl_tonguing iv-vl-ctrl-range ! 127 vl_damping iv-vl-ctrl-range ! 127 vl_absorption iv-vl-ctrl-range ! \ \ "null" values vl_default_pressure vl_pressure iv-vl-ctrl-value ! vl_default_growl vl_growl iv-vl-ctrl-value ! vl_default_throat vl_throat iv-vl-ctrl-value ! vl_default_scream vl_scream iv-vl-ctrl-value ! vl_default_embouchure vl_embouchure iv-vl-ctrl-value ! vl_default_tonguing vl_tonguing iv-vl-ctrl-value ! vl_default_damping vl_damping iv-vl-ctrl-value ! vl_default_absorption vl_absorption iv-vl-ctrl-value ! ;m :m UPDATE: ( -- ) update: super \ iv-ins-#open IF midi.instr.set.channel \ done in gm.instrument class!?! \ [ exists? task-vl_sysex .IF ] \ midi.instr.set.id \ vl.setup.ctrl# vl.setup.ctrl.range [ .THEN ] \ vl_#ctrl 0 DO i iv-vl-ctrl# @ i iv-vl-ctrl-value @ midi.control LOOP \ 127 midi.volume THEN ;m \ access bank & preset \ note that the bank will not switch until a new preset# is selected :m PUT.BANK: ( b# -- , set bank number ) dup 1 4 within? IF dup iv=> iv-vl-bank# 1- \ convert to the midi range of 0 to 3 \ midi.instr.set.channel $ 00 $ 21 midi.control $ 20 swap midi.control ELSE " put.bank:" " bank number outside range of 1 to 4" er_warning ob.report.error drop THEN ;m :m GET.BANK: ( -- b# ) iv-vl-bank# ;m \ patch object :m PUT.PATCH: ( addr -- , assign a patch object ) dup iv=> iv-gm-patch \ iv-gm-fx IF dup iv-gm-fx put.patch: [] THEN iv-gm-scale IF dup iv-gm-scale put.patch: [] THEN \ ?dup IF vl_#ctrl 0 DO i over get.control.range: [] i iv-vl-ctrl-range ! LOOP \ dup get.offset: [] put.offset: self dup get.note.range: [] put.note.range: self \ dup get.bend.range: [] 100 * iv=> iv-gm-bend-range \ don't update! \ \ only change preset if patch settings are different \ dup get.preset: [] swap get.bank: [] over get.preset: self = over get.bank: self = AND IF 2drop update: self \ "soft" update self ELSE put.bank: self put.preset: self THEN THEN ;m \ "note" playing :m RAW.NOTE.ON: ( note# vol -- , in addition send breath control ) midi.instr.set.channel dup midi.breath midi.noteon ;m \ midi controllers & mode switches \ physical modelling : VL.REPORT.ERROR ( $ -- , print error message ) " unrecognised controller type" er_warning ob.report.error ; :m CONTROL: ( n ctrl -- , send control message ) dup vl.valid.ctrl? IF midi.instr.set.channel 2dup iv-vl-ctrl# @ swap midi.control iv-vl-ctrl-value ! ELSE " control:" VL.REPORT.ERROR 2drop THEN ;m :m CONTROL.ENV: { n Ętime ctrl | #step -- , generate envelope } ctrl vl.valid.ctrl? IF vtime@ time@ vtime! \ Ętime env_resolution / -> #step \ check env_resolution > 0 #step IF \ non zero steps in envelope... \ calculate interpolation 0 ctrl iv-vl-ctrl-value @ \ previous value at time "zero" Ętime n \ and new value Ętime ticks later set.interp \ 0 \ time "zero" to be passed through the loop #step 0 DO env_resolution vtime+! \ env_resolution + \ current ticks from time "zero" dup interp ctrl control: self LOOP drop THEN \ time@ Ętime + vtime! \ advance virtual time n ctrl control: self \ at least send the last message \ vtime! \ restore virtual time ELSE " control.env:" VL.REPORT.ERROR THEN ;m :m PUT.CONTROL#: ( CC# ctrl -- , assign midi controller number ) dup vl.valid.ctrl? IF [ exists? task-vl_sysex .IF ] \ midi.instr.set.id \ 2dup ( -- m# c# m# c# ) CASE vl_pressure OF vl.set.pressure.ctrl# ENDOF vl_growl OF vl.set.growl.ctrl# ENDOF vl_throat OF vl.set.throat.ctrl# ENDOF vl_scream OF vl.set.scream.ctrl# ENDOF vl_embouchure OF vl.set.embouchure.ctrl# ENDOF vl_tonguing OF vl.set.tonguing.ctrl# ENDOF vl_damping OF vl.set.damping.ctrl# ENDOF vl_absorption OF vl.set.absorption.ctrl# ENDOF ENDCASE [ .THEN ] \ iv-vl-ctrl# ! ELSE " put.control#:" VL.REPORT.ERROR 2drop THEN ;m :m GET.CONTROL#: ( ctrl -- CC# , retrieve midi controller number ) dup vl.valid.ctrl? IF iv-vl-ctrl# @ ELSE " get.control#:" VL.REPORT.ERROR drop THEN ;m :m PUT.CONTROL.RANGE: ( depth ctrl -- , set controller depth ) dup vl.valid.ctrl? IF over -127 127 within? IF [ exists? task-vl_sysex .IF ] \ midi.instr.set.id \ 2dup ( -- d c# d p# ) CASE vl_pressure OF vl.set.pressure.range ENDOF vl_growl OF vl.set.growl.range ENDOF vl_throat OF vl.set.throat.range ENDOF vl_scream OF vl.set.scream.range ENDOF vl_embouchure OF vl.set.embouchure.range ENDOF vl_tonguing OF vl.set.tonguing.range ENDOF vl_damping OF vl.set.damping.range ENDOF vl_absorption OF vl.set.absorption.range ENDOF ENDCASE [ .THEN ] \ iv-vl-ctrl-range ! ELSE " put.control.range:" " range must be within -127 and +127" er_warning ob.report.error 2drop THEN ELSE " put.control.range:" VL.REPORT.ERROR 2drop THEN ;m :m GET.CONTROL.RANGE: ( ctrl -- depth ) dup vl.valid.ctrl? IF iv-vl-ctrl-range @ ELSE " get.control.range:" VL.REPORT.ERROR drop THEN ;m \ pitch bend :m PUT.BEND.RANGE: ( n -- , set bend range to 2n semitones ) dup 0 12 within? IF put.bend.range: super ELSE " put.bend.range:" " the vl70m has a maximum pitch bend range of 12 semitones" er_warning ob.report.error drop THEN ;m \ reset :m RESET: ( -- , reset controllers ) reset: super \ vl_default_pressure vl_pressure control: self vl_default_growl vl_growl control: self vl_default_throat vl_throat control: self vl_default_scream vl_scream control: self vl_default_embouchure vl_embouchure control: self vl_default_tonguing vl_tonguing control: self vl_default_damping vl_damping control: self vl_default_absorption vl_absorption control: self ;m \ print : VL.PRINT.CTRL ( i -- , print info for given controller ) dup iv-vl-ctrl# @ 3 .r dup iv-vl-ctrl-range @ 5 .r iv-vl-ctrl-value @ 5 .r ; :m PRINT: ( -- ) print: super \ ?pause \ ." Yamaha VL70m:" cr \ space ." preset bank = " iv-vl-bank# 3 .r cr \ ?pause \ ." Controllers: # max cur" tab ." # max cur" cr space ." pressure = " vl_pressure vl.print.ctrl tab ." growl = " vl_growl vl.print.ctrl cr space ." throat = " vl_throat vl.print.ctrl tab ." scream = " vl_scream vl.print.ctrl cr space ." embouchure = " vl_embouchure vl.print.ctrl tab ." tonguing = " vl_tonguing vl.print.ctrl cr space ." damping = " vl_damping vl.print.ctrl tab ." absorption = " vl_absorption vl.print.ctrl cr ;m ;class exists? task-vl_sysex NOT .IF cr cr cr space ." The vl70m instrument class has been loaded WITHOUT the sysex utilities!" cr cr cr cr .THEN