Updated on the 27/03/23
The ability to read MIDI files within an instrument has always been missing in Csound, until now Last week I pushed two new Cabbage opcodes to the code base. You can try them out with the latest dev build of Cabbage. They are relatively untested, so be gentle! I will add them to the docs once they have been properly tested.
Opcodes described below.
cabbageMidiFileInfo Opcode
This opcode takes a path to a MIDI file and prints some useful debug information about the file, including the file type, the last time stamp, the number of tracks, a list and all time changes and a list of all time signature changes, listed by timestamps in seconds
The channels can hold strings or numbers, but only numeric channels work with the optional threshold arguments.
Added in Cabbage v2.9.5
Syntax
cabbageMidiFileInfo SMidifile
Initialization
-
SMidifile
– midi filename, must be an absolute path
cabbageMidiFileReader Opcode
This opcode outputs an array of MIDI events in the form of numbers (ala midiin
, alongside a trigger signal to indicate an event has been received. The number of events received in each k-cycle is also output.
Added in Cabbage v2.9.5
Syntax
kStatus[], kChan[], kNote[], kVel[], kNumEvents, kTrig
cabbageMidiFileReader
SMidifile, iTrackNum, kPlay, kLoop, kSpeedFactor, kReset [, iSkipTime]
Initialization
-
SMidifile
– midi filename, must be an absolute path -
iTrackNum
– the track index you wish to read. (Use the cabbageMidiFileInfo opcode to determine the number of tracks if you are sure). The first track is track 0. -
iSkipTime
(optional) – sets the initial skip time in second, defaults to 0.
Performance
-
kPlay
– set to 1 start playback, 0 to pause. -
kLoop
– set to 1 enable looping. -
kSpeedFactor
– used to increase/decrease playback speed. This basically acts as a timestamp multiplier. Larger values will slow down playback, while smaller values will increase playback speed. -
kReset
– a trigger signal (a 1 followed immediately by a 0) will cause playback to return to the start.
Example
<Cabbage>
form caption("Midi playback") size(400, 100), colour(40), guiMode("queue") pluginId("def1")
rslider bounds(114, 8, 60, 60) channel("transpose") range(-24, 24, 0, 1, 1), text("Transpose")
checkbox bounds(8, 8, 100, 30) channel("play"), text("Play")
checkbox bounds(8, 42, 100, 30) channel("loop"), text("Loop")
rslider bounds(178, 8, 60, 60) channel("speed") range(0.5, 2, 1, 1, 0.001), text("Speed")
button bounds(242, 8, 81, 28) channel("reset"), text("Reset")
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-n -d
</CsOptions>
<CsInstruments>
; Initialize the global variables.
ksmps = 32
nchnls = 2
0dbfs = 1
instr 1
SCsdPath chnget "CSD_PATH"
//file lives in same dir as the .csd file..
SMidifile init sprintf("%s/happyBirthday.mid", SCsdPath)
cabbageMidiFileInfo SMidifile
kEventIndex = 0
kRes, kResetTrigger cabbageGetValue "reset"
kStatus[], kChan[], kNote[], kVel[], kNumEvents, kTrig cabbageMidiFileReader SMidifile, 1, cabbageGetValue("play"), cabbageGetValue("loop"), cabbageGetValue("speed"), kResetTrigger, 0
//printing this many events can have a serious impact on performance..
//printk2 kNumEvents
if kTrig == 1 then
while kEventIndex < kNumEvents do
if (kStatus[kEventIndex] == 128 || kVel[kEventIndex] == 0) then //note off
turnoff2 2, 1, 1
elseif (kStatus[kEventIndex] == 144) then //note on
event "i", 2, 0, 10, kNote[kEventIndex]+cabbageGetValue:k("transpose"), kVel[kEventIndex]/127
endif
kEventIndex += 1
od
endif
endin
instr 2
kEnv madsr 0.01, .2, .6, .1
a1 oscili kEnv, cpsmidinn(p4)
outs a1*0.1, a1*0.1
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
;i2 0 10
</CsScore>
</CsoundSynthesizer>