Podcast with James McCartney

I do feel a little like CSound and SuperCollider might be false equivalencies. People use each for the same thing, but SuperCollider is a programming language and development environment PLUS a graph-based synthesis engine. CSound is a graph-based synthesis engine - CSound has a lot less surface area to manage: it just does a lot less, which makes it easier to maintain and more flexible to use. This also makes it a lot less unique - there are a lot of competitors to CSound, but very few for SuperCollider (lots of things can cover one PART of what SC does, but few/none provide it all in one place).

I suspect if SuperCollider waned in the future, it would be because of a significant competitor and not because it rots away. It was forward thinking as a language, but is still now decades behind the state of the art in terms of programming languages design and tooling. The synthesis graph is great, but at this point audio graph processing is a pretty solved problem with decades of examples to draw from - building a good/better one is more about someone with the time to do it. Tim B built supernova from scratch in, what, a couple years - partly I think b/c grad school gave him time, and he had enough of a programming background to actually finish the project.

Build a nice new synthesis engine, write a wrapper for scx plugins, and a front-end language based on something modern that compiles in LLVM - and spend your last year of grad school writing cool abstractions for making music? This starts to sound REALLY appealing as a platform. I suppose I’d hope that, if someone chooses to do this, they would use SuperCollider as a basis and pull it forward into the future?

3 Likes

All very good points, Scott !

And imagine something would happen, how long would it take that such an open source project would achieve the same amount of maturity, widespread applicability, and international support that SC has? I suppose many years, and what to do meanwhile? If the artistic side – however you name it: musical practice, composition, sound art etc. – is your main thing, I’d rather stick to the proven environment, at least for a transition time.
I think that often in this kind of discussion the focus is too much on possibly out-dated architecture. Thinking about what’s not possible now is surely propelling innovation but, paradoxically, also masking possibilities. More important, from my point of view, what you can already do with this amazing software, provided spending enough time, effort and reflexion.

1 Like

… which might explain the interest (which seems to be ratcheting up lately) in non-sclang clients: Lucas’ port of the SC class library to Python, recent discussion on the mailing list of a Racket client in development, past work on Scala, jcollider, supercolliderjs… There’s “Rethinking the SuperCollider Client” (2010), which requires registration and clicking through some hoops to access, and includes this chart:

This kind of work could keep the SC UGen library around for another couple of generations.

With that said, I’m unlikely to use them :laughing: – I’m 50 this year, and I have to ask myself, how long would it take to become fluent in another language and port MixerChannel, Voicer, chucklib and chucklib-livecode, at minimum? I’d have to guess upward of two years, not making much music and not developing new features. Optimistically that’s, what, 10% of my remaining working life… nah. Better to use that time for more shows.

If I were 25 and chafing against sclang’s issues, it would be a different calculation – a/ that it’s even possible to attach other languages to scsynth and b/ that people are doing it are both very good things, to attract and keep younger users.

hjh

2 Likes

Concerning the reusability of certain scripts, that’s a bit different: things might always change in details. In general you can’t expect the same audio code running decades from now with the exact same results. The sustainability of live-electronics is limited in that sense. That is proven by many complaints on the SC mailing list in the past, slight implementational changes as well as operating system specifics can always totally change the sounding results.

My understanding is that the CSound community is a lot more worried about this than the SC community. In SC development I feel there’s a sense of trying to make good tradeoffs between backwards compatibility and other concerns; in CSound I think there’s a lot more of a concern about “will my composition be playable as-is in 50-100 years?” the way you’d worry about sheet music being readable.

One way this manifests, for example, is that we occasionally make fixes or other changes to existing UGens; in CSound they typically won’t alter an opcode, instead keeping the “foo” opcode but adding a “foo1” opcode with the fix/alteration.

As I argued in my post above, no big alternatives have shown up since 2003 (ChucK).

Although it’s definitely not knocking the other environments off their thrones, I’d make the case that Extempore is an exciting, modern environment along the lines of the others, which dates from around 2012. It’s got a lot of neat power tools, including the full-graph compilation James McCartney mentions in the podcast. As far as I can tell it’s not used by a huge number of people, but I’ve seen awesome musically complex gigs played with it.

Emphasizing this: just saw this on the CSound homepage: “One of the main principles in Csound development is to guarantee backwards compatibility. You can still render a Csound source file from 1986 on the latest Csound release, and you should be able to render a file written today with the latest Csound in 2036.”

That I call a statement, chapeau :slight_smile:

As exciting as Extempore may sound on paper, or in demos, it’s not a terribly well maintained project. Hardly any updates in a year. Combined with the complex tooling and LLVM frozen to a 2016 version, it’s not really useable by outsiders. If their private website were to go down, it would also become unbuildable, as the stuff on github is not self-contained; the (custom patched?) LLVM source automatically gets downloaded during build from the extempore private website. Also, compilation is like 5-6 longer than for SC. And I’m not sure if it’s just the size of their libraries or ancient LLVM but they also load much more slowly than what Kronos manages to pull off, roughly based on the same backend.

Kinda sad because simple audio-rate function compilation works decently fast in Extempore, perhaps even faster than Faust, e.g. example from their (online-only) doc that does still work

(bind-func dsp
  (lambda (in:SAMPLE time:i64 chan:i64 data:SAMPLE*)
    (* .1 (sin (/ (* 2.0 3.1415               ;; 2pi(ish)
                     440.0                    ;; frequency (Hz)
                     (i64tof (% time 44100))) ;; time mod samplerate
                  44100.0)))))

(dsp:set! dsp)

;; functions are hot-swappable, to cut it off, do e.g.
(bind-func dsp:DSP
  (lambda (in time chan dat) 0.0)) ;; cuts snd

If anyone is curious, it doesn’t work to rebind the dsp itself; if you do (dsp:set! dsp) again you get this message:

You can only set the DSP callback once, but you
can re-define that function as often as you like

The explicit typing is a bit annoying, but by and large not strictly necessary, as you can see from the 2nd example. One thing that’s not so cool is that there are single and multi-channel versions of the same things, e.g. oscillators, an seemingly these need to be chosen explicitly e.g. osc_c vs. osc_mc_c. (The final _c is for closure.) This is probably due to types not being polymorphic like in some other languages in this space. You also need to cast numbers explicitly e.g. from int to float, as this this basic frequency sweeper, lifted as-is from the documentation:

(bind-func dsp:DSP
  (let ((oscil (osc_mc_c 0.0))
        (duration (* SRf 1.0))      ;; samplerate * 1.0 seconds
        (range (/ 440.0 duration))) ;; rise up to 440.0 hz
    (lambda (in time chan dat)
      ;; explicit conversion required to coerce time (i64) into a float
      (oscil chan 0.3 (* range (% (i64tof time) duration))))))

And I could be wrong on this, but basing a live coding thing on a language (Scheme) that apparently doesn’t support optional parameters except in a left-to-right fashion as closures such as

(bind-func osc_c
  (lambda (phase)
    (lambda (amp freq)
      (let ((inc:SAMPLE (* STWOPI (/ freq SR))))
        (set! phase (+ phase inc))
        (if (> phase SPI) (set! phase (- phase STWOPI)))
        (* amp (_sin phase))))))

seem like a pretty bad idea. I mean even XLisp used by Nyquist has optional & named parameters.

Actually, there is some work behind the scenes to make the guts of their JIT compiler less dependent on a given version of LLVM, so upgradable. But this is being done by an outside contributor, so it’s in the eternal PR review process that unfortunately the git flow model “facilitates”.

I’d have to check what JIT Kronos uses, but one of the obscure thing about LLVM is that it has multiple incompatilble JITs, or at least JIT APIs. Extempore uses the older MCJIT one, not the latest, modular ORC. And MCJIT was actually the 2nd gen LLVM JIT. There was an even older one in LLVM <3.5, which didn’t even get a real name.

One other thing I could not yet figure out how to do in Extempore is how to get an equivalent of Ndef \fadeTime, i.e. at least custom fade times if not also cross-fade custom envelopes (that Ndef also supports) when replacing a source with bind-func.

1 Like

Thanks for sharing this podcast (yes, I’m late to the party) :slight_smile: From minute 17 onwards, JM talks about a Mac program he wrote to create samples based on various synthesis techniques for the Ensoniq Mirage sampling keyboard. For those interested, here’s a review of this program called “Synfonix” on pages 12 & 13 in issue March/1988 of “Transoniq Hacker – The Independent News Magazine for Ensoniq Users”. Enjoy!

3 Likes

Although I think that discussions about tools tends to go to the unproductive olympic field of “my way is better because I can do more or do it faster”, I often like to read about comparisons between SC and other computer music environments. The best thing in theses discussion is to see the unique possibilities open by the design goals constraints.

After doing some archeology I found what seem to be JMC discussing about ChucK:

SuperCollider

I am the author of SuperCollider which is also a programming language for real time audio synthesis. A number of misstatements about SuperCollider have been made in the papers on ChucK.

SuperCollider is no less concurrent than ChucK, and is no less “strongly-timed” than ChucK. Every thread in SuperCollider carries with it a time reference in a certain clock base. Clocks can run at different tempi. ChucK doesn’t handle tempo.

// fork two concurrent threads in SuperCollider: 
fork { 4.do { "A".postln; 0.2.wait } }; 
fork { 8.do { "B".postln; 0.1.wait } };

Indeed, a number of claims about ChucK seem to be based on its limitations. Chuck runs its threads inside the audio calculation loop which means that any long large calculations will stall the audio and cause a glitch. SuperCollider is based on a client/server model which decouples event generation from audio synthesis.

Chuck lacks higher order or anonymous functions, whereas SuperCollider has closures, map/filter/fold, currying, list comprehensions, tail call optmization, etc.

By James McCartney at Thu, 2006-04-27 04:27

Syntax

To be honest, when I looked at Supercolider I coun’t really get into it due to it’s unfamiliar syntax. It’s been a while since I looked, but I was wondering what you based it on.

By kruhft at Thu, 2006-04-27 06:13

syntax similar to Ruby

It isn’t that unusual, it is very similar to Ruby. SuperCollider wasn’t based on Ruby, it was based on Smalltalk, but Yukihiro Matsumoto and I coincidentally happened to have made many of the same choices in syntax, such that I was subsequently able to borrow some of Ruby’s syntax features that I didn’t already have. And since both Ruby and SuperCollider are both in the Smalltalk family, they also share many of the same method names.

By James McCartney at Thu, 2006-04-27 06:29

After reading this I got extremely curious to see audio-code examples explaining the what can/can’t (or is more/less laborious) be done in SuperCollider in comparison with ChucK and vice-versa.

1 Like

Well, if we’re going to talk syntax, I don’t like the Lisp style languages with the parentheses (s-expressions) that keep piling up, although it’s pretty easy to have an editor auto-generate those. Also possible to write a pre-parser that gets rid of them. Even been done in the audio realm with SAL in Nyquist, but they’ve added quite a few C-like explicit keywords like return in SAL which are kinda annoying in themselves. Never mind the Pascal-borrowed begin and end that they’ve put in SAL. Those are a super annoying alternative to s-expressions when it comes to blocks.

1 Like

I was a little confused by this table. How can SuperCollider be OOP, and at the same time be a “functional” language? Perhaps not “purely functional”, as OOP is a paradigm that brings with it side effects hidden by the nature of OOP design, and, consequently, everything a functional program tries to avoid. With parallel processes, for example, problems like “data races” in OOP are often mentioned as a limitation. (It’s not a confrontational comment at all, I just don’t understand what the table actually means.)

sclang isn’t pure-functional by design, but there are definitely a lot of functional concepts built into it very deeply. After all, it’s a language that - at least semantically - doesn’t inherently even have control flow at all - it only implements it via first class functions and it’s type system. But it’s very mutation heavy (iirc even sclang’s ability to make things immutable doesn’t REALLY work the way one would want, if the intention is to guarantee more pure functional operation).

Function composition is a pretty core concept in the language though - without it basically the entire pattern and event systems disappear.

1 Like

Yes, I see that. lazy evaluation with list comprehension is another good example, and I enjoy using it. Pass a function as an argument (even if as a string to be interpreted later). There is some beauty in hybridisms, and sc does it well.

You can write supercollider(code, in(a, totally), functional(style), if(you(want))), I’m always a bit surprised i’ve never seen anyone do this - I figured there would be at least a few hardcore functional folks out there who would prefer this.

I think people tried to stretch it a bit. I recall a quark exploring FP concepts for example.

I heard one programmer say that functional programming isn’t a syntax; it’s a style. If you write your functions/methods to return new objects always instead of mutating, then you could write functional code in just about any language (though you’d lack the array optimizations that specifically functional languages have developed).

SC’s class library is certainly not rigorously functional in this way, sure.

hjh

1 Like

Yes, but even if your class method returns a new object without side effects, it’s like a function with no inputs, which is something strange in mathematical terms. It’s safer to have no side effects, but still, it’s a different paradigm.

I think it’s pretty straightforward to define a method or function with inputs, which returns a new object instead of mutating.

hjh

1 Like

Functions with no inputs that always return the same thing is equivalent to coping an immutable variable. Since Supercollider doesn’t really have a concept of immutability, wrapping it in a function achieves a similar result.

In other words: f() = x might as well be just x mathematically - as it is constant -, but since there is no immutability in supercollider, having a function (which is itself immutable) return a mutable object is about as close as we can.

Also, in supercollider everything is callable: 2.value(...) just returns 2, which is a common way to make an interpreter. I think some array languages have a similar feature, they might call it an identity or constant function?