Cabbage Logo
Back to Cabbage Site

Polyphonic after-touch - MIDI triggered

I’m looking for feedback regarding polyphonic pressure (after-touch) detection in MIDI triggered instruments. Perhaps @iainmccurdy would have an advice.

When using kstatus from midiin I’m getting what I expect and I can filter out the after-touch messages just fine. But if I use a MIDI-triggered instrument, within which I try to catch polyphonic after-touch (using something like kAfterTouch polyaft iNote, 0, 1), I see that kAfterTouch = 1 when there is no after-touch MIDI message sent, for example, if I touch a key very gently. Then, when I’m actually sending after-touch, it works as expected. But the kAfterTouch value initially jumps from 1 to something between 0-1. I’d expect that kAfterTouch initializes at 0 and stays there until an actual after-touch message is transmitted. Is this a known bug in the polyaft opcode? I’m resolving this by:

kStartAfterTouch init 0
kAfterTouch init 0
ktmp polyaft iNote, 0, 1 
if kStartAfterTouch == 0 then
    if ktmp < 1 then
        kStartAfterTouch = 1
    kAfterTouch = ktmp

Is there a better way to handle this? I need to start from 0, so smoothing will generally not be adequate.

I’ve never had a device that outputs polyphonic aftertouch so I’ve never been able to try it. I’m curious as to what you are using as there are few of these around! Anyway, it does sound like a bug in the opcode if midiin is producing a different result. You could double-check the data using something like Snoize’s MIDI Monitor just to confirm that it is not your device.
Your workaround looks efficient and I can’t suggest anything better. If you’re sure it’s the opcode, you could lodge an issue report for Csound on GitHub.

Thanks for the feedback Iain. I’m exploring how I can abuse Launchpad X by Novation. It can output either channel or polyphonic after-touch. On Mac I’m using MIDI Monitor to see when note-on and after-touch messages are coming in.

Greetings! I have found MPE to work fairly well with Csound/Cabbage–I am using a roliu seaboard (1st gen) on a Mac. Here is a relatively simple MPE flute instrument that uses the roli XY pad for modulation effects and incorporates polyphonic aftertouch:

form caption("MPE flute") size(400, 300), guiMode("queue"), pluginId("MPE2")

button bounds(345,5,47,25) channel(“record”) text(“Record”,“Stop”) colour(“red”) fontColour(“white”)
combobox bounds(345,35,47,15) channel(“recch”) value(2) text(“mono”,“stereo”)
combobox bounds(345,55,47,15) channel(“bit”) value(1) text(“16-bit”,“24-bit”,“32-bit”)
button bounds(345,75,47,25), channel(“play”) text(“Play”) colour(“green”) fontColour(“white”)
combobox bounds(5,35,100,20) channel(“xysel”) items(“sine”,“triangle”,“bipolar square”,“unipolar square”,“saw up”,“saw down”) value(1)
label bounds(110,35,100,15) channel(“t2”) text(“Mod Select”) fontColour(“white”)
nslider bounds(5,65,50,23) channel(“xmax”) range(0,60,48,1,.1)
label bounds(60,67,150,15) channel(“t3”) text(“Mod Width Max (X)”) fontColour(“white”)
nslider bounds(5,95,50,23) channel(“ymax”) range(0,250,100,1,.1)
label bounds(60,97,150,15) channel(“t4”) text(“Mod cps Max (Y)”) fontColour(“white”)
nslider bounds(5,125,50,23) channel(“xyport”) range(0.001,.1,.01,1,.001)
label bounds(60,127,150,15) channel(“t5”) text(“Mod cps Max (Y)”) fontColour(“white”)

keyboard bounds(8, 198, 381, 95)

-n -d -+rtmidi=NULL -M0 --midi-key=4 --midi-velocity-amp=5 ;-+raw_controller_mode=1 ;was supposedto make ctrl 113 and 114 work butit didn’t

; Initialize the global variables.
ksmps = 32
nchnls = 2
0dbfs = 1

alwayson 1
alwayson 20
alwayson 750
massign 0,10 ;assigns all incoming MIDI channels to instr 1
gkx,gky init 0
gaout init 0

gichnls init 0 ;globals for soundfile table loading
giReady init 0
gkTabLen init 2
giTable init 501
gkRecordingActiveFlag,gkFileRecorded init 0

instr 1 ;live audio input and record/play control
gkrecord chnget “record”
gkplay chnget “play”
kRecStart trigger gkrecord,0.5,0
if kRecStart==1 && gkRecordingActiveFlag==0 then
event “i”,600,0,-1
gkRecordingActiveFlag = 1
kPlayStart trigger gkplay,0.5,0
if kPlayStart==1 && gkFileRecorded==1 then
event “i”,601,ksmps/sr,3600

instr 10
kxmax chnget “xmax”
kymax chnget “ymax”
kxysel chnget “xysel”
kenv madsr .05, .2, .8, .2
kY midictrl 74,0,1 ;grabs incoming Y values and maps to .5-1 floating point values
kY port kY,.01 ;maybe map these to a filter instead? or make choices for mappings of the controller
kbend pchbend 0,48 ;this provides pitch bend in semitones mapped in direct correlation with roli keys
;24 would be half the deviation, 96 twice the deviation
kaft aftouch 0,1

klfo1 lfo gkxkxmax,gkykymax,0 ;amp (MIDI), cps, [wave] 0=sine, 1=triangle, 2=bipolar square, 3=unipolar square, 4=saw up, 5=saw down
klfo2 lfo gkxkxmax,gkykymax,1
klfo3 lfo gkxkxmax,gkykymax,2
klfo4 lfo gkxkxmax,gkykymax,3
klfo5 lfo gkxkxmax,gkykymax,4
klfo6 lfo gkxkxmax,gkykymax,5
if kxysel=1 then
elseif kxysel=2 then
elseif kxysel=3 then
elseif kxysel=4 then
elseif kxysel=5 then
elseif kxysel=6 then

aout wgflute kaftkaft,cpsmidinn(p4+kbend+klfo),(kY.4)+.1,.1,.1,kY*.1,0,0 ;gky100,gkx24
aout dcblock2 aout
outs aout,aout

instr 20 ;instrument always on to sense non-note-specific MIDI controller values coming in on channel 1
kxyport chnget “xyport”
kstatus,kchan,kdata1,kdata2 midiin
if kstatus==176 then
if kdata1==113 then
kx=kdata2/127 ;mapsx-y pad x value to 0-1
gkx portk kx,kxyport
elseif kdata1=114 then
ky=kdata2/127 ;mapsx-y pad y value to 0-1
gky portk ky,kxyport

instr 600 ; record file
irecch chnget “recch”
ibit chnget “bit”
print p1
if gkplay==1 then
chnset k(0),“record”
gkFileRecorded init 1
itim date
Stim dates itim
itim date
Stim dates itim
Syear strsub Stim, 20, 24
Smonth strsub Stim, 4, 7
Sday strsub Stim, 8, 10
iday strtod Sday
Shor strsub Stim, 11, 13
Smin strsub Stim, 14, 16
Ssec strsub Stim, 17, 19
Sfilnam sprintf “%s_%s_%02d_%s_%s_%s.wav”, Syear, Smonth, iday, Shor,Smin, Ssec
gSname sprintf “MPEflute_%s”, Sfilnam
if gkrecord==1 then ; record
if ibit=1 then
elseif ibit=2 then
elseif ibit=3 then
if irecch=1 then
fout gSname,imode,gaout
elseif irecch=2 then
fout gSname,imode,gaout,gaout
gkRecordingActiveFlag = 1 - release()

instr 601 ; play file
if gkplay==0 then
ichn filenchnls gSname
if ichn=1 then
aL diskin2 gSname,1
outs aL,aL
aL,aR diskin2 gSname,1
outs aL,aR
iFileLen filelen gSname
p3 = iFileLen
xtratim 0.1
krelease release
chnset 1-krelease,“play”

instr 750 ;clear global audio paths that include feedback (don’t need to clear the global LFO outputs)
clear gaout

;causes Csound to run for about 7000 years... f0 z Widgets 100 100 320 240 true 240 240 240

Nice! But it seems you’re using the midiin, which works fine. There seem to be some bugs in “non-generic” MIDI opcodes.

Further, e.g. this one works fine:
kval ctrl7 1, 97, 0, 1
but this one does not detect anything
kval ctrl7 1, 98, 0, 1
while this one will detect CC 98 no problem
kstatus, kchan, kdata1, kdata2 midiin

I could do all my MIDI input with the midiin but then I had difficulties when I “switch” to monophonic portamento/glissando mode. I could implement that employing a MIDI-triggered instrument.

If anyone has an example of a mono-phonic portamento/glissando instrument that is using a generic MIDI input and not a MIDI-triggered instrument, i.e. unlike the super cool “FOF Choir” example, that would be great.