Cabbage Logo
Back to Cabbage Site

Tracking and querying MIDI notes that are currently playing

Hi,

I would like to track which MIDI notes (pitches) are currently playing in my DAW and then trigger a MIDI event if a certain combination of notes are playing. Would I use a CSound array to store note on messages, remove the notes when a corresponding note off message is received, and then query the array to see if the contents meet the criteria for triggering a MIDI event? If so, could anyone point me towards a relevant example that uses arrays? If not, what would be a better approach?

Thank you,
-d. vyd

Yes, using an array should work. For example, the following will play a tone, only if the notes of a major triad based on middle C are played:

gkNotes[] init 128 ; notes status array, 1=held 0=off

instr 1 ; trigger by midi
 gkNotes[notnum()] = 1-release:k()
endin

alwayson 99
instr 99
 if ((gkNotes[60]+gkNotes[64]+gkNotes[67])==3) then
  out poscil:a(0.2,330)
 endif
endin

Thank you Iain, Perhaps I am too new to CSound & Cabbage, but I don’t understand the main line: gkNotes[notnum()] = 1-release:k()

The left side looks like it gets the array cell of the note being pressed. The rights side perhaps sets the value of that cell? But I’m not sure what 1-release does. I’m also not familiar with the “:” in this context. is k() referencing the control rate?

-d. vyd

Hi d. vyd, I had condensed a lot of functionality into a single line using opcodes as function but if I break it down into several lines of code (as shown below) maybe it is easier to follow:

kRelFlag          release              ; 0 when note is held, 1 when released
kRelFlag          =       1 - kRelFlag ; invert logic
iNum              notnum               ; number of MIDI note played
gkNotes[iNum]     =       kRelFlag     ; write flag to array location of note number

The left side of the original line was actually writing a value rather than getting one.
‘release’ is an opcode that outputs a ‘1’ the moment a note is released; the rest of the time it outputs zero.
‘1 -’ this value therefore just inverts the logic: now it is ‘1’ when a note is being held, zero when it is released.
The use of ‘:’ followed by a variable rate just forces the output type. So we have an array of 128 values (normally zeroes) which correspond to the 128 MIDI notes possible. E.G. if note 60 is held, the array location with index 60 will be ‘1’. When the note is released it returns to zero.

Iain,

I understand your code a bit more now. Thank you. Should this VST be created as an instrument or an effect? As an instrument, even when I simplified the criteria to generate a sound, I was unable to get a sound out of the code below. However, I am able to attach my MIDI device (Novation Launch Control XL) and play the synths that come with Cabbage.

form caption("Untitled") size(400, 300), colour(58, 110, 182), pluginID("def1") keyboard bounds(8, 158, 381, 95) -n -d -+rtmidi=NULL -M0 -m0d --midi-key-cps=4 --midi-velocity-amp=5 ; Initialize the global variables. sr = 44100 ksmps = 32 nchnls = 2 0dbfs = 1

gkNotes[] init 128 ; notes status array, 1=held 0=off

instr 1 ; trigger by midi
kRelFlag release ; 0 when note is held, 1 when released
kRelFlag = 1 - kRelFlag ; invert logic
iNum notnum ; number of MIDI note played
gkNotes[iNum] = kRelFlag ; write flag to array location of note number
endin

alwayson 99
instr 99
if ((gkNotes[45])==1) then
out poscil:a(0.5,330)
endif
endin

;causes Csound to run for about 7000 years... f0 z

Here is a complete instrument. I exported as an instrument and opened it in a VST host and it plays the note when I play a C major triad starting on note 60. Your version was missing the massign midi assignment. I noticed also that ‘alwayson’ doesn’t seem to work in Cabbage so I changed it to a long note in the score. Hopefully this works for you now.

<Cabbage>
form size(500, 80), caption("Kybd Trigger"), pluginID("KyTr")
keyboard bounds(0,0,500,80)
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-dm0 -n -+rtmidi=null -M0
</CsOptions>
<CsInstruments>
sr = 44100
ksmps = 64
nchnls = 2
0dbfs=1

massign 0, 1

gkNotes[] init 128 ; notes status array, 1=held 0=off

instr 1 ; trigger by midi
 gkNotes[notnum()] = 1-release:k()
endin

instr 99
 if ((gkNotes[60]+gkNotes[64]+gkNotes[67])==3) then
  out poscil:a(0.2,330)
 endif
endin

</CsInstruments>  

<CsScore>
i 99 0 [3600*24*7]
</CsScore>

</CsoundSynthesizer>

I think I as having some issues building that opcode as one point. I can look into it. In the meantime a score statement should do the trick.