Cabbage Logo
Back to Cabbage Site

Simple sampler

Hi. I want to create dead simple sampler which will asssign one sample per one note in octave.

I want to be able to load *.wav samples in runtime, but this failed for me and I am unable to dig out what am I doing wrong.

<Cabbage> 
form caption("WaggonBAU") size(400, 300), colour(58, 110, 182), pluginid("wagb")
keyboard bounds(6, 64, 381, 95)
filebutton bounds(6, 4, 40, 40) text("C", "C") channel("c") populate("*.wav")
filebutton bounds(52, 4, 40, 40) text("D", "D") channel("d")
filebutton bounds(98, 4, 40, 40) text("E", "E") channel("e")

</Cabbage> 
<CsoundSynthesizer>
<CsOptions>
-n -d -+rtmidi=NULL -M0 -m0d --midi-key-cps=4 --midi-velocity-amp=5
</CsOptions>
<CsInstruments>
; Initialize the global variables. 
ksmps = 32
nchnls = 2
0dbfs = 1

giSampleC init 0
giSampleD init 0
giSampleE init 0

giSampleF init 0
giSampleG init 0
giSampleA init 0
giSampleH init 0

gSfilepath init ""

;instrument will be triggered by keyboard widget
instr 1
    SampleCPath chnget "c"
    kLoadC changed SampleCPath
    
    if kLoadC==1 && strcmpk(SampleCPath,"")==1 then
        printks "open = %s\n", 0, SampleCPath
        gSfilepath = SampleCPath
        event "i", 99, 1, 0
    endif
    

    kstatus, kchan, kdata1, kdata2 midiin
    if changed(kstatus, kchan, kdata1, kdata2)==1 then
        if kstatus==144 then
            printks "this will trigger note %d\n", 0, kdata1%12
        endif
    endif
endin

instr 99
    printks "99! %s\n", 0, gSfilepath
    kSampleNo = p4
    giSampleC ftgen 1,0,0,1,gSfilepath,0,0,0
    
    printks "no=%d, lo=%d, fi=%s\n", 0, kSampleNo, giSampleC, gSfilepath
    
endin

</CsInstruments>
<CsScore>
;causes Csound to run for about 7000 years...
;f0 z
;starts instrument 1 and runs it for a week
i1 0 z
</CsScore>
</CsoundSynthesizer>

If you click on “C” it should load what sample will be played when note “C” is played. However, it seems that I didn’t get something important about loading samples.

(I am basically copying LoscilFilePlayer.csd example)

There seems to be something strange happening with the global string variable, but I can’t say what until I have more time to look into it. However, you can just use the channel directly in instr 99:

instr 99
    SampleCPath chnget "c"
    printks "99! %s\n", 0, SampleCPath
    kSampleNo = p4
    giSampleC ftgen 1,0,0,1,SampleCPath,0,0,0
    printks "no=%d, lo=%d, fi=%s\n", 0, kSampleNo, giSampleC, gSfilepath
endin

Tested something more.

It seems that file button is buggy or I use it in way it was not intended to. See this example:

<Cabbage>
form caption("Untitled") size(400, 300), colour(58, 110, 182), pluginid("def1")
keyboard bounds(8, 158, 381, 95)
filebutton bounds(106, 8, 133, 53) text("Open file A") channel("filebuttonA")
filebutton bounds(106, 70, 134, 60) text("Open file B") channel("filebuttonB")
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d -+rtmidi=NULL -M0 -m0d --midi-key-cps=4 --midi-velocity-amp=5
</CsOptions>
<CsInstruments>
; Initialize the global variables. 
ksmps = 32
nchnls = 2
0dbfs = 1

;instrument will be triggered by keyboard widget
instr 1
    SampleCPath chnget "filebuttonA"
    kLoadC changed SampleCPath
    
    ;printks "%d\n", 0, strcmpk(SampleCPath,"")
    
    if kLoadC==1 && strcmpk(SampleCPath,"")==1 then
        printks "open #1 = %s\n", 0, SampleCPath
    endif
endin

instr 2
    SampleCPath chnget "filebuttonB"
    kLoadC changed SampleCPath
    
    if kLoadC==1 && strcmpk(SampleCPath,"")==1 then
        giSampleC ftgen 1,0,0,1,SampleCPath,0,0,0
        printks "open #2 = %s\n", 0, SampleCPath
    endif
endin

</CsInstruments>
<CsScore>
;causes Csound to run for about 7000 years...
f0 z
; instr 1 listens
i 1 0 z
; if you uncomment i2, it crashes immediately
;i 2 0 z
</CsScore>
</CsoundSynthesizer>

I this example there are two instruments:

  • first that just wrote selected sample filename to log (which works as expected)
  • second which tries to actually loads it (which crashes whole plugin)

Strange thing is that the second one runs on start of instrument without needing to click on button (you need to uncomment it in CsScore).

There’s some strange stuff going on in instr 2. For example, you’re doing a k-time check for a block of code that runs at i-time. If you out-source the loading of samples to an instrument that only runs at i time you should manage to avoid some rate pitfalls. It’s easier than you think:

<Cabbage>
form caption("Untitled") size(400, 300), colour(58, 110, 182), pluginid("def1")
keyboard bounds(8, 158, 381, 95)
filebutton bounds(106, 8, 133, 53) text("Open file A") channel("filebuttonA")
filebutton bounds(106, 70, 134, 60) text("Open file B") channel("filebuttonB")
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d -+rtmidi=NULL -M0 -m0d --midi-key=4 --midi-velocity-amp=5
</CsOptions>
<CsInstruments>
; Initialize the global variables. 
ksmps = 32
nchnls = 2
0dbfs = 1

instr 1    ;p4 is MIDI note number...
    if p4 == 60 then
       a1 poscil 1, 1, 1
       outs a1, a1
    endif
endin

;always on..
instr LISTENER
    SampleCPath chnget "filebuttonA"       
    if changed:k(SampleCPath) == 1 then
        printks "open #1 = %s\n", 0, SampleCPath
        event "i", "LOAD_SAMPLE", 0, 0
    endif
endin

;load sample when user selects new file
instr LOAD_SAMPLE
    SampleCPath chnget "filebuttonA"
    if filevalid(SampleCPath) == 1 then
        giSampleC ftgen 1,0,0,1,SampleCPath,0,0,0
    endif
endin



</CsInstruments>
<CsScore>
;causes Csound to run for about 7000 years...
f0 z
; instr 1 listens
i"LISTENER" 0 z
</CsScore>
</CsoundSynthesizer>

Do I understand correctly that ftgen (in my example) is run regardless of outer if? (Because k-rate conditions)

CSound is really weird language (but still easiest way to create own VST plugins).

For sure :rofl: Some of the rate stuff is very confusing!

Yeah, my understanding is that the ftgen is being called at i-time with no valid string, hence the error.