# Fout opcode suppored for Cabbage

**URL:** <https://forum.cabbageaudio.com/t/fout-opcode-suppored-for-cabbage/4941>\
**Category:** Cabbage 3\
**Created:** [30 November 2025 12:56 UTC](https://forum.cabbageaudio.com/t/fout-opcode-suppored-for-cabbage/4941 "2025-11-30T12:56:34Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![CharlieSL1](https://forum.cabbageaudio.com/user_avatar/forum.cabbageaudio.com/charliesl1/32/6134_2.png) [@CharlieSL1](https://forum.cabbageaudio.com/u/CharlieSL1)\
**Post date:** [30 November 2025 12:56 UTC](https://forum.cabbageaudio.com/t/fout-opcode-suppored-for-cabbage/4941/1 "2025-11-30T12:56:35Z")

</div>

Hi!

I’m currently working with Dr. Richard Boulanger on a research project called CStore, which import AI into the Csound.

We’ve run into an issue when trying to export a short preview “one-shot” of a Cabbage-designed instrument (just a few seconds of audio) using the `fout` opcode.

The problem:

Many plugin-designed .csd files use performance loops or k-rate logic that essentially run indefinitely. To generate one-shot preview audio, I wrote a Python tool that:

1. parses the .csd
2. finds the instrument’s score events
3. rewrites duration values
4. inserts a `fout` opcode to export audio (AIFF format)

But in practice:

- sometimes Csound creates a massively long file (for example, larger than 1 TB)
- other times it produces an empty file
- or the rendering fails completely

It seems that the issue might be related to how Cabbage handles performance loops and internal scheduling, but I’m not entirely sure.

My question:

Does Cabbage 2 officially support the `fout` opcode?  
If not, will Cabbage 3 include support for reliable offline exporting or a safe way to render a bounded-duration audio file from a plugin-style .csd?

Our goal is simple:  
Inside a “designed-for-plugin” Cabbage .csd, we want to render a few seconds of audio for machine-learning dataset generation, without depending on real-time output or infinite loops.

Any guidance on:

- correct usage of `fout` inside Cabbage
- whether offline export is expected to work
- or future plans in Cabbage 3 related to this
- or any other way I can do that function!  
would be incredibly helpful.

Thanks so much!

— Charlie

---

<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:** [30 November 2025 13:00 UTC](https://forum.cabbageaudio.com/t/fout-opcode-suppored-for-cabbage/4941/2 "2025-11-30T13:00:05Z")

</div>

Hi @CharlieSL1, always nice to hear from Richard’s students. There are no limitations to using fout in Cabbage. It should work just like it doesn’t in plain old Csound. Do you have a simple example of one of these modified csd files? The key to the issue can probably be found there 🤔

---

<div class="post-metadata">

**Author:** ![CharlieSL1](https://forum.cabbageaudio.com/user_avatar/forum.cabbageaudio.com/charliesl1/32/6134_2.png) [@CharlieSL1](https://forum.cabbageaudio.com/u/CharlieSL1)\
**Post date:** [3 December 2025 23:34 UTC](https://forum.cabbageaudio.com/t/fout-opcode-suppored-for-cabbage/4941/3 "2025-12-03T23:34:04Z")

</div>

Hi Rory,

Thanks a lot for getting back to me — and for confirming that `fout` should work the same way in Cabbage as in plain Csound.

Today Dr. B and I spent about 30 minutes debugging, and we got our test file working as a `.csd` in Cabbage, so `fout` itself is definitely functional. However, we’re still seeing a few strange behaviours and I’d love your input.

Using the attached/embedded Trapped09 example (adapted from Dr. B’s Trapped collection), we noticed:

1. If we place `fout` directly on the local `amix` signal inside `instr Trapped09` , the rendered file can sound noisy / trashy compared to what we hear from the Monitor output.
2. If instead we record from a `Monitor` instrument that uses `monitor` → `fout` on `allL, allR` , the bounced `allMix` file sounds correct.
3. The duration of the file rendered from `Trapped09` (local `amix` ) does not always match the duration of the file rendered from the full mix, even though we’re using the same `ip3` / `dur` values.
4. For our use case, we’d ideally like a clean way to:

- trigger the sound once,
- write it to disk with `fout` , and then
- have Cabbage/Csound stop automatically after the render (so we don’t end up with very long or looping renders).

Here is the test `.csd` we’ve been working with (the problematic `fout` line is currently commented in `instr Trapped09` , and the working one is in `instr Monitor` ):

```
<Cabbage> bounds(0, 0, 0, 0) 
form caption("Trapped09") size(650, 250), guiMode("queue") pluginId("def1")

button bounds(34, 34, 101, 44) channel("trigger") text("Trigger") textColour("white")

hslider bounds(322, 10, 150, 50) channel("dur") range(2, 9, 4, 1, 0.001) text("Dur") textColour("white")
hslider bounds(250, 68, 150, 50) channel("note") range(20, 80, 50, 1, 0.001) text("MIDI NN") textColour("white")
hslider bounds(408, 68, 150, 50) channel("rndNote") range(0, 8, 3, 1, 0.001) text("Rnd NN") textColour("white")

hslider bounds(484, 10, 150, 50) channel("amp") range(0, 1, .6, 1, 0.001) text("Synth Lvl") textColour("white")

hslider bounds(286, 122, 162, 50) channel("rndRate") range(100, 400, 185, 1, 0.001) text("RndRate") textColour("white")
hslider bounds(452, 122, 150, 50) channel("rndAmp") range(0, 10, 3.3, 1, 0.001) text("RndAmp") textColour("white")

hslider bounds(338, 182, 150, 50) channel("delaySend") range(0, 1, .76, 1, 0.001) text("Delay Send") textColour("white")
hslider bounds(490, 182, 150, 50) channel("delayTime") range(.001, 5, .08, 1, 0.001) text("Delay Time") textColour("white")

hslider bounds(22, 184, 150, 50) channel("rvbSend") range(0, 1, .45, 1, 0.001) text("Rvb Send") textColour("white")
hslider bounds(176, 184, 150, 50) channel("rvbPan") range(0, 6, 4, 1, 0.001) text("Rvb Pan") textColour("white")

hslider bounds(160, 10, 150, 50) channel("masterLvl") range(0, 1, 0.9, 1, 0.001) text("Master Lvl") textColour("white")

combobox bounds(120, 104, 100, 25), populate("*.snaps"), channelType("string") automatable(0) channel("combo31") text("Starting-4note", "More Notes Less Vcomb", "More noise & open filter -pan LFO faster", "faster, less notes, more delay", "Slower, More resonance, and RingMod", "Complex PolyRhythms", "Alll Notes, Spinning, No Delay, All FreeVerb") value("1")
filebutton bounds(58, 104, 60, 25), text("Save", "Save"), populate("*.snaps", "test"), mode("named preset") channel("filebutton32")
filebutton bounds(58, 134, 60, 25), text("Remove", "Remove"), populate("*.snaps", "test"), mode("remove preset") channel("filebutton33")
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-odac 
</CsOptions>
<CsInstruments>
ksmps = 32
nchnls = 2
0dbfs = 1

garvb init 0
gadel init 0

giSine ftgen 1, 0, 8192, 10, 1
giWav2 ftgen 2, 0, 8192, 10, 10, 8, 0, 6, 0, 4, 0, 1
giWav3 ftgen 3, 0, 8192, 10, 10, 0, 5, 5, 0, 4, 3, 0, 1
giWav4 ftgen 4, 0, 8192, 10, 10, 0, 9, 0, 0, 8, 0, 7, 0, 4, 0, 2, 0, 1

gkRevPan init 4

instr 1
    iDur = chnget:i("dur")
    iNote = chnget:i("note") + rnd(chnget:i("rndNote"))
    iFrq = cpsmidinn(iNote)
    iAmp = chnget:i("amp")
    kTrig chnget "trigger"
    if changed(kTrig) == 1 then
        event "i", "Trapped09", 0, iDur, iFrq, iAmp
    endif
endin

instr Trapped09
ip3 = chnget:i("dur")
iamp = chnget:i("amp")*.2
kNote = chnget:k("note")+rnd(chnget:i("rndNote"))
kFrq = cpsmidinn(kNote)*.25

kRndAmp = chnget:k("rndAmp")
kRndFrq = chnget:k("rndRate")

k2 randh kRndAmp, kRndFrq, .1                     
k3 randh kRndAmp * .98, kRndFrq * .91, .2             
k4 randh kRndAmp * 1.2, kRndFrq * .96, .3            
k5 randh kRndAmp * .9, kRndFrq * 1.3     

kenv linen iamp, ip3 *.2, ip3, ip3 * .8  

a1 oscil kenv, kFrq + k2, giSine, .2             
a2 oscil kenv * .91, (kFrq + .004) + k3, giWav2, .3
a3 oscil kenv * .85, (kFrq + .006) + k4, giWav3, .5
a4 oscil kenv * .95, (kFrq + .009) + k5, giWav4, .8

kgate transeg 1, ip3, 0, 0 
amix = (a1 + a2 + a3 + a4) * kgate
aL = a1 + a3
aR = a2 + a4

              outs aL * chnget:k("masterLvl"), aR * chnget:k("masterLvl")
; Problem area when enabled:
; fout "/Applications/CsoundTest/fout_aif.aiff", 14, amix

garvb = garvb + (amix * chnget:k("rvbSend")) 
gadel = gadel + (amix * chnget:k("delaySend")) 
endin

instr Delay
denorm gadel
ip3 = chnget:i("dur")
kgate expseg 1, ip3*.7, 1, ip3*.3, .0001
asig delay gadel, chnget:i("delayTime")
asig = asig*kgate
               outs asig*chnget:k("masterLvl"), asig*chnget:k("masterLvl")
gadel = 0
endin

instr Reverb 
denorm garvb                   
k1 oscil .5, chnget:k("rvbPan"), 1
k2 = .5 + k1
k3 = 1 - k2
asig reverb garvb, 3.1
               outs (asig * k2) * chnget:k("masterLvl"), ((asig * k3) * (-1)) * chnget:k("masterLvl")
garvb = 0
endin
             
instr Monitor
allL, allR monitor
fout "/Applications/CsoundTest/fout_allMix.wav", 14, allL, allR
endin

</CsInstruments>
<CsScore>
i 1 0 15
i "Delay" 0 15
i "Reverb" 0 15
i "Monitor" 0 15
i "Trapped09" 0 3
i "Trapped09" 4 3
i "Trapped09" 8 3
</CsScore>
</CsoundSynthesizer>

```

If you have any suggestions about:

- best practice for using `fout` from within a Cabbage plugin (especially inside a single instrument vs from `monitor` ), and
- a clean pattern for “render once then stop” in a plugin context,

that would be incredibly helpful for us and for the larger project we’re working on.

Thanks again for your time and for all your work on Cabbage — it’s been central to this project with Dr. B.

---

<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:** [4 December 2025 09:23 UTC](https://forum.cabbageaudio.com/t/fout-opcode-suppored-for-cabbage/4941/4 "2025-12-04T09:23:05Z")

</div>

Using fout within an instrument is not a problem, so long as theinstrument isn’t triggered multiple times. This is usually why problems arise with this approach. Also, I see that you have 3 instances of `Trapped09`. This might also cause some issues as the sound file might not be closed. You should probably call [`ficlose`](https://csound.com/docs/manual/ficlose.html) during the release phase of the instrument to ensure it’s properly closed before trying to write again. Closing the sound file after writing should help in the context of rendering to disk too. You can also render faster than realtime in a k-rate loop if I’m not mistaken. During each k-cycle will process ksmps samples, so if you run things in a k-rate loop you should be able to do a kind of offline bounce, but I’ve not tried it.

---

<div class="post-metadata">

**Author:** ![CharlieSL1](https://forum.cabbageaudio.com/user_avatar/forum.cabbageaudio.com/charliesl1/32/6134_2.png) [@CharlieSL1](https://forum.cabbageaudio.com/u/CharlieSL1)\
**Post date:** [19 January 2026 21:17 UTC](https://forum.cabbageaudio.com/t/fout-opcode-suppored-for-cabbage/4941/5 "2026-01-19T21:17:40Z")

</div>

Hi Rory,

First, Happy New Year!!!

Thanks a lot for the detailed explanation, it helped me narrow the issue down.

I tried adding `ficlose` to force the file to close during release, but I could not get it to behave reliably. After your note about multiple triggers and multiple instances, I changed the approach and now I only write a single stereo mix bus using a dedicated `Monitor` instrument:

- `Monitor` reads the output via `monitor`

- `fout "…/Re-CAB-Trapped09_fout_allMix.wav", 14, allL, allR`

With this setup, the export does succeed, so the “multiple simultaneous Trapped09 writing to the same file” problem seems gone.

My current biggest blocker is automation: I cannot figure out a way to run this render from Python without manually clicking Play and Stop in the Cabbage GUI. My goal is a command driven workflow where a script triggers the render and exits cleanly, producing the wav, with no GUI interaction.

Below is the exact .csd I’m testing right now (full file):

```csound
<Cabbage> bounds(0, 0, 0, 0)
form caption("Trapped09") size(650, 250), guiMode("queue") pluginId("def1")

button bounds(34, 34, 101, 44) channel("trigger") text("Trigger") textColour("white")

hslider bounds(322, 10, 150, 50) channel("dur") range(2, 9, 4, 1, 0.001) text("Dur") textColour("white")
hslider bounds(250, 68, 150, 50) channel("note") range(20, 80, 50, 1, 0.001) text("MIDI NN") textColour("white")
hslider bounds(408, 68, 150, 50) channel("rndNote") range(0, 8, 3, 1, 0.001) text("Rnd NN") textColour("white")

hslider bounds(484, 10, 150, 50) channel("amp") range(0, 1, .6, 1, 0.001) text("Synth Lvl") textColour("white")

hslider bounds(286, 122, 162, 50) channel("rndRate") range(100, 400, 185, 1, 0.001) text("RndRate") textColour("white")
hslider bounds(452, 122, 150, 50) channel("rndAmp") range(0, 10, 3.3, 1, 0.001) text("RndAmp") textColour("white")

hslider bounds(338, 182, 150, 50) channel("delaySend") range(0, 1, .76, 1, 0.001) text("Delay Send") textColour("white")
hslider bounds(490, 182, 150, 50) channel("delayTime") range(.001, 5, .08, 1, 0.001) text("Delay Time") textColour("white")

hslider bounds(22, 184, 150, 50) channel("rvbSend") range(0, 1, .45, 1, 0.001) text("Rvb Send") textColour("white")
hslider bounds(176, 184, 150, 50) channel("rvbPan") range(0, 6, 4, 1, 0.001) text("Rvb Pan") textColour("white")

hslider bounds(160, 10, 150, 50) channel("masterLvl") range(0, 1, 0.9, 1, 0.001) text("Master Lvl") textColour("white")

combobox bounds(120, 104, 100, 25), populate("*.snaps"), channelType("string") automatable(0) channel("combo31") text("Starting-4note", "More Notes Less Vcomb", "More noise & open filter -pan LFO faster", "faster, less notes, more delay", "Slower, More resonance, and RingMod", "Complex PolyRhythms", "Alll Notes, Spinning, No Delay, All FreeVerb") value("1")
filebutton bounds(58, 104, 60, 25), text("Save", "Save"), populate("*.snaps", "test"), mode("named preset") channel("filebutton32")
filebutton bounds(58, 134, 60, 25), text("Remove", "Remove"), populate("*.snaps", "test"), mode("remove preset") channel("filebutton33")
</Cabbage>

<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>

<CsInstruments>
ksmps = 32
nchnls = 2
0dbfs = 1

garvb init 0
gadel init 0

giSine ftgen 1, 0, 8192, 10, 1
giWav2 ftgen 2, 0, 8192, 10, 10, 8, 0, 6, 0, 4, 0, 1
giWav3 ftgen 3, 0, 8192, 10, 10, 0, 5, 5, 0, 4, 3, 0, 1
giWav4 ftgen 4, 0, 8192, 10, 10, 0, 9, 0, 0, 8, 0, 7, 0, 4, 0, 2, 0, 1

gkRevPan init 4

instr 1
    iDur = chnget:i("dur")
    iNote = chnget:i("frq")+rnd(chnget:i("rndNote"))
    iFrq = cpsmidinn(iNote)
    iAmp = chnget:i("amp")

    kTrig chnget "trigger"
    if changed(kTrig) == 1 then
        event "i", "Trapped09", 0, iDur, iFrq, iAmp
    endif
endin

instr Trapped09
    ip3 = chnget:i("dur")
    iamp = chnget:i("amp")*.2

    kNote = chnget:k("note")+rnd(chnget:i("rndNote"))
    kFrq = cpsmidinn(kNote)*.25

    kRndAmp = chnget:k("rndAmp")
    kRndFrq = chnget:k("rndRate")

    k2 randh kRndAmp, kRndFrq, .1
    k3 randh kRndAmp * .98, kRndFrq * .91, .2
    k4 randh kRndAmp * 1.2, kRndFrq * .96, .3
    k5 randh kRndAmp * .9, kRndFrq * 1.3

    kenv linen iamp, ip3 *.2, ip3, ip3 * .8

    a1 oscil kenv, kFrq + k2, giSine, .2
    a2 oscil kenv * .91, (kFrq + .004) + k3, giWav2, .3
    a3 oscil kenv * .85, (kFrq + .006) + k4, giWav3, .5
    a4 oscil kenv * .95, (kFrq + .009) + k5, giWav4, .8

    kgate transeg 1, ip3, 0, 0
    amix = (a1 + a2 + a3 + a4) * kgate
    aL = a1 + a3
    aR = a2 + a4

    outs aL * chnget:k("masterLvl"), aR * chnget:k("masterLvl")

    garvb = garvb + (amix * chnget:k("rvbSend"))
    gadel = gadel + (amix * chnget:k("delaySend"))
endin

instr Delay
    denorm gadel
    ip3 = chnget:i("dur")
    kgate expseg 1, ip3*.7, 1, ip3*.3, .0001
    asig delay gadel, chnget:i("delayTime")
    asig = asig * kgate
    outs asig*chnget:k("masterLvl"), asig*chnget:k("masterLvl")
    gadel = 0
endin

instr Reverb
    denorm garvb
    k1 oscil .5, chnget:k("rvbPan"), 1
    k2 = .5 + k1
    k3 = 1 - k2
    asig reverb garvb, 3.1
    outs (asig * k2) * chnget:k("masterLvl"), ((asig * k3) * (-1)) * chnget:k("masterLvl")
    garvb = 0
endin

instr Monitor
    allL, allR monitor
    fout "/Users/lishi/Desktop/Research/CStore/out/Re-CAB-Trapped09_fout_allMix.wav", 14, allL, allR
endin
</CsInstruments>

<CsScore>
i 1 0 15
i "Trapped09" 0 15
i "Delay" 0 15
i "Reverb" 0 15
i "Monitor" 0 15
i "Trapped09" 0 15
i "Trapped09" 16 15
i "Trapped09" 31 15
</CsScore>
</CsoundSynthesizer>

```

A few things I’m unsure about, and I would really appreciate your guidance on the recommended way:

1. Is there a supported way to launch a Cabbage .csd render from command line (or via Python subprocess) that behaves like pressing Play, then automatically stops when the score ends?

2. If the best practice is to bypass Cabbage for offline rendering, would you recommend keeping a “Csound only” version of the file (no section) and running Csound directly to render to disk?

3. Regarding faster than realtime: do you have a minimal example of the k rate loop approach you mentioned for an offline bounce, or any flags/workflow you recommend in practice?

If it helps, I can also send a minimal repro .csd that shows the current Monitor based render working inside Cabbage, but not yet scriptable from Python.

Thanks again for your time.

Best,  
Charlie Shi

---

<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:** [20 January 2026 09:41 UTC](https://forum.cabbageaudio.com/t/fout-opcode-suppored-for-cabbage/4941/6 "2026-01-20T09:41:03Z")

</div>

Hi @CharlieSL1 glad to hear you made some progress.

> [@CharlieSL1](#):
>
> Is there a supported way to launch a Cabbage .csd render from command line (or via Python subprocess) that behaves like pressing Play, then automatically stops when the score ends?

No, the only way to do this would be to trigger a new instrument to do the work. Well, you could trigger Csound to run in the background using a Python process, but I would try to stay clear of the Python opcodes in Csound. Some hosts will not allow plugins to run subprocesses, and I have seen problems with this in Live in the past. Allowing plugins to launch arbitrary processes during playback is a bit of a security risk, so it is not surprising that DAWs are starting to guard against it. On the other hand, if this is just for prototyping a proof of concept, you might as well use whatever tools you can to help realize your ideas.

> [@CharlieSL1](#):
>
> If the best practice is to bypass Cabbage for offline rendering, would you recommend keeping a “Csound only” version of the file (no section) and running Csound directly to render to disk?

Personally, if I could, I would keep everything in a single .csd file. Your idea of batch processing a collection of instruments to add extra features seems like a neat and well-structured way to do things.

> [@CharlieSL1](#):
>
> Regarding faster than realtime: do you have a minimal example of the k rate loop approach you mentioned for an offline bounce, or any flags/workflow you recommend in practice?

Perhaps this is the missing piece of the puzzle for you. Check out this instrument. All it does is copy a sound file to disk, but it does so in a single k-rate loop. I am using Csound 7 syntax, but it is easy to change to Csound 6. There are no special tricks involved here.

```javascript

instr 1
    prints("Writing copy of file...\n")
    lenght:i = filelen("C:\\Users\\roryw\\sourcecode\\cabbage3-recipes\\samples/cleanGuitar.wav")*sr
    print(lenght)
    index:k = 0

    while index < lenght do
        sig:a = diskin2("C:\\Users\\roryw\\sourcecode\\cabbage3-recipes\\samples/cleanGuitar.wav", 1, 0, 1) 
        fout("C:\\Users\\roryw\\sourcecode\\cabbage3-recipes\\copy1.wav", 4, sig:a)
        index += 1
    od  

    if(release() ==1) then
        prints("Finished writing file...\n")
        ficlose("C:\\Users\\roryw\\sourcecode\\cabbage3-recipes\\copy2.wav")
    endif
endin

```

I hope this helps 🙂

---

<div class="post-metadata">

**Author:** ![CharlieSL1](https://forum.cabbageaudio.com/user_avatar/forum.cabbageaudio.com/charliesl1/32/6134_2.png) [@CharlieSL1](https://forum.cabbageaudio.com/u/CharlieSL1)\
**Post date:** [29 January 2026 21:46 UTC](https://forum.cabbageaudio.com/t/fout-opcode-suppored-for-cabbage/4941/7 "2026-01-29T21:46:19Z")

</div>

Hi Rory,

Thank you again for your advice on using fout and closing the soundfile properly. It was really helpful.

I followed your guidance and was able to export a WAV successfully from my Cabbage/Csound .csd workflow using Python. Your notes about avoiding multiple triggers and calling ficlose in the release phase made the difference.

Really appreciate your time and support.

Best regards,  
Li（Charlie) Shi
