Cabbage Logo
Back to Cabbage Site

Issue with MIDI array

I’m currently working on a 4 voice harmoniser in Cabbage that uses incoming MIDI notes written to an array with each voice then generating a harmony using one of the 4 notes stored within the array and need some help.

I’ve gotten it to work with cpsmidi (I was having issues writing the notes to the array using just midiin) and now I’m trying to remove the notes from the array when the note is released but I’m having some issues. It seems to remove the notes sporadically and I’m unsure why. Any help would be greatly appreciated! I’ve attached the Cabbage file below :slight_smile:

05midi concept cpsmidi.csd (1.3 KB)

I have a few queries.

instr 1 is triggered by MIDI notes but it is also triggered from the score. I’m not sure if this is the best strategy as things like cpsmidi will only output zero when triggered from the score. You also use midiin, which makes more sense in the context of an instrument that is triggered from the score but not from MIDI notes. I would use cpsmidi or midiin in a single instrument, but probably not both.

ktrigger changed icps

seems like a bad idea because the input argument is i-rate so will never change at k-rate - the output rate of the opcode. ktrigger will be 1 and then zero on the first and second k-rate passes and maybe this is the output that you want but it has more to do with changed's slightly odd behaviour which was discussed recently in a different thread.

I would be inclined to forget about ktrigger and instead do everything within the conditional at i-time. This is because iNoteOnMIDI is created at i-time and does not change thereafter. Maybe then you could make gkCounter i-rate and increment is at i-time in the instrument like this:

giCounter = (giCounter + 1) % 4

You can replace:

icps cpsmidi
iNoteOnMIDI ftom icps

with a single line

iNoteOnMIDI notnum

With

if (kvel == 0 || kstatus == 128) then

you seem to be sensing the note release. If you were to get rid of midiin, you can replace this with something that uses the release opcode.

These are just a few random thoughts. I hope they are useful.

Hi Iain,

Thanks a lot for the reply, it really helped! I’ve implemented the changes you mentioned and it seems to work fine now except for one issue. When two MIDI notes are held down, the second position in the array flickers between the first and second MIDI note number. I’ve tried a few approaches to fix this but haven’t had much success. Any help would be greatly appreciated!

Thanks again,
Scot

06midi concept - iain.csd (1.3 KB)

Can I check exactly what your aim is here? You want the array to be populated with note numbers from held MIDI notes? If so and as the array can store up to four values, what do you want to happen if a fifth note is played?

Yes the array should be populated by currently held notes (and if all notes are lifted then giCounter should be reset to 1 so that held notes always populate from position 1 onwards). If a 5th note is played then with the current setup I assume giCounter will go back to 1 therefore the 5th note will repopulate position 1 of the array which I’m ok with as it will be clear to the user that there is a maximum of 4 voices generated hopefully preventing the user from playing more than 4 notes.

Your update seems to work quite well, Scot. I don’t think I am experiencing the flickering you describe when playing two notes. I can identify a couple of potential issues though:

If you press and hold a fifth note, it will overwrite the note number that was written by the - still held - first note. This means that when the first note is released, it won’t be able to find its note number in the array anymore in the sequence of four if statements at the bottom of the instrument. (Maybe this isn’t a big problem.)

Another thing is that the counter will overwrite held note numbers in the array, whether these are free zeroes later in the array or not, so the global counter may be a flawed approach.

Addressing these two things would complicate the code somewhat. One way around it would be to make the array much larger, i.e.: larger than 10 fingers. Another way is to hunt for empty spaces in the array so that held notes are always in a contiguous sequence from the start of the array. I have attached an implementation of this here in case you think it is a possibility. The drawback is that if a 5th note is pressed, it simply won’t be written to the array so there is a four-note polyphony limit. I have changed the array to a function table.
05midi concept cpsmidi3.csd (1.6 KB)

That was exactly what I was trying to achieve! Thanks so much for the help, I’ve attached a file that has your table code implemented into the complete harmoniser code in case you’re interested.

One last thing I’d love to add if possible is to have the note used for the sub to be the note in the first position of the table shifted down to the first MIDI octave (ie. if the first MIDI note played is C4, then the sub plays C1, F4 to F1 etc.). I feel like there may be a way to use the octave opcode or octcps but I can’t seem to get it fully.

Apologies for asking so many questions!

Thanks again,
Scot

Hi Scot, if you want to transpose any MIDI note number down 3 octaves, you can just subtract 36 (12 semitones per octave). If you need to convert an MIDI note number to cycles-per-second frequency you can use cpsmidinn or mtof.
If you want to apply the 3-octave transposition in the pvscale line, multiply kscal by 0.125. Multiplying it by octave(-3) or semitone(-36) would work also.

Sorry I should have been more specific, the goal is that no matter what octave a note is played in, the sub plays that note in the first octave, so C4 would become C1, C5 would become C1, F6 would become F1 etc. If there were a way to derive the note without octave information I could just assign each not name to a corresponding Hz in the first octave but there may be an easier way

Ah, right. That’ll be the modulus operator again (%).

Here is code that will wrap the full range of input notes to a single octave range output starting from C1:

iNum      notnum                 ; MIDI note number played
iStep     =       iNum % 12      ; wrap around at 12. I.e.: all Cs will be zero, all C#s will be 1 and so on
iBase     =       24             ; (C1)
iInterval =       iBase + iStep  ; sub note (number)

Ah of course it is! Thanks, got it all working now :slight_smile: