Cabbage Logo
Back to Cabbage Site

Meters

I’ve just pushed through two new widgets for horizontal and vertical metering. Instead of messing with tables you can just set the current level using a chnset as you would do a slider. They are much faster to implement, and much less of a CPU hog in comparison to the gentable method. I just need to update the docs now. I left out any text or markings as I figure this is easily added on a case by case basis.

p.s. the gif below doesn’t show the gradients correctly, but on my machine each colour runs smoothly into the next.

1 Like

Awesome!

i add the vertical meters to the vst, the vst have sound,load instruments, but the meters just do that random movement from the example even if im not playing, if i change numbers on the score it will stop moving, or if i change the code the vst wont load, i look thru the forum and i saw another example with outputs , i change them but no luck, so what exactly i have to change so it work with my vst,thanx

Can you post some code so we can see what you’re doing…

vmeter bounds(944, 8, 23, 68) channel(“vMeter1”) value(0.362802) overlayColour(70, 53, 53, 255) outlineThickness(0) meterColour:0(255, 0, 0, 255) meterColour:1(255, 0, 255, 255) meterColour:2(255, 255, 0, 255) meterColour:3(0, 255, 255, 255) meterColour:4(0, 0, 255, 255)
vmeter bounds(982, 8, 24, 68) channel(“vMeter2”) value(0.819532) overlayColour(70, 53, 53, 255) outlineThickness(0) meterColour:0(255, 0, 0, 255) meterColour:1(255, 0, 255, 255) meterColour:2(255, 255, 0, 255) meterColour:3(0, 255, 255, 255) meterColour:4(0, 0, 255, 255)
image bounds(938, 4, 36, 75) channel(“image328ce6c9ca1e4925879b765812e66900”) file(“grill.png”)
image bounds(978, 4, 36, 75) channel(“image328ce6c9ca1e4925879b765812e66900”) file(“grill.png”)



-n -d -+rtmidi=NULL -M0 --midi-key=4 --midi-velocity-amp=5 -m0d

sr = 44100
ksmps = 32
nchnls = 2
0dbfs = 1

; Assign midi channel 10 to the midi triggered instrument (instr 1)
massign 10, 1

;gSSamplePath init “”
giTable1 init 100
giTable2 init 101

giIndxTabGen[] init 6 ; Table containing the reference (number) to the GEN01 tables (mono or left channel)
giIndxTabGenR[] init 6 ; Table containing the reference (number) to the GEN01 tables (right channel if exists)
giNumChannel[] init 6 ; Table containing the number of channels for each sample
; giReady[] init 6 ; not used !!!

;-------------------------------------------------
; Extract Filename from the whole path
;-----------------------------------------------------
opcode FileNameFromPath,S,S ; Extract a file name (as a string) from a full path (also as a string)
Ssrc xin ; Read in the file path string
icnt strlen Ssrc ; Get the length of the file path string

ESCAPE: ; Escape point once the backslash has been found
Sname strsub Ssrc, icnt+1, -1 ; Create a new string of just the file name
xout Sname ; Send it back to the caller instrument
endop

opcode CleanStrng, S, S ;returns the ASCII numbers of the input string as string
reinit Toto1
Toto1:
ipos init 0
rireturn
Sin xin ;input string
ilen strlen Sin ;its length
ipos = 0 ;set counter to zero
Sres = “” ;initialize output string
loop: ;for all characters in input string:
ichr strchar Sin, ipos ;get its ascii code number
Stemp strsub Sin,ipos,ipos+1 ;extract the char
if ichr ==92 then ; if it is a backslash
Stemp ="/" ; replace it with a slash
endif
Sres strcat Sres, Stemp ;append this to the output string
loop_lt ipos, +1, ilen, loop ;see comment for ‘loop:’
xout Sres ;return output string
endop

opcode MyReverb,aa,aakkkk ;Opcode for adding a reverb and panning to the entry signal
aInL,aInR,kRoom,kDamp,kMix,kPan xin
aRvbL,aRvbR freeverb aInL, aInR,kRoom, kDamp
aOut1 = kMixaRvbL+(1-kMix)aInL
aOut2 = kMix
aRvbR+(1-kMix)aInR
aOut1= sqrt(1-kPan)
(aOut1 ) ; pan the signal using a sqrt rule
aOut2= sqrt(kPan)
(aOut2 )
xout aOut1,aOut2 ; send audio to outputs
endop

;------------------------------------------------------------------
;always on instrument that waits for user to specify sample folder
;------------------------------------------------------------------
instr 100
SFilepath chnget “sampleFolderButton”
if changed:k(SFilepath) == 1 then
reinit TheFilePathreinit
TheFilePathreinit:
gSSamplePath CleanStrng SFilepath
rireturn
event “i”, 90, 0, 0.1
endif
endin

instr 4

if metro(20) == 1 then
chnset abs(randi:k(1.1, 100, 2)), “vMeter1”
chnset abs(randi:k(1.1, 100, 2)), “vMeter2”
endif
endin

;------------------------------------------------------------------
; READ a directory and load samples into gen tables
; when user has selected folder, load them into function tables.
; it will create the number of tables accordingly to the number
; of channels of the sample
;-----------------------------------------------------------------
instr 90
prints “Loading files to function tables\n”
gSFiles[] directory gSSamplePath , “.wav” ; Populate a table with the file names
iCnt init 0
while iCnt<6 do ; Let’s work with the 6 first samples in the table
printf_i “Loading :%s\n”,iCnt,gSFiles[iCnt]
;--------------------------------------------------------------
; Note :the function tables are indexed starting 100 -> 111
; and the indexes are stored in table
; giIndxTabGen[] : mono or left channel for stereo
; giIndxTabGenR[] : right channel (if exists)
;--------------------------------------------------------------

giNumChannel[iCnt] filenchnls gSFiles[iCnt] ; Read how many channel in sample and store it in table
if giNumChannel[iCnt] ==2 then ; test if sample is stereo
giIndxTabGen[iCnt] ftgen 100+iCnt,0,0,1,gSFiles[iCnt],0,0,1 ; The left channel is stored into the table, tables numbers from 100 -> 111
giIndxTabGenR[iCnt] ftgen 200+iCnt,0,0,1,gSFiles[iCnt],0,0,2 ; a similar table receives the right channel, tables numbers from 200 -> 211
else ; Case of Mono sample
giIndxTabGen[iCnt] ftgen 100+iCnt,0,0,1,gSFiles[iCnt],0,0,1 ; the only one channel is store in the table
endif
iCnt+=1 ; next sample
od
event_i “i”, 102, 1, 0 ; now it is finished : Lets fill up the sound filers
endin

;---------------------------------------------------------
;once files have been loaded to tables, update soundfilers
instr 102
iCnt init 0
while iCnt<6 do
SMessage sprintf “file(”%s")", gSFiles[iCnt]
printf_i “%s\n”,iCnt,gSFiles[iCnt]
SChannel sprintf “filer%d”, iCnt
chnset SMessage, SChannel
SLabel sprintfk “FilenameLabel%d”, iCnt
Smessage FileNameFromPath gSFiles[iCnt]
Smessage3 sprintfk “text(%s)”,Smessage
chnset Smessage3 ,SLabel
; giReady[iCnt] = 1
iCnt+=1
od
endin

;-------------------------------------------------------------------
;this instrument get triggered by the keyboard. It listens for
;a keypress and then trigger instr 3 to play the associated sample
;-------------------------------------------------------------------
instr 1
;----------------------------------------------------------------------
; Call instr 3 with Midi Note ,Velocity, index (0 -> 6)for widget and Gen01 table
;----------------------------------------------------------------------

; kick 1 NOTE 36
if p4 == 36 then ;check midi note cf GM midi drum table
chnset “value(1)”,“bng0” ; blink the little light on screen
a11 ,a12 subinstr 3,p4,p5,0 ;call inst 3 with corresponding table index
outs a11,a12 ;output the sound !!!
endif
; snare 1 NOTE 37
if p4 == 37 then
chnset “value(1)”,“bng1”
a11,a12 subinstr 3,p4,p5,1
outs a11,a12
endif
; openhi NOTE 39 openhi
if p4 == 39 then
chnset “value(1)”,“bng2”
a11,a12 subinstr 3,p4,p5,3
outs a11,a12
endif
; hihat 1 NOTE 38
if p4 == 38 then
chnset “value(1)”,“bng3”
a11,a12 subinstr 3,p4,p5,2
outs a11,a12
endif
; perc NOTE 41
if p4 == 41 then
chnset “value(1)”,“bng4”
a11,a12 subinstr 3,p4,p5,5
outs a11,a12
endif
; kick 2 NOTE 40
if p4 == 40 then
chnset “value(1)”,“bng5”
a11,a12 subinstr 3,p4,p5,4
outs a11,a12
endif

endin

;=============================================================================
;receives Note pitch P4, Velocity P5, table Number P6,index corresponding to widget
;=============================================================================

instr 3

SChn_AttTim sprintf “AttTim%d”,P6
SChn_RelTim sprintf “RelTim%d”,P6
SChn_Pan sprintf “Pan%d”,P6
SChn_ChoiceFilt sprintf “FilterChoice%d”,P6
SChn_Reverb sprintf “ReverbOnOff%d”,P6
SChn_Room sprintf “RoomSize%d”,P6
SChn_Damp sprintf “Damping%d”,P6
SChn_Mix sprintf “Mix%d”,P6
SChn_CutOff sprintf “Filtercutoff%d”,P6
SChn_Reson sprintf “FilterRes%d”,P6
SChn_Pitch sprintf “Pitch%d”,P6
SChn_Level sprintf “level%d”,P6
kVolume chnget “volume”
iAttTim chnget SChn_AttTim ; read in widgets
iRelTim chnget SChn_RelTim
kPan chnget SChn_Pan
kChoiceFilt chnget SChn_ChoiceFilt
kReverb chnget SChn_Reverb
kRoom chnget SChn_Room
kDamp chnget SChn_Damp
kMix chnget SChn_Mix
kCutOff chnget SChn_CutOff
kReson chnget SChn_Reson
kPitch chnget SChn_Pitch
kLevel chnget SChn_Level
iamp = p5 ; gain of signal = velocity of midi note
;---------------------------------------
; Mono signal
;----------------------------------------

if giNumChannel[P6]==1 then
if iAttTim>0 then ; is amplitude envelope attack time is greater than zero…
kenv linsegr 0,iAttTim,1,iRelTim,0 ; create an amplitude envelope with an attack, a sustain and a release segment (senses realtime release)
else
kenv linsegr 1,iRelTim,0 ; create an amplitude envelope with a sustain and a release segment (senses realtime release)
endif
kenv expcurve kenv,8 ; remap amplitude value with a more natural curve
aenv interp kenv ; interpolate and create a-rate envelope
kporttime linseg 0,0.001,0.05 ; portamento time function. (Rises quickly from zero to a held value.)
klevelF portk kLevel ,kporttime

  iNumSamples = ftlen(giIndxTabGen[P6]) 
aPhasor phasor kPitch*sr/iNumSamples      	;modulate phasor speed for reading the sample
a1 tab aPhasor, giIndxTabGen[P6], 1  
a1 = a1 * klevelF*aenv*iamp

if kChoiceFilt ==1 then ; no filter
a11 = a1
endif
if kChoiceFilt ==2 then ; low pass filter
a11 rezzy a1, kCutOff ,kReson, 0
endif
if kChoiceFilt ==3 then ; high pass filter
a11 rezzy a1, kCutOff ,kReson, 1
endif

if kReverb==1 then ; reverb on
aRvbL,aRvbR MyReverb a11,a11,kRoom,kDamp,kMix,kPan
outs aRvbL,aRvbR
else ; reverb off
a11, a12 pan2 a11, kPan
outs a11,a12
endif
endif

;---------------------------------------
; Stereo signal
;
; Not done yet !
; need to use loscil 3 instead of
;----------------------------------------

if giNumChannel[P6]==2 then
if iAttTim>0 then ; is amplitude envelope attack time is greater than zero…
kenv linsegr 0,iAttTim,1,iRelTim,0 ; create an amplitude envelope with an attack, a sustain and a release segment (senses realtime release)
else
kenv linsegr 1,iRelTim,0 ; create an amplitude envelope with a sustain and a release segment (senses realtime release)
endif
kenv expcurve kenv,8 ; remap amplitude value with a more natural curve
aenv interp kenv ; interpolate and create a-rate envelope
kporttime linseg 0,0.001,0.05 ; portamento time function. (Rises quickly from zero to a held value.)
klevelF portk kLevel ,kporttime

  iNumSamples = ftlen(giIndxTabGen[P6]) 
aPhasor phasor kPitch*sr/iNumSamples      			;modulate phasor speed for reading the sample
  aL tab aPhasor, giIndxTabGen[P6], 1
  aR tab aPhasor, giIndxTabGenR[P6], 1
aL = aL * klevelF*aenv*iamp
aR = aR * klevelF*aenv*iamp

if kChoiceFilt ==1 then
a1L = aL
a1R = aR
endif
if kChoiceFilt ==2 then
a1L rezzy aL, kCutOff ,kReson, 0
a1R rezzy aR, kCutOff ,kReson, 0

endif
if kChoiceFilt ==3 then
a1L rezzy aL, kCutOff ,kReson, 1
a1R rezzy aR, kCutOff ,kReson, 1
endif

if kReverb==1 then
aRvbL,aRvbR MyReverb a1L,a1R,kRoom,kDamp,kMix,kPan
outs aRvbL,aRvbR
else
a1L =sqrt(1-kPan)* a1L
a1R =sqrt(kPan)* a1R
outs a1LkVolume,a1RkVolume
endif

endif

endin

i100 0 z ;i 800 0 z i4 0 10000

i could not pasted complete because exceeds the amount of leters permited.

also im trying to make to eliminate the loop ,
i know it has to do with this ( aPhasor phasor kPitch*sr/iNumSamples ) change phasor to linsegr, i di but nothing happened,thanx

I didn’t mean post your entire code. (also you used the ‘quote’ button rather than the code format ‘</>’ button. Anyhow, it’s always better to isolate the problem and post a minimal example. 9 times out of 10 you’ll find the solution yourself in the process. I’ve ben updating the Cabbage examples, and have just redone the meter example. It might help.

meter.csd (2.7 KB)

yes, the phasor will loop endlessly. Maybe try a linseg rather than a linsegr which you don’t really need. Something like

aPhasor linseg 0, iNumSamples/sr, 1, 1, 1

This will produce a signal moving from 0 to 1 in the length of the soundfiler. The last two 1s are just to make sure the ramp stops at the end of the soundfile.

i manage to use this new meter example and thanx you add a player i did a copy of the player and modify the push button so it trigger each time i hit it as a pad,also add individual volumes to control each sound, i hope i can add pan, and efx ,i really wil love to build my own vst as much i can, i noticed i cant control the led meter with volume ,even the volume on 0 the turn on, i will keep trying, any help will be nice,
ALEX METER 2.zip (1.6 MB)

also meters stop when the sample stop and don go down to 0, where i could go and fix that? thnx

You can check for when the instrument goes into its release phase and then 0 the VU meters then. Also, if you want the volume to affect the VU meters, you need to scale the signal before it goes to the RMS opcode.

sampler meter idea.csd (3.6 KB)

p.s. I removed the code for the first sample on the left…

[edit] btw, you seem to be adding identchannels here, they are not supported in this gui mode…