Cabbage Logo
Back to Cabbage Site

Transient shaper algorithm

Hi,

I am currently working on a plugin and I need to be able to edit transients. I studied the theory behind transient shaping, and to implement that I would need to be able to code an envelope follower

Here is an extract of my researches around the topic:

Basically, once you have two envelopes (same release times but different attack times), you can substract them and then use the difference to boost/reduce the original signal.

I coded a basic implementation with Cabbage and it works very well on plain drums, but when you use it with more complex audio files it sounds horrible: the resulting waveform over-saturates & become really messy.

I have two questions regarding the algorithm behind:

- Do you know how I could edit the envelope so it has more logarithmic/exponential/linear slopes? I am not sure about how to do it… maybe by simply computing the exponential/logarithm of the follow2 result? Or I can multiply the envelope by the signal and then apply filters (I saw plugin makers using RC filters to smooth the result)

- Do you know why the song saturates? I tried to balance the signal after & before the multiplication without success…

Here are the steps involved:

Here are the top 2 resources I found about this subject:

Thanks for your help and have a nice day :slight_smile:

This is a common trick. A simple low pass filter will smoothen the signal for you, tone might do the trick.

I’m not well versed in the ways of followers/shapers, but I can only assume it’s because your boosting the amplitude too much. I’d try writing the attackdiff values to the console to see if you spot anything crazy. You can use downsamp and printk to do this. Another useful thing to do is to write the envelopes to a sound file using fout and open them in an editor to see what’s going on. Let me know how you get on. There are some DSP gurus we can ping if we need to :+1:

Hi Rory,

Thanks for the feedback! I will try these tricks and see if it works :slight_smile:
I’m not familiar with Csound variable structures yet, and I’m not sure how follow2 envelope results are stored. It’s probably a simple Array, maybe we can use a transfer function to edit the shape and then smooth the resulting signal to remove artifacts.

Btw I spent my week-end reading tons of documentation about Csound, Cabbage and DSP in general and I saw many DSP gurus documenting a lot of tricks online, this is really cool! Can’t wait to have a better level and give back to the community.

In a nuthsell, i-rate vars are constants, but can be updated on each new instance of an instrument, k-rate values are scalars which are updated every ksmps samples, and a-rate vars are blocks of ksmps samples. So the output of follow2 is a block samples. If you pass it through a tone you can smooth the signal. Here’s a simple example.

instr 1
    a1 diskin2 "voice.wav", 1, 0, 1
    a2 follow2 a1, .1, .1
    a3 tone a2, 10
    ;outs a1, a1
    fout "followOut.wav", 4, a2
    fout "followOutTone.wav", 4, a3
endin

follow2Test.csd (746 Bytes)

Thanks it’s very clear!

Would it be possible to use a Cabbage widget to visualize a2 or a3? Maybe with a gentable?

Sure, you can load the sound into a soundfiler widget. You could also load it to a function table and then display it that way without the need to write to disk. Note my earlier example was done with an older version of Cabbage that didn’t mind relative paths. The newer versions of Cabbage need absolute paths. Hence the sprintf’s in this example.

follow2Test.csd (1.1 KB)

Thanks!! Can’t wait to get back home and try it :slight_smile:

1 Like

Hi Rory, sorry to bother you again… I spent a lot of time this evening trying to understand how to load it in a function table.

What I did was :

  • init a function table
  • use tablew to write the signal at index 0 of the function table
  • then the soundfiler widget should be updated since it’s linked to the function table

And it’s not working… Is tablew the right opcode to use? I thought it would do the trick.

Also, even this code is not working:

; sr is set by host
ksmps = 64
nchnls = 2
0dbfs = 1

instr 1
a1,a2 diskin2 “samples/mjdilla.mp3”,1,0,1
fout sprintf("%s/mjdilla.mp3", chnget:S(“CSD_PATH”)), 4, a2
endin

It writes a file, but the files is “empty” and I can’t read it with VLC.

It is very similar to your example!

Thanks again for your help

I’m not sure fout can’t handle writing of mp3s. For now I’d try with wavs. I’m not at my PC now but can prepare an example with function table for you later. Tablew should do the trick. You’ll need to use an a-rate index. If you post the coffee I can probably tell you what’s wrong…

I will try to dig in the documentation and then post my result!

I have a sketchy implementation haha

Here it is:

<Cabbage>
form caption("Test") size(500, 400), pluginId("envf"), guiRefresh(16), guiMode("queue")
soundfiler bounds(4, 200, 200, 100), tableNumber(1)
</Cabbage>
<CsoundSynthesizer>
<CsOptions>
-d -n
</CsOptions>
<CsInstruments>
; sr is set by host
ksmps  =  64
nchnls =  2
0dbfs  =  1

gilen = 131072
gicps = sr/gilen
gitab ftgen 1, 0, gilen, 10, 1

instr 1
    ;a1,a2 ins
    a1,a2  diskin2 "samples/loop.wav",1,0,1
    aenvtest follow2 a1, 0.001, 0.001
    
    aphs phasor gicps
    andx = aphs * gilen
    
	tablew aenvtest, andx, gitab  
    aosc table aphs, gitab, 1

    outs a1, a2
endin

</CsInstruments>

<CsScore>
f0 z
i 1 0 [60 * 60 * 24 * 7]
</CsScore>

</CsoundSynthesizer>

There are a few issues:

  • The soundfiler is only updated when I stop the Cabbage plugin. I’d like to “stream” the result in the soundfiler instead
  • I’m not sure using a phasor is useful, but I can’t find anything else in the documentation

I will have to dig more!

edit: it looks like you already answered a similar question here Continuous display of audio data

As you saw in the other post, you can set a timer to update the table, but note there is a performance hit with this. While it’s fine for testing, it might not be great for the final product. The phasor works well here. If you wanted to manually increment the sample index you’d need to set ksmps to 1, and increment a k-rate variable a a-time. The use of the phasor is more efficient.

Let me know if you get stuck with the continuous updating.