~ambiServer = { arg i; Server.embed(Synth("ambi", [\out, ~ambiBus[i]]), addr: ~myAddr[i], ~myOptions, synth: Synth("test")); } ! 4;
d'oh. this should read:
~ambiServer = { arg i; Server.embed(Synth("ambi", [\out, ~ambiBus[i]]), addr: ~myAddr[i], ~myOptions); } ! 4;
---
and I just want to add that this is really orthogonal to the Supernova approach, it's actually possible to have both.
Christof
Gesendet: Dienstag, 28. Mai 2019 um 12:09 Uhr Von: "Christof Ressi" christof.ressi@gmx.at An: sc-dev@lists.bham.ac.uk Betreff: Aw: Re: [sc-dev] Re: [sc-dev] sc-dev meeting minutes
Where it is unfortunate (now) is that, for each server that needs to access memory, each server needs to allocate it’s own.
yes, embedding Servers doesn't solve this problem either but often we have more than enough RAM anyway.
Physical Output buses, obviously, get summed together for you when writing to the sound card, but this is different than what happens if two instances of scsynth write to then same virtual bus, and I think is something that will lead to confusion with this approach. Not that it can’t be overcome, but I do think it is an issue. Especially in case where you are writing sound to many buses that need additional processing done (for example, writing output with Amibisonic encoding that needs to be decoded for each instance of scsynth).
that's exactly where embedded Servers can help. here's a fictional example:
( // SynthDef to produce a 3rd order ambisonic signal in a seperate Server instance SynthDef("ambi", { arg out; Out.ar(out, Server.ar(numOut: 16)); }).add;
// create 4 ambisonic busses ~ambiBus = { Bus.audio(numChannels: 16) } ! 4;
// create 4 NetAddress ~ambiAddr = { arg i; NetAddr.new("localhost", 57111 + i) } ! 4;
// create some Server options if needed (numInputBusChannels/numOutputBusChannels can be deduced from the Ugen) // ~myOptions = )
// create 4 Servers with the fictional "embed" method: Synth, name, addr, options, clientID // internally, each Server instance writes to its 16 hardware outputs and the Ugen will route this to the given audio bus (= ~ambiBus[i])
~ambiServer = { arg i; Server.embed(Synth("ambi", [\out, ~ambiBus[i]]), addr: ~myAddr[i], ~myOptions, synth: Synth("test")); } ! 4;
// now we can encode each ~ambiBus signal and finally decode all of them.
does this make sense? we actually do stuff like this in Pd with [pd~].
the key point is to realize that Server.ar really is a Unit generator (a block box which consumes/produces audio). Internally, the Ugen would probably buffer the inputs and outputs, so we wouldn't need to implement any audio bus locking. the procedure is: a) lock the buffer b) copy Ugen input to buffer, copy last Server result to Ugen output. c) unlock and signal the embedded Server to process the next block we would get a constant delay of one block but we should never have to wait for the embedded Server.
treating a Server like a UGen means we can freely (and dynamically!) interconnect different Server instances in Supercollider and also nest them arbitrarily. Running each Server on its own thread is a very simple solution which doesn't scale (having too many threads can lead to performance issues), but for many use cases it would be fine I guess.
I can probably hack together a prototype of this in a day or so, maybe I do this in summer :-) Of course, the Supernova approach is vastly superior but also though to implement.
Christof
Gesendet: Dienstag, 28. Mai 2019 um 04:53 Uhr Von: josh@realizedsound.net An: sc-dev@lists.bham.ac.uk Betreff: Re: [sc-dev] Re: [sc-dev] sc-dev meeting minutes
On May 27, 2019, at 7:47 PM, christof.ressi@gmx.at wrote:
I've written a multithreaded virtual modular synthesizer and it is quite easy as long as the thread management is done manually (the user says: please run this node on thread X) but it gets hairy when it should be done automatically (e.g. based on average CPU use).
but there's also a totally different possibility of concurrency: embedding Scsynth instances within itself. In Pd there's an object [pd~] which runs a seperate Pd instance in another process. You can send/receive audio and messages, but you can't immediately share data (although there's the "shmem" external for shared memory). The reason Pd has to run in a seperate process is that it is a fundamentally single-threaded program with lots of global state (but there have been recent efforts to overcome this limitation utilizing thread local storage).
AFAICT, Scsynth on the other hand has very little (mutable) global state: gUnitDefLib, gBufGenLib, gPlugInCmds and those dictionaries can actually be shared across instances (they just have to be made thread-safe). This means it would be possible to run a complete seperate Scsynth instance inside a single Ugen in a dedicated thread. The parent and child instance(s) don't share any data, but they can exchange audio data (and maybe also control data). You can basically achieve the same thing with running several Scsynth processes and connecting them with Jack, but doing it all in Supercollider makes the routing much easier and you can exchange audio with guaranteed minimal latency.
the only thing it takes is writing a fake audio driver and export a function with which you can tick the driver, similar to the callback functions of the real audio backends. the UGen would just need to call this function in the perform routine; actual Server creation (+ options) could be handled asynchronously with unit commands (like VSTPlugin). it could look like this:
// SynthDef to process a stereo audio signal with a seperate Server instance ( SynthDef("test", { arg in, out; Out.ar(out, Server.ar(in: In.ar(in, 2), numOut: 2)); }).add; )
// open Server inside a Synth node ~myServer = Server.new("foo", ~myAddr, ~myOptions, synth: Synth("test"));
would someone find this useful or is this just ridiculous? :-D
How would this address the issue of shared memory / busses between instances that are controlled by the dummy server?
Personally, I already manage different instances of scsynth that don’t need input / output from other instances to take advantage of multiple cores. Where it is unfortunate (now) is that, for each server that needs to access memory, each server needs to allocate it’s own.
Physical Output buses, obviously, get summed together for you when writing to the sound card, but this is different than what happens if two instances of scsynth write to then same virtual bus, and I think is something that will lead to confusion with this approach. Not that it can’t be overcome, but I do think it is an issue. Especially in case where you are writing sound to many buses that need additional processing done (for example, writing output with Amibisonic encoding that needs to be decoded for each instance of scsynth).
Best,
Josh
also, with the exported tick function it wouldn't be too hard to also embed Supercollider in a VST plugin or Pd external.
Christof
Gesendet: Dienstag, 28. Mai 2019 um 02:07 Uhr Von: jamshark70@gmail.com An: sc-dev sc-dev@lists.bham.ac.uk Betreff: Re: [sc-dev] Re: [sc-dev] sc-dev meeting minutes
On Tue, May 28, 2019 at 6:37 AM amindfv@gmail.com wrote:
I see a few routes for the _hypothetical_ unification:
- There's the route of putting code from scsynth "into" supernova
- The reverse (supernova code "into" scsynth)
- Reimplement ParGroups in scsynth without using supernova code at all
My opinion is that this -- reimplementing -- is probably the best way. See below.
- Just start using supernova for everything, and fix as many bugs as needed for everyone to be happy with that route
The major bug here is -- I recall reading in Tim's master's thesis that supernova builds a dependency graph internally for thread allocation. The dependency graph must be updated when groups (parallel or not) are moved.
It happens that I sometimes issue a lot of group-moving commands in quick succession (MixerChannel automatically reorders groups according to channel output and pre-/post-send routing). I've seen in a number of cases where creating and deleting a large number of mixer channels (e.g., a section transition in performance) causes supernova to crash.
I believe there is some stress condition that causes supernova's dependency graph calculation to break.
If this is not fixed, then reusing supernova code is IMO a non-starter.
So far, the dev community has treated this part of supernova as a black box. We'll have to open it up and fix it (and it's difficult to reproduce -- I could crash it 80% of the time with a large, complex live set, but I've failed to create a smaller reproducer), or create a new implementation that simply doesn't use this code.
hjh
sc-dev mailing list
info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mai... archive: http://www.listarc.bham.ac.uk/marchives/sc-dev/ search: http://www.listarc.bham.ac.uk/lists/sc-dev/search/
sc-dev mailing list
info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mai... archive: http://www.listarc.bham.ac.uk/marchives/sc-dev/ search: http://www.listarc.bham.ac.uk/lists/sc-dev/search/
sc-dev mailing list
info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mai... archive: http://www.listarc.bham.ac.uk/marchives/sc-dev/ search: http://www.listarc.bham.ac.uk/lists/sc-dev/search/
wrong list, sorry!
Gesendet: Dienstag, 28. Mai 2019 um 12:13 Uhr Von: "Christof Ressi" christof.ressi@gmx.at An: pd-dev pd-dev@lists.iem.at Betreff: [PD-dev] Fw: Aw: Re: [sc-dev] Re: [sc-dev] sc-dev meeting minutes
~ambiServer = { arg i; Server.embed(Synth("ambi", [\out, ~ambiBus[i]]), addr: ~myAddr[i], ~myOptions, synth: Synth("test")); } ! 4;
d'oh. this should read:
~ambiServer = { arg i; Server.embed(Synth("ambi", [\out, ~ambiBus[i]]), addr: ~myAddr[i], ~myOptions); } ! 4;
and I just want to add that this is really orthogonal to the Supernova approach, it's actually possible to have both.
Christof
Gesendet: Dienstag, 28. Mai 2019 um 12:09 Uhr Von: "Christof Ressi" christof.ressi@gmx.at An: sc-dev@lists.bham.ac.uk Betreff: Aw: Re: [sc-dev] Re: [sc-dev] sc-dev meeting minutes
Where it is unfortunate (now) is that, for each server that needs to access memory, each server needs to allocate it’s own.
yes, embedding Servers doesn't solve this problem either but often we have more than enough RAM anyway.
Physical Output buses, obviously, get summed together for you when writing to the sound card, but this is different than what happens if two instances of scsynth write to then same virtual bus, and I think is something that will lead to confusion with this approach. Not that it can’t be overcome, but I do think it is an issue. Especially in case where you are writing sound to many buses that need additional processing done (for example, writing output with Amibisonic encoding that needs to be decoded for each instance of scsynth).
that's exactly where embedded Servers can help. here's a fictional example:
( // SynthDef to produce a 3rd order ambisonic signal in a seperate Server instance SynthDef("ambi", { arg out; Out.ar(out, Server.ar(numOut: 16)); }).add;
// create 4 ambisonic busses ~ambiBus = { Bus.audio(numChannels: 16) } ! 4;
// create 4 NetAddress ~ambiAddr = { arg i; NetAddr.new("localhost", 57111 + i) } ! 4;
// create some Server options if needed (numInputBusChannels/numOutputBusChannels can be deduced from the Ugen) // ~myOptions = )
// create 4 Servers with the fictional "embed" method: Synth, name, addr, options, clientID // internally, each Server instance writes to its 16 hardware outputs and the Ugen will route this to the given audio bus (= ~ambiBus[i])
~ambiServer = { arg i; Server.embed(Synth("ambi", [\out, ~ambiBus[i]]), addr: ~myAddr[i], ~myOptions, synth: Synth("test")); } ! 4;
// now we can encode each ~ambiBus signal and finally decode all of them.
does this make sense? we actually do stuff like this in Pd with [pd~].
the key point is to realize that Server.ar really is a Unit generator (a block box which consumes/produces audio). Internally, the Ugen would probably buffer the inputs and outputs, so we wouldn't need to implement any audio bus locking. the procedure is: a) lock the buffer b) copy Ugen input to buffer, copy last Server result to Ugen output. c) unlock and signal the embedded Server to process the next block we would get a constant delay of one block but we should never have to wait for the embedded Server.
treating a Server like a UGen means we can freely (and dynamically!) interconnect different Server instances in Supercollider and also nest them arbitrarily. Running each Server on its own thread is a very simple solution which doesn't scale (having too many threads can lead to performance issues), but for many use cases it would be fine I guess.
I can probably hack together a prototype of this in a day or so, maybe I do this in summer :-) Of course, the Supernova approach is vastly superior but also though to implement.
Christof
Gesendet: Dienstag, 28. Mai 2019 um 04:53 Uhr Von: josh@realizedsound.net An: sc-dev@lists.bham.ac.uk Betreff: Re: [sc-dev] Re: [sc-dev] sc-dev meeting minutes
On May 27, 2019, at 7:47 PM, christof.ressi@gmx.at wrote:
I've written a multithreaded virtual modular synthesizer and it is quite easy as long as the thread management is done manually (the user says: please run this node on thread X) but it gets hairy when it should be done automatically (e.g. based on average CPU use).
but there's also a totally different possibility of concurrency: embedding Scsynth instances within itself. In Pd there's an object [pd~] which runs a seperate Pd instance in another process. You can send/receive audio and messages, but you can't immediately share data (although there's the "shmem" external for shared memory). The reason Pd has to run in a seperate process is that it is a fundamentally single-threaded program with lots of global state (but there have been recent efforts to overcome this limitation utilizing thread local storage).
AFAICT, Scsynth on the other hand has very little (mutable) global state: gUnitDefLib, gBufGenLib, gPlugInCmds and those dictionaries can actually be shared across instances (they just have to be made thread-safe). This means it would be possible to run a complete seperate Scsynth instance inside a single Ugen in a dedicated thread. The parent and child instance(s) don't share any data, but they can exchange audio data (and maybe also control data). You can basically achieve the same thing with running several Scsynth processes and connecting them with Jack, but doing it all in Supercollider makes the routing much easier and you can exchange audio with guaranteed minimal latency.
the only thing it takes is writing a fake audio driver and export a function with which you can tick the driver, similar to the callback functions of the real audio backends. the UGen would just need to call this function in the perform routine; actual Server creation (+ options) could be handled asynchronously with unit commands (like VSTPlugin). it could look like this:
// SynthDef to process a stereo audio signal with a seperate Server instance ( SynthDef("test", { arg in, out; Out.ar(out, Server.ar(in: In.ar(in, 2), numOut: 2)); }).add; )
// open Server inside a Synth node ~myServer = Server.new("foo", ~myAddr, ~myOptions, synth: Synth("test"));
would someone find this useful or is this just ridiculous? :-D
How would this address the issue of shared memory / busses between instances that are controlled by the dummy server?
Personally, I already manage different instances of scsynth that don’t need input / output from other instances to take advantage of multiple cores. Where it is unfortunate (now) is that, for each server that needs to access memory, each server needs to allocate it’s own.
Physical Output buses, obviously, get summed together for you when writing to the sound card, but this is different than what happens if two instances of scsynth write to then same virtual bus, and I think is something that will lead to confusion with this approach. Not that it can’t be overcome, but I do think it is an issue. Especially in case where you are writing sound to many buses that need additional processing done (for example, writing output with Amibisonic encoding that needs to be decoded for each instance of scsynth).
Best,
Josh
also, with the exported tick function it wouldn't be too hard to also embed Supercollider in a VST plugin or Pd external.
Christof
Gesendet: Dienstag, 28. Mai 2019 um 02:07 Uhr Von: jamshark70@gmail.com An: sc-dev sc-dev@lists.bham.ac.uk Betreff: Re: [sc-dev] Re: [sc-dev] sc-dev meeting minutes
On Tue, May 28, 2019 at 6:37 AM amindfv@gmail.com wrote:
I see a few routes for the _hypothetical_ unification:
- There's the route of putting code from scsynth "into" supernova
- The reverse (supernova code "into" scsynth)
- Reimplement ParGroups in scsynth without using supernova code at all
My opinion is that this -- reimplementing -- is probably the best way. See below.
- Just start using supernova for everything, and fix as many bugs as needed for everyone to be happy with that route
The major bug here is -- I recall reading in Tim's master's thesis that supernova builds a dependency graph internally for thread allocation. The dependency graph must be updated when groups (parallel or not) are moved.
It happens that I sometimes issue a lot of group-moving commands in quick succession (MixerChannel automatically reorders groups according to channel output and pre-/post-send routing). I've seen in a number of cases where creating and deleting a large number of mixer channels (e.g., a section transition in performance) causes supernova to crash.
I believe there is some stress condition that causes supernova's dependency graph calculation to break.
If this is not fixed, then reusing supernova code is IMO a non-starter.
So far, the dev community has treated this part of supernova as a black box. We'll have to open it up and fix it (and it's difficult to reproduce -- I could crash it 80% of the time with a large, complex live set, but I've failed to create a smaller reproducer), or create a new implementation that simply doesn't use this code.
hjh
sc-dev mailing list
info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mai... archive: http://www.listarc.bham.ac.uk/marchives/sc-dev/ search: http://www.listarc.bham.ac.uk/lists/sc-dev/search/
sc-dev mailing list
info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mai... archive: http://www.listarc.bham.ac.uk/marchives/sc-dev/ search: http://www.listarc.bham.ac.uk/lists/sc-dev/search/
sc-dev mailing list
info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mai... archive: http://www.listarc.bham.ac.uk/marchives/sc-dev/ search: http://www.listarc.bham.ac.uk/lists/sc-dev/search/
Pd-dev mailing list Pd-dev@lists.iem.at https://lists.puredata.info/listinfo/pd-dev