Porting TidalCycles to sclang?

Just some food for thought: Over at the TidalCycles discourse, Alex McLean has published an interesting paper where he mentions the recent effort of porting Tidal to Python and make it in general less Haskel-centric, more language independent:

This gave me (and also him, as he states in the forum) the idea that if Tidal could be ported to Python it could possibly be ported to sclang too… And provide some kind of alternative pattern syntax within sclang itself. Which could be very interesting.

From what I’ve seen since this experiment began, the core foundation is here:

It contains a definition of a Pattern, Timespan, etc… There are a lot of clever functional programming tricks used to reproduce applicatives and other twists from Haskell. It can be useful to understand the nature of patterns and how they are transformed by being passed to other functions, etc…

It is also worthwhile to look at one other file to understand how Tidal messages are scheduled and passed through OSC to SuperDirt. Because SuperDirt is SC-based, it surely can be simplified:

The project is still simple enough. Motivated and experienced programmers can probably start working from that point to slowly reproduce all the basic functionalities of Tidal in any language allowing a slight touch of interactive / functional programming.

2 Likes

TL;DR – parsing Tidal code strings in SC = possible with some effort; performance would have to be tested.

On the syntax side: my own live coding dialect demonstrates that it is possible to implement, in SC code, a parser that generates an abstract syntax tree, and the tree can emit SC code for any objects that are required. It isn’t easy, exactly, but it does work (including recursive syntactical structures).

One thing that I’m less certain of is speed. Using my dialect in Linux, I’ve experienced no performance lags with it, though I’m sure it’s possible to hit limits with exceptionally long expressions. I’ve taken my live coding environment, in Linux, into hour+ sessions and it holds up very well.

Once, though, I used this dialect in a group project with some students, along with home-grown clock sync classes. One student experienced lags in the timing OSC messages when submitting a new pattern string, meaning that code generation was taking longer than I had experienced in Linux :pleading_face: … which would reflect either slower performance in the SC virtual machine overall (which hasn’t been observed in general so I think that’s not it), or maybe regular expressions were slower in Windows at that time (I use regexes to distinguish syntax elements before dispatching to the right parsing class). Still baffled by that one.

hjh

3 Likes

I think they mean to port the concepts using each language’s features as they are.

TL;DR – parsing Tidal code strings in SC = possible with some effort; performance would have to be tested.

This goes first in the message, I had to read it all :slight_smile: Apologies, I’m funny this morning.

Yes, that’s how I understood it too. Though also in Haskell, Tidal adds its own layer of “micro notation” which could be language agnostic or parsed in other languages.

Concerning sclang, I think this mini notation could actually be enriching. I don’t see it as only a kind of live coding dialect but a distinct way of treating and patterning time which leads to very different results than the current Pattern class.

For my own selfish usecase I would be much more interested in seeing some more ports of the innovative ideas for event patterns that Tidal has come up with during the years make it’s way into a classic SC pattern class format. Only because this would make it easy to hook into existing pattern workflows.

I started porting one of the pattern weaving functions and remixing it in SC a few years ago and it was great fun!

3 Likes

Ok ok ok, fixed :laughing:

hjh

Ah, very interesting stuff in there (not only the weave pattern)! I shall give it a try :slight_smile:

My Bacalao quark (which is my personal “playground” for audio live coding) was started two years ago with the idea of recreating a version of the Tidal mini-notation in sclang, which could play nicely with regular SC Patterns. Not because I thought I could do it better (definitely not!), but just as a fun exercise at first. I support quite a bit of the mini-notation, with some of my own extra things. I’m not claiming it’s a good implementation (it was a fun side project that grew), but I’ve been using and extending it happily for the last couple of years.

One of the issues with trying to this is that Tidal is “time based” (i.e. its patterns are pure functions of time), where as SC patterns are event-based, and this very different “world view” caused me quite a bit of difficulty (and some very interesting discussions on this forum!). I wanted to provide the same “time chaining” composition Tidal uses (where the left-most pattern provides the “time structure”, whereas things chained on the right provide values at the corresponding times – this behaviour is distinct from Pchain's behaviour, which just takes the next value in the stream). I made the “mistake” (?) of trying to get my “time chain” operator to work even when not playing the pattern on a clock – that is, for some reason I decided it was important that it should work even when getting an Event stream from the resulting chained pattern and calling “next”, without any clock running… This made my PtimeChain class quite a challenged to implement, and it has numerous limitations. But it still works well for me (knowing the limitations).

There are two ways of using the Bacalao “mini-notation” – one uses an sclang preprocessor to parse a “non-SC” notation, and the other (which I now prefer) uses new Array methods to parse the patterns (both also support shortened forms of Pbind key names, like deg for degree, mn for midinote, etc).

// Bacalao "Tidal-style" mini-notation
deg"0 [2 4] 7@2"

// produces:
Pbind('degree', Ppatlace([ 0, 2, 4, 7 ]), 'dur', Pseq([ 0.25, 0.125, 0.125, 0.5 ]))

// the equivalent in Array notation (not requiring the preprocessor):
[deg: "0 [2 4] 7@2"].pb

To do time-chaining of multiple Pbind keys (structure from left, values from right), you can do:

// With Bacalao preprocessing:
// ( << applies the PtimeChain operator, analogous to <> for Pchain ):
note"0*2 [4 5]!2" << amp"0.5 0.1" << pan"[-0.5 0.5]*2"

// or using Array notation with the tc (PtimeChain) method instead of pb (Pbind)
[note: "0*2 [4 5]!2", amp: "0.5 0.1", pan: "[-0.5 0.5]*2"].tc

You can see many more (possibly out of date) examples on this page:

I’m not sure Bacalao is of much interest to others (there are a lot of extra functions and tools that are probably only of use to me…or were at some point :wink: ), and it’s very “opinionated” – and not modular – but there is quite a bit you can do with it.

Glen.

3 Likes

Oh, this is very interesting! I’ll look into this. You have to forgive my ignorance, I didn’t know all these efforts already existed. (And that even the question of a Tidal port to sclang has been brought up already in October, as I just found out.) :see_no_evil:

Could you point me to these discussions? I’d love to re-read them, but couldn’t find them when searching here in the forum. Or are they to be found in the mailing list archives?

I think the main one was this:

1 Like

If we’re talking about Tidal-inspired live coding pattern languages, then I should mention my own chucklib-livecode.

One thing that I did (perhaps?) a bit differently is to have functions to insert wildcards at time points within the bar, and other functions to replace those wildcards with real values with various characteristics. For example, “insert anchor points @ spaced 2-5 eighth notes apart, then ornament these with up to 6 32nd-note *s (like a burst generator), then replace @ with a predefined pitch sequence and * with intervallic sequences”… and building this up in performance, element by element, produces a concrete sense of development.

It doesn’t do everything – one missing feature is a proper variable – but as a sequencer on steroids, I’ve had a lot of fun with it.

hjh

2 Likes

I’ve been looking into the built-in SuperCollider alternative syntax of method selectors as binary operators (see Syntax Shortcuts | SuperCollider 3.12.2 Help).

I mixed it with some extensions for Nil and Pdef, as well as other stuff I had developed for Živa to simplify pattern syntax, and tried to run it with SuperDirt. It seems to work pretty well. It’s a bit of a hack because I overrode Pdef.doesNotUnderstand, but it works. I’m planning on looking deeper into this. I’m posting it in case others might find it interesting.

Here’s an example of what the resulting syntax looks like:

SuperDirt.start;
SuperDirt.default = ~dirt;

~d1 s: [\bd, \sn].pseq dur: 0.25 room: [0,1,1].pseq;
~d2 s: \superpiano octave: 4 n: [0,2,4] cutoff: sine(0.1, 300, 3000);

sine(0.1, 300, 3000) is an extension of Float that returns an Ndef, so plain SC code works, too. The only extra thing in the following example is the method s: I added to Nil, the rest is vanilla SC:

SuperDirt.start;
SuperDirt.default = ~dirt

~d1 s: Pseq([\bd, \sn], inf) dur: 0.25 room: Pseq([0,1,1], inf);
~d2 s: \superpiano octave: 4 n: [0,2,4] cutoff: Ndef(\sine, {SinOsc.kr(0.1).range(300, 3000)});

Here’s what my extension for Nil and Pdef look like:

+ Nil {
	sound {|snd|
		var code = thisProcess.interpreter.cmdLine.split($ )[0];
		var connection = code.findRegexp("~[a-zA-Z0-9]+")[0][1].asSymbol;
		Pdef(connection, Pbind(\type, \dirt, \s, snd));
		Pdef(connection).play;
		^Pdef(connection);
	}

	s { |snd| ^this.sound(snd) }
}

+ Pdef {
	doesNotUnderstand { |selector, args|
		var pairs;
		if(this.source.isNil) {^nil};
		pairs = this.source.patternpairs;
		args.debug("values");
		pairs = pairs.asDict;
		pairs[selector] = args;
		pairs = pairs.asPairs;
		pairs.debug("pairs");
		this.source = Pbind(*pairs);
	}
}

I guess adding a MiniTidal parser would make it even more Tidal-ish

6 Likes

I’ll check it out, and it looks super cool. As I understand it roughly, the parser seems to be the most challenging part. Was it so?

Other environments already have not one but several parser libraries, the idea of creating DSL in SC was not so explored as it could be yet.

My dialect’s parser has been stable for long enough now that I’ve probably forgotten how difficult it was :laughing: but it’s structured in a way that at least makes sense. Debugging it, though… The other challenge was handling the variety of data cases.

hjh

OT but checkout @shiihs scparco quark which helps build ParserTrees…