# New MIDI file opcodes

**URL:** <https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586>\
**Category:** Announcements\
**Created:** [13 February 2023 12:50 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586 "2023-02-13T12:50:51Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![rorywalsh](https://forum.cabbageaudio.com/user_avatar/forum.cabbageaudio.com/rorywalsh/32/165_2.png) [@rorywalsh](https://forum.cabbageaudio.com/u/rorywalsh)\
**Post date:** [13 February 2023 12:50 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/1 "2023-02-13T12:50:51Z")

</div>

_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`](https://csound.com/docs/manual/midiin.html), 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

```csound
<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>

```

---

<div class="post-metadata">

**Author:** ![iainmccurdy](https://forum.cabbageaudio.com/letter_avatar_proxy/v4/letter/i/8baadc/32.png) [@iainmccurdy](https://forum.cabbageaudio.com/u/iainmccurdy)\
**Post date:** [13 February 2023 21:07 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/2 "2023-02-13T21:07:58Z")

</div>

Nice job, Rory, it works for me.

There’s a small typo in your example: “`kStatus[]`” should be “`kstatus[]`”

`kSpeedFactor` seems a bit odd though. I’m not sure what’s going on, I’ll try it with a simpler midi file…

---

<div class="post-metadata">

**Author:** ![rorywalsh](https://forum.cabbageaudio.com/user_avatar/forum.cabbageaudio.com/rorywalsh/32/165_2.png) [@rorywalsh](https://forum.cabbageaudio.com/u/rorywalsh)\
**Post date:** [14 February 2023 09:16 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/3 "2023-02-14T09:16:04Z")

</div>

Thank Iain, example updated 👍 Yeah, I’m not sure how to call refer to the speed control. It basically just multiplies each time stamp by a given value. So the following time sequence: 1, 2, 3, 4, 5, 6, becomes 2, 4, 6, 8, 10, 12 when the speed value is 2. I’m open to suggestions about how to refer to it, maybe note spacing is a little more intuitive? I have to say, after all these years of using Csound it feels very strange to be able to load MIDI files 🤣

---

<div class="post-metadata">

**Author:** ![iainmccurdy](https://forum.cabbageaudio.com/letter_avatar_proxy/v4/letter/i/8baadc/32.png) [@iainmccurdy](https://forum.cabbageaudio.com/u/iainmccurdy)\
**Post date:** [14 February 2023 11:55 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/4 "2023-02-14T11:55:08Z")

</div>

Well there has always been the ability to play MIDI files using the -F command line flag, but this is limited to simple playback of one file at a time. What you are doing here is what I’ve always thought would be more useful - MIDI files as assets within the orchestra. I wrote an example before to convert MIDI files to Csound score but this is a much better solution.

---

<div class="post-metadata">

**Author:** ![rorywalsh](https://forum.cabbageaudio.com/user_avatar/forum.cabbageaudio.com/rorywalsh/32/165_2.png) [@rorywalsh](https://forum.cabbageaudio.com/u/rorywalsh)\
**Post date:** [14 February 2023 20:14 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/5 "2023-02-14T20:14:05Z")

</div>

> [@iainmccurdy](#):
>
> Well there has always been the ability to play MIDI files using the -F command line flag

I never found this option to be that useful. Having no control over the playback, or tempo, and anything like that was always problematic for me.

---

<div class="post-metadata">

**Author:** ![mjmogo](https://forum.cabbageaudio.com/user_avatar/forum.cabbageaudio.com/mjmogo/32/3522_2.png) [@mjmogo](https://forum.cabbageaudio.com/u/mjmogo)\
**Post date:** [26 March 2023 16:40 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/6 "2023-03-26T16:40:36Z")

</div>

Are you planning any options such as looping? I could see that as being a good option to start with, that way you could load up several patterns and use them as loops. If you could control the tempo of several different midi files/patterns playing, you could do a lot of Reich-ish phase playing. Would multiple instances be possible? Would they all share the same tempo?

Also, have you thought about making it a general midi player, and allow someone to build a sequence in real time? Version 2?

---

<div class="post-metadata">

**Author:** ![rorywalsh](https://forum.cabbageaudio.com/user_avatar/forum.cabbageaudio.com/rorywalsh/32/165_2.png) [@rorywalsh](https://forum.cabbageaudio.com/u/rorywalsh)\
**Post date:** [27 March 2023 08:21 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/7 "2023-03-27T08:21:04Z")

</div>

The option to loop hadn’t even come into my head. Good idea. Easily added I think. And you can already give each opcode its own independent tempo.

---

<div class="post-metadata">

**Author:** ![rorywalsh](https://forum.cabbageaudio.com/user_avatar/forum.cabbageaudio.com/rorywalsh/32/165_2.png) [@rorywalsh](https://forum.cabbageaudio.com/u/rorywalsh)\
**Post date:** [27 March 2023 14:20 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/8 "2023-03-27T14:20:40Z")

</div>

I just added looping controls, and updated the description above 👍 Also, when it stops, it goes back to the start, I guess a pause control might also be useful?

---

<div class="post-metadata">

**Author:** ![ph\_ne](https://forum.cabbageaudio.com/letter_avatar_proxy/v4/letter/p/7bcc69/32.png) [@ph\_ne](https://forum.cabbageaudio.com/u/ph_ne)\
**Post date:** [31 August 2023 10:01 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/9 "2023-08-31T10:01:58Z")

</div>

Thanks for this! Seems to open amazing possibilities!

This looks unnecessary in the example, becaus kNoteIndex is never used. Or am i blind?

> [@rorywalsh](#):
>
> kNoteIndex init 0

---

<div class="post-metadata">

**Author:** ![rorywalsh](https://forum.cabbageaudio.com/user_avatar/forum.cabbageaudio.com/rorywalsh/32/165_2.png) [@rorywalsh](https://forum.cabbageaudio.com/u/rorywalsh)\
**Post date:** [31 August 2023 10:10 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/10 "2023-08-31T10:10:58Z")

</div>

Correct, and updated 🙂

---

<div class="post-metadata">

**Author:** ![cybilopsin](https://forum.cabbageaudio.com/letter_avatar_proxy/v4/letter/c/c5a1d2/32.png) [@cybilopsin](https://forum.cabbageaudio.com/u/cybilopsin)\
**Post date:** [25 September 2023 00:44 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/11 "2023-09-25T00:44:15Z")

</div>

How cool is that! I can see a workflow with lilypond code generating small MIDI files to loop in Cabbage.

It immediately brings to mind the question of doing the data conversion the opposite direction. Say you generated some data to stored in a quartet of arrays `kStatus[], kChan[], kNote[], kVel[]` with some other method and the data was all in the appropriate numerical range. Would it be useful to have an opcode that would read those four arrays and save it all to disk as a `.mid` MIDI file?

To be sure, you could already do this with existing opcodes (`midiout`) and with the `--midioutfile` option

However, these new opcodes allow working with any number of different MIDI files as inputs in one performance. But `--midioutfile` at most could give you one MIDI output file per performance

---

<div class="post-metadata">

**Author:** ![rorywalsh](https://forum.cabbageaudio.com/user_avatar/forum.cabbageaudio.com/rorywalsh/32/165_2.png) [@rorywalsh](https://forum.cabbageaudio.com/u/rorywalsh)\
**Post date:** [25 September 2023 08:28 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/12 "2023-09-25T08:28:36Z")

</div>

I’m not sure how tricky it would be, but I can look into it 🙂

---

<div class="post-metadata">

**Author:** ![cybilopsin](https://forum.cabbageaudio.com/letter_avatar_proxy/v4/letter/c/c5a1d2/32.png) [@cybilopsin](https://forum.cabbageaudio.com/u/cybilopsin)\
**Post date:** [25 September 2023 16:32 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/13 "2023-09-25T16:32:42Z")

</div>

Nice, but I have something you probably want to look into first

I had to spin up a midi file reader mini, straight up stole the trickiest part from your code above too:

[midi\_file.csd](https://forum.cabbageaudio.com/uploads/short-url/qhTuaQTPvxmfMfMjPyL91RI8Ink.csd) (2.0 KB)

And I copied your GUI and kept the channel names the same. Everything pretty much works the same as your example.

I can’t upload the midi file but just in case, it is a four-bar midi phrase with some velocity changes and a tempo of 120? It was made with lilypond.

But, anyway, it works! 🥳

Just not perfectly? In particular, the knob that changes the speed the file plays. That works, BUT it doesn’t change the frequency at which the loop loops.

The behavior is like this: The sequence itself gets faster or slower as expected. But, if you put the knob to the left (fast), you get a increasingly long pause between loops (because the loop finishes, and waits a long time for the next one to start). And if you put the knob to the right (slow), the loop gets cut off early by the next loop starting. If you put the knob on 1.00, the loop is timed perfectly. So, it seems that the speed variable is not correctly updating the start time (and only the start time) of the loop itself.

Also the reset button doesn’t do anything, and unchecking “Play” resets the loop.

Another issue I have: In order to obtain the new MIDI cabbage opcodes, I re-installed Cabbage from your latest dev build, and when running the installer, I had it also install Csound (I wasn’t sure but I thought maybe that was required to get the new opcodes). Problem is, now my system’s version of Csound doesn’t have the `sterrain` opcode! I tested it with `csound -z` from the terminal.

---

<div class="post-metadata">

**Author:** ![rorywalsh](https://forum.cabbageaudio.com/user_avatar/forum.cabbageaudio.com/rorywalsh/32/165_2.png) [@rorywalsh](https://forum.cabbageaudio.com/u/rorywalsh)\
**Post date:** [25 September 2023 17:59 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/14 "2023-09-25T17:59:39Z")

</div>

Thanks, I’ll take a look at those issue, these opcodes are not very well tested. Reset should reset the read position back to 0. It was working when I last tried it. Maybe the .csd is off. I’ll take a look.

---

<div class="post-metadata">

**Author:** ![rorywalsh](https://forum.cabbageaudio.com/user_avatar/forum.cabbageaudio.com/rorywalsh/32/165_2.png) [@rorywalsh](https://forum.cabbageaudio.com/u/rorywalsh)\
**Post date:** [28 September 2023 08:52 UTC](https://forum.cabbageaudio.com/t/new-midi-file-opcodes/3586/15 "2023-09-28T08:52:02Z")

</div>

I’ve pushed a fix for the looping issue. It should now begin its loop as soon as the initial play through ends. I also tested the reset button, it works just as advertised, i.e., it will reset the play pointer right back to the start of the MIDI file. It can be called during playback or when the opcode is not outputting any MIDI.
