Cabbage Logo
Back to Cabbage Site

Sample Rate Reduction not using Fold

Hi everybody,

Looking to write a sample rate reduction/downsampling opcode/effect. I have been messing with the ‘fold’ opcode for this purpose, and it sounds pretty good(or bad depending on your taste), but I am curious to see if there is any noticeable difference between that and a true downsample -> upsample, sample rate reducer. My processing with fold is this:

adsL fold aL, 44100 / kfold

Here, kfold Hz from 1 to 44100.

From some tests I have been doing, it looks as though a significant amount of high frequency content is still there after the signal is run through the ‘fold’ opcode, see the attached screen shot. My DSP knowledge is only so-so, but I would assume that there would not be any content above kfold / 2, or even kfold… I have tried a few SoX commands (both downsample and -r/rate) and it seems like there is a pretty steep lowpass filter prior to the rate reduction in both of those commands… that being said, it looks as though there is only minor high frequency leakage above the downsampled Nyquist…

So, I guess my question is: Is it possible to create an opcode that downsamples and then upsamples a given signal? From what I could tell this would require a modifiable local ksmps as the downsample opcode outputs a k signal… can we do this? I have seen some other forum posts that suggest changing the global ksmps after the program has started running may cause problems… Here is what I’ve got so far, but the setksmps opcode does not seem to do anything, changing the global ksmps does though, but not really what I am after…

aL inch 1
kfreq chnget “freq”
setksmps i(kfreq)

kdsL downsamp aL
ausL upsamp kdsL

outch 1, ausL

Any help and/or opinions is greatly appreciated, thanks!

Interesting question. So I guess you can dowsample using the downsamp opcode. And you can use setksmps to set the downsample factor. While you can’t change the local ksmps at runtime, you can can it freely at init time. setksmps will set the local ksmps, not the global one. Once you have downsampled it I you can simply pass it to a channel for sampling back to the audio rate? I don’t know. Check this example. Changing setksmps to 1 result in no folder, but change the size and hey presto - folder over?

instr 1
setksmps 32
kFreq line 0, 100, 1000
a1 oscili 1, kFreq
k1 downsamp a1
chnset a(k1), "down" 
endin

instr 2
a1 chnget "down"
outs a1, a1
endin

Rory,

Thanks for the quick response. I think I maybe asked the wrong question, it seems as though downsampling and upsampling works just fine(so long as the downsampling rate is set only once).

That being said, what I am really looking for is a way to assign the downsamp krate to a slider/variable input param, to get a variable true downsampled signal. Using the ‘fold’ opcode, I am able to modify the folderover, but I am really curious to see how a variable true downsample sounds/compares…

Can you REINIT the ksmps during runtime? Perhaps similar to this:

instr 1

    gkbypass	chnget	"bypass"

    aL                      inch 1
    aR                      inch 2

    	
    kdry	 	            chnget		"dry"
    kwet	 	            chnget		"wet"
    
    kfreq	                chnget		"freq"
    
    if changed(kfreq)==1 then
        reinit REINIT
    endif
    REINIT:
        setksmps i(kfreq)
    
    kdsL downsamp aL
    ausL upsamp kdsL 
    
    kdsR downsamp aR
    ausR upsamp kdsR 
    
    outch 1, ausL
    outch 2, ausR
endin

Thanks again,

also/PS - is there a markdown guide for cabbage forums?

Did it work with the reinit? I don’t see why it wouldn’t. You’re only changing the local ksmps, and not the global one… :thinking:

No,

There are a few error messages I’m getting in the console. It looks like local ksmps must be equal/less than global ksamps, so I have made global ksmps 44100. I have also typecast I(kfreq) to int. The local ksmps still don’t seem to be valid…

I’m getting the same problems here. Let’s see what the Csound community get back to you with. Maybe they have some answers…

Sounds good, thanks Rory :+1:

Rory,

Looks like ‘fold’ was the answer I was looking for, thanks for helping me work through it anyways. That being said, I did put together a mpulse and samphold version. I think it does that same thing, but it does delay the audio output by some amount of time… I won’t be using this, but it is noticeable… perhaps it is a side effect of the samphold opcode. Here is my instr if you want to try it out

instr 1

    gkbypass	chnget	"bypass"

    aL                      inch 1
    aR                      inch 2

    	
    kdry	 	            chnget		"dry"
    kwet	 	            chnget		"wet"
    
    kfreq	                chnget		"freq"
    
    aimp mpulse 1, 1 / kfreq
    
    adsL samphold aL, aimp
    adsR samphold aR, aimp
    
     
    if gkbypass == 0 then
        outch 1, (aL * kdry) + (adsL * kwet)
        outch 2, (aR * kdry) + (adsR * kwet)
    else
        outch 1, aL
        outch 2, aR
    endif
    
  
endin

thanks again!

1 Like

I haven’t looked at that example with samphold yet, but I just wanted to clear up some confusion.

my DSP knowledge isn’t that good either, but I wouldn’t care to predict how the output of regular downsampling will show up on a spectroscope-type analyzer. FFT? it may be an artifact of this process. if it’s just hi-freq noise and not content really then maybe it makes sense. i wouldn’t necessarily assume this means fold opcode is doing any more than exactly what you said, downsample followed by upsample. just based on what little I know about how FFT works…

Second, let’s talk about how you would implement this in a user-defined opcode with your own DIY downsampling algorithm. you don’t need to get involved with setting kr or ksmps at all to do it. Actually you SHOULDNT use a local ksmps at all, that is not the correct tool.

Instead you want to put every single sample coming in (higher sample rate) into some kind of buffer and then read the buffer in a “downsampled” fasion, essentially skipping table values as you go, like playing hopskotch through the table. I say table but you could use an array instead.

You’re trying to handle the mapping of sample values from one sample rate to the lower one via a change in variable type from a to k. But you should be treating a and k as essentially the same. K rate variables are really just arrays of a-rate samples. So when you are working in a rate, you cn do the downsampling in a way that gives the same result no matter how ksmps is set.

If I were gonna do it I would use the non-interpolating table read/write opcodes (table and tablew). You set a write index first (a-rate) and then create a read index that is essentially a stepped version of the write index (but it’s also a-rate). None of this should probably be done thru the use of k-rate operation at this point. Your “read index” will be low-resolution (stepped) but actually still be an a-rate signal regardless. How exactly you do this will decide how exactly your remapping of samples proceeds. I am lazy and would probably use some scaling followed by int() followed by rescaling to quickly create the “stepped” index.

Then you just write the current a-sig (input) to the empty pre-allocated table using the “fine resolution” writing index, followed by a second step where you read back from the buffer using the “low resolution” stepped read index.

Essentially writing perfectly to the table and then reading it back “wrong” (resulting in (X-1) out of every (X) samples being ‘lost’ while 1 out of every X samples gets repeated X times, where X depends on what downsampling-ratio you pick).

Obviously there are other ways to do the actual implementing, including some ways that would involve translating from a-rate into a k-rate buffer array and back to a-rate (but despite making use of k-rate operations, this can be implemented such that the downsampling ratio is controlled separately from ksmps, which would be set constant like normal).

My suggestion of how to implement using table/tablew would be just one example of an implementation, but the point is that you can write your own DIY downsampling algorithm using primitive opcodes, and none of this has to do with setting a local ksmps. It’s much more like what you would be doing if you wrote a downsampling algorithm in C or C++, except with Csound syntax. Put values on a buffer, then take them off. If you want to add realtime controls on the downsample-ratio, like you say, you would do this by adding some calculations in between the “writing index” and the “reading index”.

It’s not the first time I’ve observed people misunderstanding what a local ksmps is for (it’s really only useful in very specific cases) and making things seem more complex than they are.

Let me know if any of that makes sense.

1 Like