[Csnd] opcodes to write/read pfields
| Date | 2019-12-08 14:02 |
| From | Eduardo Moguillansky |
| Subject | [Csnd] opcodes to write/read pfields |
|
Some time ago I wrote a set of opcodes pread/pwrite which can write/read to and from pfields of any active instance. This allows to create an instance and control/modulate its parameters from another instrument. They turned out to be very useful, much more straightforward than using globals, arrays, etc. These opcodes allow one-to-one and one-to-many communication. I would like to propose to include them (like this or modified) in the standard distribution. instr generator pset p1, p2, p3, 0.5, 1000, 4000, 0.1 kamp = p4 kfreq = p5 kcutoff = p6 kresonance = p7 asaw vco2, kamp, kfreq aout moogladder2, asaw, kcutoff, kresonance aout *= linsegr(0, 0.05, 1, 0.05, 0) outs aout, aout endin instr control iglissdur = p4 inum = nstrnum("generator") inum1 = inum + 0.001 inum2 = inum + 0.002 ; amp schedule inum1, 0, p3, 0.2 schedule inum2, 0, p3, 0.2 kfreq1 linseg ntof:i("4A"), iglissdur, ntof:i("3A") kfreq2 linseg ntof:i("4F"), iglissdur, ntof:i("3F") pwrite inum1, 5, kfreq1 pwrite inum2, 5, kfreq2 endin instr broadcast printf "filter start\n", 1 inum = nstrnum("generator") kcutoff linseg 4000, p3, 400 kresonance linseg 0.1, p3*0.5, 0.9 pwrite inum, 6, kcutoff, 7, kresonance endin schedule "control", 0, 8, 4 schedule "broadcast", 4, 4 Documentation lives here: https://csound-plugins.github.io/csound-plugins/opcodes/pwrite.html https://csound-plugins.github.io/csound-plugins/opcodes/pread.html Source: https://github.com/csound-plugins/csound-plugins
cheers, |
| Date | 2019-12-08 15:26 |
| From | joachim heintz |
| Subject | Re: [Csnd] opcodes to write/read pfields |
very cool. i checked it (works well) and i am convinced it is a very
nice extension of possible communication between instruments. and
moreover, it is really csoundish!
as this is a developer decision and mostly a developer discussion, i am
only wondering whether it should not go to the user's list but to the
developer's list?
joachim
On 08/12/2019 15:02, Eduardo Moguillansky wrote:
> Some time ago I wrote a set of opcodes pread/pwrite which can write/read
> to and from pfields of any active instance. This allows to create an
> instance and control/modulate its parameters from another instrument.
> They turned out to be very useful, much more straightforward than using
> globals, arrays, etc. These opcodes allow one-to-one and one-to-many
> communication. I would like to propose to include them (like this or
> modified) in the standard distribution.
>
> instr generator
> pset p1, p2, p3, 0.5, 1000, 4000, 0.1
> kamp = p4
> kfreq = p5
> kcutoff = p6
> kresonance = p7
> asaw vco2, kamp, kfreq
> aout moogladder2, asaw, kcutoff, kresonance
> aout *= linsegr(0, 0.05, 1, 0.05, 0)
> outs aout, aout
> endin
>
> instr control
> iglissdur = p4
>
> inum = nstrnum("generator")
> inum1 = inum + 0.001
> inum2 = inum + 0.002
>
> ; amp
> schedule inum1, 0, p3, 0.2
> schedule inum2, 0, p3, 0.2
> kfreq1 linseg ntof:i("4A"), iglissdur, ntof:i("3A")
> kfreq2 linseg ntof:i("4F"), iglissdur, ntof:i("3F")pwrite inum1, 5, kfreq1
> pwrite inum2, 5, kfreq2
> endin
>
> instr broadcast
> printf "filter start\n", 1
> inum = nstrnum("generator")
> kcutoff linseg 4000, p3, 400
> kresonance linseg 0.1, p3*0.5, 0.9
> pwrite inum, 6, kcutoff, 7, kresonance
> endin
>
> schedule "control", 0, 8, 4
> schedule "broadcast", 4, 4
>
>
> Documentation lives here:
>
> https://csound-plugins.github.io/csound-plugins/opcodes/pwrite.html
>
> https://csound-plugins.github.io/csound-plugins/opcodes/pread.html
>
> Source:
>
> https://github.com/csound-plugins/csound-plugins
>
>
> cheers,
> Eduardo Moguillansky
>
> Csound mailing list Csound@listserv.heanet.ie
> |
| Date | 2019-12-08 15:31 |
| From | Steven Yi |
| Subject | Re: [Csnd] opcodes to write/read pfields |
Hi Eduardo, First reaction was that I have some reservations. Some initial thoughts: 1. p-fields have been, up until now, init-time values 2. Changing the possible rate of p-type might have longer-term implications, especially if/when pfields change to be of any data type (in particular, user-defined types). 3. One can modify audio and control-rate values already per-instrument and per-instance using channels. The UDOs at [1] show some ways to make channel reading/writing easier. A video at [2] shows modifying channels during a live coding performance. Per-instance code example visible in the touchscreen examples from Android and iOS SDKs (see [3]). 4. However with channels, one can not easily set values for an instance via note event fields. One can make helper instruments for writing channel values once or over time and which are schedulable. That isn't quite the same thing though. One could take pfield values and always write that once at init-time into a channel though, then read from the channel at perf-time as an alternative. 5. Victor and I had some conversations years ago about per-instrument/per-instance channels. I think this also came up somewhat in idea of static variables per instrument. I can't recall much of the details unfortunately. I think it might be useful though to frame the issue in this way moving forward. 6. What happens in pwrite when there is a broadcast as well as per-instance writing of pfield? Is it order-dependent what value the instrument receives? Is a desired rule about what value an instrument should receive if it written to per-instance? I suppose some related-work would be: 1. SC3 and args 2. WebAudio and it's AudioParams (which can bet set directly, or automated over time, or written to by other generators) 3. DAWs which offer per-note data modification and this brings up from programming public/private data as well as static class data (i.e., something like "static public double frequency = 440" from Java). To summarize these thoughts: 1. This is a good problem to solve. 2. I'm not sure reusing the existing pfields implementation is the best solution 3. On the other hand, I like the clarity of the code in the pread/pwrite example. Something that approaches that would be great. 4. I think we need to account for other data types besides numbers as well as make this type safe. 5. We should think about how to expose this in the API as well. Steven p.s. - I will rewrite the example code using channels and post in a moment. [1] - https://github.com/kunstmusik/csound-live-code/blob/master/livecode.orc#L761-L810 [2] - https://youtu.be/q6GKT5irCBc [3] - https://github.com/csound/csound/blob/develop/Android/CsoundForAndroid/CsoundAndroidExamples/src/main/res/raw/multitouch_xy.csd On Sun, Dec 8, 2019 at 9:02 AM Eduardo Moguillansky |
| Date | 2019-12-08 16:13 |
| From | Steven Yi |
| Subject | Re: [Csnd] opcodes to write/read pfields |
And here's an equivalent example rewritten using channels. Code has
some UDO's, but code using the UDOs has roughly the same number of
lines as the original. A little more verbose, but I see it as +/- as
one can target instrument params by name rather than pfield index.
/* assemble fractional instrument number for named instrument and
instance number */
opcode ni, i, Si
Sname, iinst xin
xout nstrnum(Sname) + (iinst / 1000)
endop
/* Assemble channel name from instr name and param name */
opcode chan_name, S, SS
Sinstr, Schan xin
SchanName = sprintf("%s.%s", Sinstr, Schan)
xout SchanName
endop
/* Assemble channel name from instr name, instance number, and param name */
opcode chan_name, S, SiS
Sinstr, iinst, Schan xin
SchanName = sprintf("%s.%d.%s", Sinstr, iinst, Schan)
xout SchanName
endop
/* Read channel for instr and param name, initializing with default value */
opcode read_chan, k,SSi
Sinstr, Schan, initVal xin
SchanName = chan_name(Sinstr, Schan)
chnset(initVal, SchanName)
xout chnget:k(SchanName)
endop
/* Read channel for instr name, instance number, and param name,
initializing with default value */
opcode read_chan, k,SiSi
Sinstr, ifrac, Schan, initVal xin
SchanName = chan_name(Sinstr, ifrac, Schan)
chnset(initVal, SchanName)
xout chnget:k(SchanName)
endop
instr generator
pset p1, p2, p3, 0.5, 1000, 4000, 0.1
iinst = round(frac(p1) * 1000)
print iinst
kamp = read_chan("generator", "amp", p4)
kfreq = read_chan("generator", iinst, "freq", p5)
kcutoff = read_chan("generator", "cut", p6)
kresonance = read_chan("generator", "res", p7)
asaw vco2, kamp, kfreq
aout moogladder2, asaw, kcutoff, kresonance
aout *= linsegr(0, 0.05, 1, 0.05, 0)
outs aout, aout
endin
instr control
iglissdur = p4
inum1 = ni("generator", 1)
inum2 = ni("generator", 2)
print inum1
print inum2
schedule(inum1, 0, p3, 0.2)
schedule(inum2, 0, p3, 0.2)
kfreq1 = linseg:k(cpsmidinn(69), iglissdur, cpsmidinn(57))
kfreq2 = linseg:k(cpsmidinn(66), iglissdur, cpsmidinn(54))
chnset(kfreq1, chan_name("generator", 1, "freq"))
chnset(kfreq1, chan_name("generator", 2, "freq"))
endin
instr broadcast
printf "filter start\n", 1
inum = nstrnum("generator")
kcutoff linseg 4000, p3, 400
kresonance linseg 0.1, p3*0.5, 0.9
chnset(kcutoff, chan_name("generator", "cut"))
chnset(kresonance, chan_name("generator", "res"))
endin
schedule "control", 0, 8, 4
schedule "broadcast", 4, 4
On Sun, Dec 8, 2019 at 10:31 AM Steven Yi |
| Date | 2019-12-08 17:19 |
| From | Eduardo Moguillansky |
| Subject | Re: [Csnd] opcodes to write/read pfields |
I like this approach. We just need to write opcodes that do this
built-in (and without polluting the channel hash-table with short-lived
entries which will make every channel opcode slower).
On 08.12.19 17:13, Steven Yi wrote:
> And here's an equivalent example rewritten using channels. Code has
> some UDO's, but code using the UDOs has roughly the same number of
> lines as the original. A little more verbose, but I see it as +/- as
> one can target instrument params by name rather than pfield index.
>
>
> /* assemble fractional instrument number for named instrument and
> instance number */
> opcode ni, i, Si
> Sname, iinst xin
> xout nstrnum(Sname) + (iinst / 1000)
> endop
>
> /* Assemble channel name from instr name and param name */
> opcode chan_name, S, SS
> Sinstr, Schan xin
> SchanName = sprintf("%s.%s", Sinstr, Schan)
> xout SchanName
> endop
>
> /* Assemble channel name from instr name, instance number, and param name */
> opcode chan_name, S, SiS
> Sinstr, iinst, Schan xin
> SchanName = sprintf("%s.%d.%s", Sinstr, iinst, Schan)
> xout SchanName
> endop
>
> /* Read channel for instr and param name, initializing with default value */
> opcode read_chan, k,SSi
> Sinstr, Schan, initVal xin
> SchanName = chan_name(Sinstr, Schan)
> chnset(initVal, SchanName)
> xout chnget:k(SchanName)
> endop
>
> /* Read channel for instr name, instance number, and param name,
> initializing with default value */
> opcode read_chan, k,SiSi
> Sinstr, ifrac, Schan, initVal xin
> SchanName = chan_name(Sinstr, ifrac, Schan)
> chnset(initVal, SchanName)
> xout chnget:k(SchanName)
> endop
>
> instr generator
> pset p1, p2, p3, 0.5, 1000, 4000, 0.1
>
> iinst = round(frac(p1) * 1000)
> print iinst
>
> kamp = read_chan("generator", "amp", p4)
> kfreq = read_chan("generator", iinst, "freq", p5)
> kcutoff = read_chan("generator", "cut", p6)
> kresonance = read_chan("generator", "res", p7)
> asaw vco2, kamp, kfreq
> aout moogladder2, asaw, kcutoff, kresonance
> aout *= linsegr(0, 0.05, 1, 0.05, 0)
> outs aout, aout
> endin
>
> instr control
> iglissdur = p4
>
> inum1 = ni("generator", 1)
> inum2 = ni("generator", 2)
>
> print inum1
> print inum2
>
> schedule(inum1, 0, p3, 0.2)
> schedule(inum2, 0, p3, 0.2)
>
> kfreq1 = linseg:k(cpsmidinn(69), iglissdur, cpsmidinn(57))
> kfreq2 = linseg:k(cpsmidinn(66), iglissdur, cpsmidinn(54))
> chnset(kfreq1, chan_name("generator", 1, "freq"))
> chnset(kfreq1, chan_name("generator", 2, "freq"))
> endin
>
> instr broadcast
> printf "filter start\n", 1
> inum = nstrnum("generator")
> kcutoff linseg 4000, p3, 400
> kresonance linseg 0.1, p3*0.5, 0.9
> chnset(kcutoff, chan_name("generator", "cut"))
> chnset(kresonance, chan_name("generator", "res"))
> endin
>
> schedule "control", 0, 8, 4
> schedule "broadcast", 4, 4
>
> On Sun, Dec 8, 2019 at 10:31 AM Steven Yi |
| Date | 2019-12-08 18:05 |
| From | Michael Gogins |
| Subject | Re: [Csnd] opcodes to write/read pfields |
I think this is a very good idea. Regards, Mike On Sun, Dec 8, 2019, 09:02 Eduardo Moguillansky <eduardo.moguillansky@gmail.com> wrote:
|
| Date | 2019-12-08 23:16 |
| From | Victor Lazzarini |
| Subject | Re: [Csnd] opcodes to write/read pfields |
I agree we should move with caution. The trouble with introducing these things is that we can't go back. Also agree that instrument interfaces is an interesting topic to discuss. The channel-based approach Steven mentioned works well and has been used in several examples. Maybe we need to make it more seamless and fully builtin. Prof. Victor Lazzarini Maynooth University Ireland > On 8 Dec 2019, at 15:32, Steven Yi |
| Date | 2019-12-08 23:57 |
| From | Eduardo Moguillansky |
| Subject | Re: [Csnd] opcodes to write/read pfields |
On 09.12.19 00:16, Victor Lazzarini wrote: > I agree we should move with caution. The trouble with introducing these things is that we can't go back. > > Also agree that instrument interfaces is an interesting topic to discuss. > > The channel-based approach Steven mentioned works well and has been used in > several examples. Maybe we need to make it more seamless and fully builtin. I would love to see a builtin solution for this. The other problem with the channel-based approach is the pollution of the global hash-table with entries which are by definition short-lived and would with time add up and make every channel operation slower. There could be a sepparate hash-table only for this communication layer, with the functionality to dispose entries when the instance becomes inactive. One feature which the original proposition had, which makes the code very clear, is that the same procedure used to read initial parameters is used to read dynamic parameters. Supercollider follows this same principle and makes for very terse code. Using channels, the instrument creating the instance should set-up the channels with initial values before creating the new instance, or should rely on the new instrument setting the initial value of the channel to the preset, etc. A lot of convention for a process which aims to be the default way of communicating between instruments. > > Prof. Victor Lazzarini > Maynooth University > Ireland > >> On 8 Dec 2019, at 15:32, Steven Yi |