[Csnd] arrays as control values

Hello everybody!

I’m trying to use arrays as a source for control values. So i was thinking about creating an UDO for this.
And it’s already working but i always get a click between end and beginning of the array which is problematic. Maybe someone can help.

My UDO looks like this:

opcode ctrl_arr,a,kk[]oo
  ;; reads an array as input and outputs control signals with the
  ;; option to normalize the indexes of the input array and the output
  ;; the array is copied into a table to be used with tablei and read
  ;; out with a phasor
  
  ;; input
  kSpeed,kArr[],iNormIndex,iNormOutput xin
  
  if iNormOutput == 1 then
    scalearray kArr,0,1
  endif

  iTable ftgen 0,0,lenarray:i(kArr),2,0
  copya2ftab kArr,iTable

  aIndex phasor kSpeed
  aCtrl tablei aIndex,iTable,iNormIndex
  
  ;; output
  xout aCtrl
endop

And this is my instrument for testing purposes and which clicks:

<CsoundSynthesizer>
<CsOptions>
-d -odac -W -3
</CsOptions>
<CsInstruments>
sr = 48000
ksmps = 64
nchnls = 2
0dbfs = 1.0

opcode ctrl_arr,a,kk[]oo
  ;; reads an array as input and outputs control signals with the
  ;; option to normalize the indexes of the input array and the output
  ;; the array is copied into a table to be used with tablei and read
  ;; out with a phasor
  
  ;; input
  kSpeed,kArr[],iNormIndex,iNormOutput xin
  
  if iNormOutput == 1 then
    scalearray kArr,0,1
  endif

  iTable ftgen 0,0,lenarray:i(kArr),2,0
  copya2ftab kArr,iTable

  aIndex phasor kSpeed
  aCtrl tablei aIndex,iTable,iNormIndex
  
  ;; output
  xout aCtrl
endop

instr 1

  ;; controls
  kSpeed = 0.25
  kValues[] fillarray 80,160,40
  aAmp ctrl_arr kSpeed,kValues,1,1
  aFreq ctrl_arr kSpeed,kValues,1
  kAmp = k(aAmp)
  ;; signals
  aSine1 poscil3 kAmp,aFreq
  aSine2 poscil3 kAmp,aFreq*0.75

  ;; output
  aOut1 = aSine1
  aOut2 = aSine2
  outs aOut1,aOut2
endin
;-----------------------------------------------------------
</CsInstruments>
<CsScore>
i1 0 50
</CsScore>
</CsoundSynthesizer>

Csound mailing list
Csound@listserv.heanet.ie
https://listserv.heanet.ie/cgi-bin/wa?A0=CSOUND
Send bugs reports to
        Issues · csound/csound · GitHub
Discussions of bugs and features can be posted here

Hi Philipp,
just as a matter of interest, have you tried passing your amplitude control through a portamento filter:
kAmp = port(kAmp, .01)
The click might be caused by an instantaneous jump in volume and frequency.

Best wishes,

Jeanette

yes the problem is that the first and last value of the array have a distance and there is no imterpolation betwween them:
  80, 160, 40
will cause a click between 40 at the end of the first cycle, and 80 at the beginning of the first cycle.

you can see what happens when you change the output to
    outs (aOut1+aOut2) / 2, aAmp
and the look at the audio file.

cheers -
  joachim

of course….

this was it. But i’m wondering if there is a way to interpolate between last and first value of the array (without port)?

i guess that's complicated, and then you must define in which wayyou want to interpolate (which time to use from which segment, interpolate linear or other).

but why not use port or a similar opcode for the amplitudes? a very short interpolation time should be sufficient, and for the frequencies it is not necessary.

Hi Philipp!
Oct 1 2023, Philipp Neumann has written:
...

But i’m wondering if there is a way to interpolate between last and first value of the array without port?

...
You would need some kind of lowpass filter, slew or lag processor. Port
is one, lag is another. There are a few more control signal filters.
Look at the manual and browse the "Standard Filters" section. Or use
some manner of processing on the origiinal array.

Were you thinking of another approach?

Best wishes,

Jeanette

Other issues aside, the ftable does not contain three values to begin with, it is only holding two (80, 160).

ftprint iTable will confirm. And as it stands now the frequency is also dropping to 0 Hz (probably not what you want).

Adding:
kFrq = k(aFrq)
printk .25, kFrq
will also confirm that.

You need to make the table size negative since lenarray is not a power of two.

iTable ftgen 0,0,-lenarray:i(kArr),2,0

The frequency will still drop to 0 although then it’s generally mitigated by the amp.

Scott

Perhaps you could generate a bigger array with interpolated values interleaved… something like this

kvalues[] fillarray 80,160,40
kmodvals[] ArrRtt kvalues,1 ;ArrRtt from the csudo Repository
klinterp[] linlin 0.5, kvalues, kmodvals

kcombine[] interleave kvalues, klinterp

;The kcombine array now looks like this => [80.0000 120.0000 160.0000 100.0000 40.0000 60.0000]

(although… I get weird behaviour with the interleave opcode running this. Not sure if it’s a bug or just me).

Thorin

Thanks for the hint! I also didn’t notice the frequency drop….

Can someone explain why the frequency in this example is dropping to zero and how to prevent this?
I really don’t have a clue. Or maybe i’m taking the wrong approach in using an array for control data.

Again the Instrument:

<CsoundSynthesizer>
<CsOptions>
-d -odac -W -3
</CsOptions>
<CsInstruments>
sr = 48000
ksmps = 64
nchnls = 2
0dbfs = 1.0

opcode ctrl_arr,a,kk[]oo
  ;; reads an array as input and outputs control signals with the
  ;; option to normalize the indexes of the input array and the output.
  ;; the array is copied into a table to be used with tablei and read
  ;; out with a phasor.
  ;; to prevent clicks when used with amplitude values use 'port' on
  ;; the output
  
  ;; input
  kSpeed,kArr[],iNormIndex,iNormOutput xin

  ;; create table from array
  if iNormOutput == 1 then
    scalearray kArr,0,1
  endif
  
  iTable ftgen 0,0,-lenarray:i(kArr),2,0
  copya2ftab kArr,iTable

  ;; read the table
  aIndex phasor kSpeed
  aCtrl tablei aIndex,iTable,iNormIndex
  
  ;; output
  xout aCtrl
endop

instr 1

  ;; controls

  kValues[] fillarray 220,110,440
  iLen lenarray kValues
  kSpeed = 0.25
  aAmp ctrl_arr kSpeed,kValues,1,1
  aFreq ctrl_arr kSpeed,kValues,1
  kAmp = k(aAmp)
  kAmp port kAmp,0.05
  ;; signals
  aSine1 poscil3 kAmp,aFreq
  aSine2 poscil3 kAmp,aFreq*0.75

  ;; output
  aOut1 = aSine1
  aOut2 = aSine2
  outs aOut1,aOut2
endin
;-----------------------------------------------------------
</CsInstruments>
<CsScore>
i1 0 50
</CsScore>
</CsoundSynthesizer>

Hi Philipp,

not knowing your further intentions for applying control signals beyond the test instr perhaps easier for anyone to offer better advice with a little more info.

In this one specific case, converting the a-rate amp signal to k-rate doesn’t seem to offer any benefits and can also result in undesirable stepping/artefacts, even using port. I didn’t initially point that out, not knowing the full scope of how you might use the control signals outside of the test instr. Adding lag to aAmp eliminates the clicks. And of course it would be easy to use seperate arrays for amp & freq.

Also not sure if you intend to change the kArr values in realtime or not. This could possibly result in complications as the table is created at i-rate. There might be some workarounds though. If not then an i-rate array might suffice? And possibly offer other solutions as well as using less overhead (of that I’m not sure).

I don’t know why the frequency continues to drop to 0. It likely has something do so with the tablei/table3 interpolation (it doesn’t occur with table). Why it interpolates to 0 at the end of the table but not the beginning, not sure. Maybe someone can explain.

That said, understanding that “it just does” makes it relatively easy to create a workaround. Just imagine that the table is adding a 0 at the end of the array, so limit the phasor to compensate.

-d -odac -W -3 sr = 48000 ksmps = 64 nchnls = 2 0dbfs = 1.0

opcode ctrl_arr,a,kk[]oo
;; reads an array as input and outputs control signals with the
;; option to normalize the indexes of the input array and the output
;; the array is copied into a table to be used with tablei and read
;; out with a phasor

;; input
kSpeed,kArr[],iNormIndex,iNormOutput xin

if iNormOutput == 1 then
scalearray kArr,0,1
endif

iTable ftgen 0,0,-lenarray(kArr),2,0
copya2ftab kArr,iTable
aIndex phasor kSpeed
; limit phasor range
iLmt = 1 - 1/(lenarray(kArr))
print iLmt
aCtrl table3 aIndex * iLmt,iTable,iNormIndex
; output
xout aCtrl
endop

instr 1
;; controls
kSpeed = 0.25
kValues[] fillarray 80,160,40
aAmp ctrl_arr kSpeed,kValues,1,1
aFreq ctrl_arr kSpeed,kValues,1
kAmp = k(aAmp)
;; signals
aSine1 poscil3 aAmp,aFreq
aSine2 poscil3 aAmp,aFreq*0.75
;; output
aOut1 = lag(aSine1, .001)
aOut2 = lag(aSine2, .001)
outs aOut1,aOut2
endin
;-----------------------------------------------------------


i1 0 48

Anyways, that seems to work fine in this specific case but no idea if that fits your overall plan.

Hey Scott.
Thank you for your reply.
I guess, for now, it works when i limit the phase and use the same starting and ending value in the array.

The dropping to 0 has probably something to do with the guard point i guess. And i don’t know how it is handled inside the table.

I really can’t offer more information, because i just know that i want to create algorithmically, outside of csound, arrays and use them inside csound to control my instruments. it has also something to do with a composition approach i’m trying out, which i can’t explain here further.

But for now it’s working!

Thank you everybody!