SuperCollider 4: First Thoughts

Hey @prko

Yes, this kind of idea would be great in a Jupyter notebook. But terminals don’t do this kind of thing, that’s why there are notebooks nowadays!

1 Like

There are some interesting possibilites when thinking of wasm as a general platform not just for scsynth/sclang but also for the IDE.

If one could dream, I think something like egui could make a nice basis for an IDE 2.0 (see demo egui – An immediate mode GUI written in Rust) or replacing QtGUI.
This would allow to really think of an IDE more as a desk where multiple snippets and windows can be arranged next to each other, multiple people can work on the same desk, integrating visual representations, sources and feedback - video playback in IDE within a document? No problem.

It would be a new way of thinking an IDE and not just in such a boring manner like vscode does - and it would immediately run locally or distributed in the browser in similar performances (this is not electron, it is native machine code!). Combind with the LSP it would create something really refreshing and creative :slight_smile:

4 Likes

Maybe something like SmallTalk? There is a history around those good ideas.

3 Likes

@dscheiba do you have the skills and time to do it? I’d love to see something when there is a prototype.

It may be pragmatic to have a pop-up window that works as a post-window from the current mouse position or from the block of code being evaluated, appear for a while when evaluating a block of code, and then disappear when the mouse pointer is moved, something is typed, or the cursor is moved.

A converter that converts the current output value of a Ugen to a float or integer when a trigger is received might be practical? Then we could be more flexible:

This would mean re-integrating the control layer into the DSP loop (where the whole design of SC3 was to get the control layer fully out of the DSP loop – which gives us benefits such as glitch-free buffer loading, which AFAIK Pd doesn’t have).

SC4 would be an opportunity for a radical redesign – a really radical redesign would be the only way to implement this suggestion.

hjh

1 Like

The synchronous nature of Pd (that is alternating DSP and message passing in a single thread) does not prevent asynchronous commands! There is just no good API for externals - yet :slight_smile:: New asynchronous task API by Spacechild1 · Pull Request #1357 · pure-data/pure-data · GitHub

For [vstplugin~] I currently use my own task queue to enable asynchronous (glitch-free) plugin and preset loading.

The general problem is rather that users may perform arbitrary operations on the control layer, so there is no upper bound for CPU usage. Pd uses a ringbuffer to accomodate CPU spikes, at the cost of additional latency, but this only helps up to a certain extend. For example, nobody is stopping you from iterating over a 10 million element array, in which case you either break up the loop and distribute it over multiple DSP ticks – or you accept that you will get a drop out.

Often wondered if you could use Julia to write synths and register them with the server on the fly. Julia has an excellent jit producing highly efficient code that often rivals c.

1 Like

This was JMc’s original idea for SC3 (“sc3d5” which was never completed), but it didn’t get off the ground because JIT technology wasn’t there yet.

hjh

1 Like

Is the server built to accept new ugens while running? (I know nothing about this side of sc). Because getting a c pointer out of Julia is trivial

function foo(x::Int, y::Int)
   return x + y
end

@cfunction(foo, Int, (Int, Int))

No because they’re loaded as dynamic libraries. It could be written to monitor for new libraries, or alternatively this is something a custom ugen could do (similar to what the VST3 plugin and Faust plugins do).

For Julia to be used for this you’d need some way for SuperCollider to call your Julia code. This would either require compiling it into a dynamic library with a C interface that matches what SuperCollider expects, or building some bridge code to Julia.

Doing this in any language that has a Garbage Collector is always going to be challenging. I’m not saying it can’t be done - just that it would be difficult and error prone. Sharing memory between GC languages and non-GC languages is difficult (particularly if the memory has to be used by a RT thread).

A while ago I attempted a rewrite of the SC-server in Rust and abandoned it because Rust isn’t really the right language for something like that. But in the process I did do some thinking (and work) on what an ideal SuperCollider 4 server would be in imho:

  • Backwards Compatible - While it would enhance SC3, all SC3 plugins should just work, and all SC3 OSC commands should just work.
  • Modular - Yes the OSC server is cool, but you should also be able to embed SuperCollider into other software ranging from VSTs, to other languages. This would also make it easier to maintain/change.
  • Multi-core - Not only should I be able to designate any group as supporting concurrent synths, but I should also be able to designate groups that can be run in parallel (that would allow buses to be processed simultaneously).
  • single sample graphs - Ability to designate either a synthdef, or part of a synthdef, as single sample - this would only work with UGens that supported single sample. And obviously feedback would be baked into that.
  • oversampled graphs - signals coming into those graphs would be upsampled, signals coming out would down-sampled and everything inside the graph would run at the higher rate
  • MIDI in the server - new ugens that support MIDI in and out.
  • Proxy Ugens - some concept of UGens that can change dynamically, so that their inputs/outputs can change. This would allow UGens to more flexibly support things like VSTs, JIT code, etc.

What is missing from that list. Or what on that list is wrong.

The reason I’m asking is that I’m going to make another attempt to rewrite SC-server, this time in Zig. I make zero promises, and this will probably not result in anything usable, but I figured why not pretend that it could… :slightly_smiling_face:

3 Likes

FWIW, it’s pretty easy to sidestep this limitation. UGens can open-endedly route processing to any arbitrary function when they are instantiated, and can read from any number of inputs (this is determined by sclang, not the UGen). So, it’s easy to have a “wrapper” UGen that uses one of it’s arguments to select from any number of processing functions, even ones that are loaded dynamically at runtime. This is more or less what @Spacechild1’s VSTPlugin is doing, but it could equally be done with e.g. Julia meta-UGen.

1 Like

This might be implicit but: 64 bit floating point samples, so BufRd et al just work

That’s a good one. I’m not sure if that’s compatible with backwards compatibility, and there obviously performance implications there as well.

Yes I just realized those might be at odds… What do you imagine the performance hit being?

Having worked quite hard on a stopgap solution I’m pretty convinced that every ugen should at least have the option of running at high resolution for the case when it’s used to index into a big buffer

Actually, isn’t that a requirement for a 64 bit integer type? Though I suppose there are situations where you want 64 bit floats, though I can’t think of any.

No, I think it requires 64 bit floats because e.g. suppose the playback rate is 0.5… BufRd will interpolate between integer indices

Fair point. It’s been a long time since I’ve used BufRead directly :slightly_smiling_face: