Trigger Tidal code with MIDI

Hello,

I would like to trigger and evaluate Tidal code with MIDI encoders and buttons.
For instance, some Tidal code could be saved or "registered", either within a specific file or better with a specific annotation at the beginning of the code, with a particular name and figure, which could then be triggered with a MIDI device, coded within SC.

I've only tried cycseq:
Introducing CycSeq: a cycle sequencer for tidal,
which can almost do what I want.

But, I am also looking for a simpler solution in Atom, where I could be more independent.
Can someone recommend a way or ressources to do this in Atom?

Or maybe, would it be possible to send the code from SC to Tidal, and then back to SC for synth messages ?

3 Likes

Sure this is no problem! What you could do is using this snippet:

capply condpat effectpat = every 
    (fmap (\x -> if x > 0 then 1 else 0) 
    (discretise 1 condpat)) (effectpat)  -- Btw. you can remove discretise

The condpat parameter could be set with an osc control like cF 0 "24" (in my case the 0 is the default value and 24 is the name of my cc-control)

And to switch between two patterns you can use it this way:

d1 $ capply (cF 0 "24") (const $ s "808*4") $ s "bd*4"

If you want to conditionally enable/disable a pattern, you could use it this way:

d1 $ capply (cF 0 "24") (const $ s "808*4") $ s ""

This should give you the opportunity to achieve the behaviour of enabling/disabling patterns conditionally with osc/midi controls.

I hope this is what you are looking for!

Note: I wrote the CycSeq editor basically to encapsulate behavior, which you can also create and reproduce with TidalCycles.

5 Likes

Many thanks @mrreason,

I've got to study your code more in depth, since I'm still a newbie with Tidal.
But your code seems to switch only between 2 patterns.
What I would like is being able to trigger several and numerous prepared patterns with midi or OSC.

For instance, it could ideally look like this, where a MIDI or OSC device could trigger the following codes, by triggering 1, 2 or 3 via MIDI:

1 "intro1" -- Preset number & name
d1 $ sound "bd"

2 "intro2"
d1 $ sound "bd:3"

3 "intro3"
d1 $ sound "bd:4"

Many thanks,
Christophe

1 Like

You can nest several patterns and trigger them conditionally. For example like this:

d1 $ capply (cF 0 "24") (const $ s "808*4") 
   $ capply (cF 0 "25") (const $ s "808(3,8)") 
   $ s "bd*4"

Here you have to keep in mind that only one Pattern can always be active. And the ranking goes from top to bottom. By default s "bd*4" is played. If CC number 24 is pressed, all other patterns are ignored. If I polish all of these to get closer to your example in the last post, then I would do something like this:

capply condpat effectpat = every 
    (fmap (\x -> if x > 0 then 1 else 0) 
    (discretise 1 condpat)) (effectpat) -- Btw. you can remove discretise

capply' name effectpat = capply (cF 0 name) (const $ effectpat)

intro1 = capply' "24" (s "808*4") 
intro2 = capply' "25" (s "808(3,8)") 
intro3 = s "bd*4"

d1 $ intro1 $ intro2 $ intro3

The osc control of patterns must already be done within the TidalCycles code. This is at least the approximation of what I understand, what you want to achieve.

4 Likes

@mrreason thanks for the info. Just logged in to try and find out/ask how to achieve this.

Do you think you could provide an explanation of what the condpat declaration is doing? I have been using a midi controller without this declaration since starting Tidal a few weeks ago (admittedly I haven't been using buttons, just faders and pots thus far).

Also I had a brief play with an example gleaned from a youtube video (can't remember the title off hand) using a button to apply a lpf however the responsiveness was latent by a second or so. Are you aware of settings that could negate latency. I am not logged into my Tidal setup atm to be able to try the above example.

One other thing is anyone aware of a way to send a message to the controller to light the active button or even better have the button pulse to the cps. I could probably work this out for myself but if someone already has the knowledge and an example would be much appreciated.

Again thanks for the info.

Thanks @mrreason,

Your code works well with different noteOns or buttons even if you mention CC.
But, I still have issues transposing your code with a single MIDI encoder, to select between dozens of preset variations.

Below a pratical example (adapted from the documentation) with the MIDI SC code, which gets the value of 4 encoders (CC id between 0 and 3) and sends the corresponding preset numbers, from 0 to 999..., for e.g. 4 tracks (d1 to d4) to select the corresponding indexed code in Tidal.
But still I do not succeed in Tidal to select between several preset variation by changing the preset value of the 1st encoder (CC 0) for d1.

Another way would be to send directly the registered preset name via OSC from SC.

(
var on, off, cc;
var osc;

osc = NetAddr.new("127.0.0.1", 6010);

MIDIClient.init;
MIDIIn.connectAll;

on = MIDIFunc.noteOn({ |val, num, chan, src|
osc.sendMsg("/ctrl", num.asString, val/127);
});

off = MIDIFunc.noteOff({ |val, num, chan, src|
osc.sendMsg("/ctrl", num.asString, 0);
});

~tidalPresets = [0, 0, 0, 0];

cc = MIDIFunc.cc({ |val, num, chan, src|
case
{num.inclusivelyBetween(0,3) and:{val==1}} {~tidalPresets[num] = ~tidalPresets[num]+1}
{num.inclusivelyBetween(0,3) and:{val==127}} {~tidalPresets[num] = (~tidalPresets[num]-1).max(0)};
osc.sendMsg("/ctrl", num.asString, ~tidalPresets[num] /*val/127*/); ~tidalPresets[num].postln;
});

if (~stopMidiToOsc != nil, {
~stopMidiToOsc.value;
});

~stopMidiToOsc = {
on.free;
off.free;
cc.free;
};
)
1 Like

Glad to hear that you are making progress!

In general it does not matter what you use. In the end you are sending OSC messages to TidalCycles like

osc.sendMsg("/ctrl", name_of_control, value);

So you could also send them to TidalCycles under any name with any other program/app (I still use TouchOSC for example).

I believe this is also the only right way. TidalCycles basically listens to the controls for the name you give them. These are basically just patterns with one value, except cP with which you can send a pattern with mini-notation. But cP is not suitable for your problem, because you want to exchange the content of a whole 'track'.

For this reason it is difficult for me to say what exactly you should do, because it depends on your personal needs. My MIDI setup is not as complex as yours, I guess.

1 Like

I'm not sure I understand you here, because condpat is just the parameter name for the self-written function capply. As a parameter it expects an OSC control as described at https://tidalcycles.org/index.php/Controller_Input. May this answers your question?

Now I am not aware of any latencies that my script should cause. I can only imagine that this might have something to do with the transport latency of osc messages. Depending on how you use the controller input, the latency can be caused by the pattern itself. But the value changes should always occur immediately.

I believe the simplest approach would be using a MIDI-clock like https://tidalcycles.org/index.php/MIDI_Clock
A more direct approach is to use the clock messages coming from TidalCycles to evaluate them: SuperDirt/scripts/sync-to-tidal-02.scd at develop · musikinformatik/SuperDirt · GitHub -> this would be helpful if you want to set a led from an Arduino with a serial connection.

1 Like

Sorry I have issues expressing in details what I have in mind.
I first just want to trigger different codes according to to the value number of CC 0.
But I do not how to build the function with capply, with the diiferent preset number corresponding to the code:

d1 $ capply (cF 0 "0") (const $ s "808*4") -- if value of CC 0 is 0
$ capply (cF 0 "0") (const $ s "808(3,8)") -- if value of CC 0 is 1
$ capply (cF 0 "0") (const $ s "808(4,8)") -- if value of CC 0 is 2
$ capply (cF 0 "0") (const $ s "808(5,8)") -- if value of CC 0 is 3
$ capply (cF 0 "0") (const $ s "808(8,8)") -- if value of CC 0 is 4...

Hope it is more clear.
Thanks,
Christophe

No problem! If I understand this correctly, then you want to replace the patterns by the values of an OSC message. This actually requires a different approach. I have thought about it again and I think the cleanest solution could look like this:

import qualified Data.Vector as V

capply'' condpat pvector =  
      condpat >>= \ i -> pvector V.! (mod i (V.length pvector))

intro1 = s "bd*4" 
intro2 = s "cp*4"
intro3 = s "808*4"
intro4 = s "hh*4"

let pvector = V.fromList [intro1, intro2, intro3, intro4]

d1 $ capply'' (cI 0 "test") pvector 

Now you can switch between the four patterns with osc-messages. In SuperCollider this could look like this:

o = NetAddr.new("127.0.0.1", 6010);

o.sendMsg("/ctrl", "test", 6);

And now you can use the MIDI functions again to trigger OSC messages via MIDI.
Note that a vector starts at index 0. If the vector contains four elements (or four patterns), then the value 4 or 8 would refer to the element with index 0 (I calculate modulo over the length of the vector to avoid wrong access).

1 Like

@mrreason thanks for your assistance. I have just started playing around with this and maybe my question is answered in the above post/s but being super new to Tidal am having difficulty understanding.

I have managed to get this working almost as i require:

capply condpat effectpat = every
(fmap (\x -> if x > 0 then 1 else 0)
(discretise 1 condpat)) (effectpat)

d1 $ capply (cF 0 "36") (const $ s "sn*4")
$ capply (cF 0 "32") (const $ s "bd(3,8)")
$ capply (cF 0 "28") (const $ s "hh(4,8)")
$ capply (cF 0 "24") (const $ s "bin(5,8)")
$ s "ab(8,8)"

Is there a way to have this toggle? The patterns change as long as a button is pressed, returning to ab(8,8) on release. I want to be able to switch between patterns and have the chosen pattern continue playing after the button is released...

Hello @TimPuk, with the solutions I presented here, TidalCycles is also used in a way that TidalCycles is not normally used xD

To realize such a behavior like the trigger of TidalCode with MIDI, I also wrote Haskell code (so this has nothing to do with the direct syntax of TidalCycles). Haskell is the language TidalCycles is based on.

The first approach of my capply function and also what I had in mind was to switch between two patterns. The capply'' function I presented today is really good for switching between many patterns.
I just didn't have this requirement myself yet, but this should be the solution. Feel free to give it a try and tell us how you get along with it. I would be very interested.

But as for your last question about the toggle. I think this is a completely different problem. Sorry, I must have overlooked that. Unfortunately, this is a problem I have to think about first, because this is something different. So far my solution approaches are not about toggling, but replacing.

1 Like

@thgrund :pray: That does work... no idea how though. Fear this may be beyond me at the moment. Is a shame Tidal doesn't have a way to achieve this inline.

Sorry for my lack of knowledge but what did you mean by "And now you can use the MIDI functions again to trigger OSC messages via MIDI" via SuperCollider or within Tidal?

@mrreason Sorry for being a pain but this is so close to working. I for the life of me can't figure out how to map a button to affect the o.sendMsg value, either via Tidal or SuperCollider. I appolagise if this has been shown somewhere above but I have been trying over the past hour various things to no avail.

Everything works perfectly by manually evaluating o.sendMsg("/ctrl", "test", 0); / o.sendMsg("/ctrl", "test", 1); in SuperCollider... Now just need evaluation to occur at the press of a button.

I really can't tell you if this belongs directly implemented in TidalCycles. In any case, I think the function should be polished up if you want to make it accessible for everyone. What I can do at this point is to create an issue on Github. Then the community can discuss it there in detail.

But let's help you with the controller input. Basically there is a tutorial on the official TidalCycles wiki. You can find it here: https://tidalcycles.org/index.php/Controller_Input

Nevertheless I can write a minimal working example for cc-messages for using my capply'''function above:

The SuperCollider side:

// Evaluate the block below to start the mapping MIDI -> OSC.
(
var cc;
var osc;

osc = NetAddr.new("127.0.0.1", 6010);

MIDIClient.init;
MIDIIn.connectAll;

cc = MIDIFunc.cc({ |val, num, chan, src|
    // This is using for debugging, you can remove this line
    ["CC-Num: " ++ num, "CC-Value:" ++ val].postln;
	osc.sendMsg("/ctrl", num.asString, val);
});

if (~stopMidiToOsc != nil, {
	~stopMidiToOsc.value;
});

~stopMidiToOsc = {
	cc.free;
};
)

// Evaluate the line below to stop it.
~stopMidiToOsc.value;

With the code above SuperCollider listens for all incoming MIDI-CC messages and sends the CC numbers as the name and the value as value.
If you try to receive all cc-values of the cc-num 3 and use it to control which pattern is playing, this could look like this in TidalCycles (note the 0 in the function cI is simply the default value and you could change it too):

d1 $ capply'' (cI 0 "3") pvector 

Hope this will help!

1 Like

@mrreason not saying it should be a part of Tidal, I understand that I am trying to adapt it to my specific needs, but if it is something that could be raised for potential future integration would be great. I am a little surprised it is not something that has come up before (there are a number of posts here and on TopLap alluding to it so maybe I am missing a more in-depth discussion somewhere...)

I already have my controller hooked up with SC with a similar script (via the wiki article), which has been working great for the compositions I have been working on. The confusion was the correct syntax, either in Tidal or SC, to have an incoming MIDI-CC alter the cI value in your script. I will have a fresh hack at it tomorrow and see if I can't figure it out.

Thanks so much for taking the time to respond to my questions!

1 Like

Thanks so much @mrreason,

Your example works great. Obviously, I still have questions...

  1. To understand better your code and syntax, do you know a particular Haskell ressource that explains capply, especially '' ?

  2. When I try to select and evaluate all the code at once, I have parse errors:
    -> possibly incorrect indentation or mismatched brackets
    -> error: expecting a single import declaration
    Do you know how to avoid to evaluate the different parts of the code in several times?

  3. By playing with triggering different code structures, I've realized it would be great to be able to have different kinds of transitions or not. For instance, there could be also an additional list, that chooses e.g. between these 3 different kinds of transitions:
    [d1 $, xfadeIn 1 8, xfadeIn 1 16]
    This way, by concatenating the 2 lists (transition & pattern), associated with another MIDI control, you could easily switch between different kinds of transitions and codes.
    I am not familiar enough with Haskell to know if it would be possible or how to concatenate these 2 lists...

Thanks,
Christophe

Glad to hear! So let's try to answer your questions:

I wrote three different capply functions, named capply, capply'and capply''. These are just valid function names. It would be totally fine to name the capply'' function thisismyawesomefunction or something like that...But I guess you should get the idea.

You can copy the functions in your BootTidal.hs or in a separate file. You could load a separate file manually with

:script mytidalfuncs.tidal

But make sure that you wrap multiple lines with :{ and }: . I already saw an example of this in the forum:

I think I now understand the requirements for a one-size-fits-all solution.
This could be a larger project and I would prefer to exclude this to an issue on Github to discuss it there. Let me try to explain this. What you could do with the custom capply function is to switch between patterns conditionally with a pattern like:

d1 $ capply'' "<0 1 2 3>" pvector 

This works totally fine. And the osc control messages are basically patterns. It is not possible (or I don't know it yet) to control (let's call it) IO function like transitions (like xfade) or d1, d2 with patterns. What you could do for example is switching the output bus with a function like orbit:

-- Some kind of switching between d1 and d2
d1 $ s "808*4" # orbit "<0 1>" 

I see two possible solutions here:

  1. There should be the possibility to control transitions with patterns and use them similar to the function orbit. Then my solution for selecting patterns with patterns would also work on transitions.
  2. Or we need a way to trigger IO functions through OSC. But I think that we then leave the pattern context and I'm not sure if this is intended.

I can imagine that you are more flexible with this second approach. This would allow to trigger the IO-functions and the attached patterns independently from the TidalCycles clock. I think of it as a variant of my CycSeq approach, but then built into TidalCycles. In CycSeq I have code in a separate window and I activate a window with the code it contains. This is basically nothing else than having an osc controller that's wraps TidalCycles code which you could trigger from outside the IO functions.
But you see, that this contains a more in depth discussion.

[Edit:] I actually see that there is an issue for trigger IO functions with pattern on GitHub: IO patterns · Issue #483 · tidalcycles/Tidal · GitHub Maybe this is a feasible approach. But as far as I see, this is using an IO-function like reading a file to interpret this as a pattern. But maybe this works for triggering a transition like xfade inside d1...

But I would also be really happy if we could find a solution.
In my case, I can imagine that this will give me an easier environment in TidalCycles to do live sampling/live looping.

2 Likes

I created the ticket on Github. You can see it here and discuss it under https://github.com/tidalcycles/Tidal/issues/716

1 Like

@mrreason @Xon

I feel like I am pushing my luck with this thread but I still for the life of me can't figure out how to get a cF cc input tied to the cI value in the above script. The script works wonderfully when manually changing the cI value either in Tidal or SC... I have tried all manner of capply cF and const configurations to apply a cI value change to no avail. Closest I have come is having a change affected upon a button being pressed only having it return to the default value upon release (which can be achieved without the use of the script provided by the @mrreason)

It may have something to do with my controller (Xone K2) which does not have toggle buttons, ie. buttons send a note on when pressed and a note off when depressed.

At this stage I am not so concerned with transitions or switching between Tidal channels as mentioned above and in the Github issue. The ability to switch between patterns within a given channel would be more than enough for me at the moment.

It may very well be quite obvious how to achieve this via the wealth of information already offered up in this thread but I, for some reason, can't figure it.

Thanks for all the info thus far. If someone could offer up this last piece to the puzzle I would be immensely greatful. :pray: