Troubleshooting custom OSC

Hi there! I'm trying to Tidalcycles connected to MaxMSP so I can send some crazy patterns but I'm getting a bit stuck following the documentation, which is located here -

http://tidalcycles.org/docs/configuration/MIDIOSC/osc

I'm just using the suggested setup code to get things working, nothing complicated but I''ve been having the following problem. If anyone can suggest anything to get this working, i'll be forever grateful. Thanks very much.

let target =
      Target {oName = "visualiser",   -- A friendly name for the target (only used in error messages)
              oAddress = "localhost", -- The target's network address, normally "localhost"
              oPort = 5050,           -- The network port the target is listening on
              oLatency = 0.2,         -- Additional delay, to smooth out network jitter/get things in sync
              oSchedule = Live,       -- The scheduling method - see below
              oWindow = Nothing,      -- Not yet used
              oHandshake = False,     -- SuperDirt specific
              oBusPort = Nothing      -- Also SuperDirt specific
             }
    oscplay = OSC "/play" $ ArgList [("s", Nothing),
                                     ("vowel", Just $ VS "a"),
                                     ("pan", Just $ VF 0.5),
                                     ("volume", Just $ VF 1),
                                     ("cut", Just $ VI 1),
                                     ("intensity", Just $ VI 0)
                                   ]
    intensity = pF "intensity"
    oscmap = [(target, [oscplay])]


stream <- startStream defaultConfig oscmap

let x1 = streamReplace stream 1
    x2 = streamReplace stream 2
    x3 = streamReplace stream 3
    x4 = streamReplace stream 4

When I call:

stream <- startStream defaultConfig oscmap

I get the following error:

t> [TidalCycles version 1.7.8]
Installed in /Users/clam/.stack/snapshots/x86_64-osx/12382b4ca686679b054c6ac37d488c643f7f1197053bb8186833a08cfc1faf8b/8.10.7/share/x86_64-osx-ghc-8.10.7/tidal-1.7.8
Listening for external controls on 127.0.0.1:6010
That port isn't available, perhaps another Tidal instance is already listening on that port?
t> *** Exception: Network.Socket.connect: : invalid argument (Invalid argument)

Hi @damu.
Did you ever get your integration from Tidal -> Maxmsp working?

I've been working on similar problems - directing OSC output to other sources, including MaxMSP.
I've had similar errors when running this OSC code from the Tidal user docs. However, I found that some of the errors don't mean OSC output isn't working.

I learned that I could ignore the error about the 6010 port not being available. My understanding is that 6010 is the port that Tidal is listening on. But that should have no bearing on sending OSC via port 5050, which is defined in the Target.

I also had problems with the OSC code in my BootTidal.hs file. In a .tidal file I got errors trying to evaluate the full code all at once. I did get OSC output working and receiving properly in Max with these steps:

  • Start Tidal as usual (no changes to BootTidal.hs)
  • Evaluate the OSC code in a .tidal file
  • Evaluate each block separately (Target block, then oscplay block, parameters, etc)
  • Add "let" before each streamReplace statement
  • remove the "volume" parameter from the oscplay ArgList - this isn't essential, and I couldn't get the volume parameter to send correctly (not sure why??)

I followed the example for multiple targets and modified the oscmap value. This lets me evaluate a line and hear it through SuperDirt and see the OSC output in Max at the same time. That way I confirm that everything is working on the Tidal side.

let oscmap = [(target, [oscplay]),
              (superdirtTarget, [superdirtShape])
             ]

x1 $ s "bd*4" #pan #vowel "e" #cut 2

In MaxMSP view the OSC output with the udpreceive 5050 object connected to print receivedmess @popup 1. With each beat from Tidal, an OSC message with the address of "/play" will go to the console. You can then connect udpreceive 5050 to route /play connected to unpack s s f f i f f f and parse all of the values defined in oscplay $Arglist. Adjust the args to unpack per the OSC args you actually send. The order of the index values in the OSC msg matches the order you define in the $ argsList.

I found that adding cycle, delta, and cps to $Arglist was essential for dealing with event timing in Max.

What are you doing with these messages once they are in Max at this point? I'm (very) new to TidalCycles and interested to hear what is being done with sound / synthesis platforms other than SuperCollider. I have no idea whether there are working systems in Max, PD, etc, but would be very happy to see such things.

Thanks for asking. I don't have anything generating audio output from Max yet, and I'm working on some other projects so I may not get back to this for awhile.

My main idea (& hope) is to use the OSC output from Tidal to trigger granular and other synthesis capability I have programmed in Max/MSP patches. I can take frequency, amplitude, and note duration from OSC and route those in the Max patch. Then define other parameters in Tidal/OSC to map to Max synth patches parameters (brightness, dampen, density, attack buffer, etc). Tidal is really good at providing a framework for generating rhythmic values via patterns and making on the fly changes to note and parameter values/patterns. Max/MSP is really good at custom sound synthesis and can process at the sample level if desired. So combining these two seems interesting. But it also involves some (at times) painstaking work.

I'm interested to hear if others have followed the path of Tidal -> OSC -> MaxMSP.

Thanks for the info. Back in the days of Max 6, I put together a couple of patches that passed data back and forth between Max and a python script using OSC. The idea was to use Max for sound synthesis and python for number crunching. I can see doing something similar with TC and Max.

Once I've gotten a better handle on the basic TC system, I may take a closer look at the OSC messages generated by TC. It would be interesting to have a Max patch that can process these cleanly.

Update: I've made progress and now have a completed piece that uses Tidal with OSC and audio output to MaxMSP. There are some limitations to my OSC parsing in Max, so there is more work to do. But I'm happy to have a working solution with a finished piece.

The objective was to be able to use custom signal processing and effects in Max, from the livecoding interface in Tidal. Additionally, I wanted to route both audio (from SuperDirt/SuperCollider) and OSC to to Max while controlling changes in Tidal.

Audio processing

  • custom samples in SuperDirt/SuperCollider
  • customized SuperDirt start file, with 24 channel ~dirt.orbit routing
  • Loopback audio device for multi channel routing (Blackhole also works)
  • Max audio input from Loopback device
  • routing in Max to use VST/AU filter plugin for each channel
  • output from Loopback to OBS for screen recording with audio

OSC processing (Tidal)

  • Used the "Multiple targets" model defined in the Configuration > OSC section of the Tidal Docs (OSC goes to SuperDirt and to MaxMSP)
  • Code added for OSC target, parameter mapping, OSC stream, etc.
  • Mapped parameters for effects & plugins hosted in Max
    • Stereo delay: delayTimeL, delayTimeR, feedbackL, feedbackR
    • VST plugin filters: VSTparam1-6

OSC processing (Max)

  • udpreceive object -> route the OSC /play address defined in OSC target
  • route an OSC address for each Tidal channel (x1, x2, x3, etc)
  • In each Max channel:
    • unpack (break out the OSC list values)
    • send individual parameter values to the delay and VST plugin objects

NOTE added after writing this post. This limitation is incorrect. (See below.)
Limitation: I do not have detailed time based processing of OSC messages in Max. The OSC messages come at the beginning of the cycle. Each message does contain timing values, which can be evaluated and processed in Max for precise timing of any parameter changes. But that was more work and learning than I was up for! For this usage - I was content with changing effects values with the cycle and quickly adapted to that in my livecoding.

For any Max users interested - the GitHub link above includes all of my code - the Tidal livecoding, Tidal OSC mapping, and the MaxMSP patch.

Very nice scrapes!

Because you have oSchedule = Live in your osc config, the OSC messages should be sent at the right time, without the need to schedule them the other end. There will be very slight jitter from processing time between the OSC being sent and mac receiving/processing it, but that should be minimal. If it's only being sent at the start of a cycle, that's a bug somewhere.

Thanks @yaxu. I went back and looked at this closely. I stand corrected! The messages are coming in at the right time, not just at the beginning of the cycle. I remember seeing things differently, but that was early on and I may have had things configured incorrectly. This is great news, and makes this solution more usable.

BTW - the very detailed OSC Configuration section in User Docs was really helpful.

1 Like

Hi @HighHarmonics !

I was trying to set tidal for sending osc messages to both supercollider and max msp ! I used your model for just testing if max msp is receiving messages but i doesn't work this is my code :

let target = Target {oName = "oscMAX", -- target name in error msg
oAddress = "localhost", -- The target's network address, normally "localhost"
oPort = 5050, -- The network port the target is listening on
oLatency = 0.2, -- Additional delay, to smooth out network jitter/get things in sync
oSchedule = Live, -- The scheduling method - see below
oWindow = Nothing, -- Not yet used
oHandshake = False, -- SuperDirt specific
oBusPort = Nothing -- Also SuperDirt specific
}

oscplay = OSC "/play" $ ArgList [
("s", Nothing), -- without "s" no OSC sent - this is for sound
("parameter1", Just $ VF 0.35),
("parameter2", Just $ VF 0.5),
("parameter3", Just $ VF 0.8)
]

let param1 = pF "parameter1"
param2 = pF "parameter2"
param3 = pF "parameter3"

oscmap = [(target, [oscplay]), (superdirtTarget, [superdirtShape])]

stream <- startStream defaultConfig oscmap

d = streamReplace stream

d1
$ s "bd"

param1 0.5

and then I have this error message in tidal :

t> [TidalCycles version 1.9.4] Installed in /Users/marioganau/.local/state/cabal/store/ghc-9.8.1/tdl-1.9.4-dd53c228/share Listening for external controls on 127.0.0.1:6010 That port isn't available, perhaps another Tidal instance is already listening on that port?

t> Can't handshake with SuperCollider without control port.

could you help me please ?

@MarioGanau
I looked thru your code and everything looks good, except one small detail.

I get these same error messages every time I use this. I just ignore them. I know there is a way to start up without the handshake error, but I can't recall it, sorry. My understanding is that the localhost 6010 port is for communication back from your OSC target. I never use this and don't know of a use case where this matters.

The line you have: d = streamReplace stream I don't think will work. If you follow the OSC documentation, you set up the streamReplace like this:

streamReplace stream 1 $ s "bd"

or with a shortcut and then use x1 like you would d1:

let x1 = streamReplace stream 1
x1 $ s "bd"

However, I'm not certain on this is the problem.
You may be having a problem in Max. In your Audio Status, make sure your Input Device is set to the same device that SuperCollider uses.

You can test the OSC receive in Max. See the attached screenshots which show how I route the OSC messages. Note that the udp object needs the same port as you specify in your Tidal OSC Target config. Using the msg boxes from the unpack object should show your OSC message from Tidal once you start a pattern. Or you can connect a msg box to the route objects which should show the full OSC message.


maxOSC-unpack

Hope this helps.

hello @HighHarmonics ! thanks a lot for your reply ...
Listen , now I am able to send osc messages to max using this code above. Also I have just this error about port 6010 but I agree with you, that's the port that tidal use for to receive osc message si it doesn't matter but there is another problem ... I can do like you say to use this function

let x1 = streamReplace stream 1
x1 $ s "bd"

and it's work but,
tell me if for you I understand well. In the end the function of tidal is only to send osc message to superdirt/supercollider with the default port 57120 but now, in this way, when i use the pattern above , I can send osc message by the port 5050 to max but I can't here the bd kick play because that message is only sent to that port. So I would like to use the d1, d2 ecc pattern for to send at the same time osc messages to both the software .

So I'll try to study more and then let's see ...

@yaxu Alex sorry for to disturb you , do you have a solution for that

have a nice day

Mario

Hi Mario, because you are including superdirt in your oscmap, then it should already work.

Please share your full BootTidal.hs file and whatever code you're running and I'll have a look to see what's going wrong.

ciao @yaxu ! so here you have my BootTidal.hs file :

:set -XOverloadedStrings
:set prompt ""

import Sound.Tidal.Context

import System.IO (hSetEncoding, stdout, utf8)
hSetEncoding stdout utf8

:{
let target =
Target {oName = "max", -- A friendly name for the target (only used in error messages)
oAddress = "localhost", -- The target's network address, normally "localhost"
oPort = 5050, -- The network port the target is listening on
oLatency = 0.2, -- Additional delay, to smooth out network jitter/get things in sync
oSchedule = Live, -- The scheduling method - see below
oWindow = Nothing, -- Not yet used
oHandshake = False, -- SuperDirt specific
oBusPort = Nothing -- Also SuperDirt specific
}
oscplay = OSC "/play" $ ArgList [("s", Nothing),
("parameter1", Just $ VF 0.1),
("parameter2", Just $ VF 0.1),
("parameter3", Just $ VF 0.1)
]
param1 = pF "parameter1"
param2 = pF "parameter2"
param3 = pF "parameter3"
oscmap = [(target, [oscplay]), (superdirtTarget {oLatency = 0.05}, [superdirtShape])]
:}

tidal <- startStream defaultConfig oscmap

:{
let only = (hush >>)
p = streamReplace tidal
hush = streamHush tidal
panic = do hush
once $ sound "superpanic"
list = streamList tidal
mute = streamMute tidal
unmute = streamUnmute tidal
unmuteAll = streamUnmuteAll tidal
unsoloAll = streamUnsoloAll tidal
solo = streamSolo tidal
unsolo = streamUnsolo tidal
once = streamOnce tidal
first = streamFirst tidal
asap = once
nudgeAll = streamNudgeAll tidal
all = streamAll tidal
resetCycles = streamResetCycles tidal
setCycle = streamSetCycle tidal
setcps = asap . cps
getcps = streamGetcps tidal
getnow = streamGetnow tidal
xfade i = transition tidal True (Sound.Tidal.Transition.xfadeIn 4) i
xfadeIn i t = transition tidal True (Sound.Tidal.Transition.xfadeIn t) i
histpan i t = transition tidal True (Sound.Tidal.Transition.histpan t) i
wait i t = transition tidal True (Sound.Tidal.Transition.wait t) i
waitT i f t = transition tidal True (Sound.Tidal.Transition.waitT f t) i
jump i = transition tidal True (Sound.Tidal.Transition.jump) i
jumpIn i t = transition tidal True (Sound.Tidal.Transition.jumpIn t) i
jumpIn' i t = transition tidal True (Sound.Tidal.Transition.jumpIn' t) i
jumpMod i t = transition tidal True (Sound.Tidal.Transition.jumpMod t) i
jumpMod' i t p = transition tidal True (Sound.Tidal.Transition.jumpMod' t p) i
mortal i lifespan release = transition tidal True (Sound.Tidal.Transition.mortal lifespan release) i
interpolate i = transition tidal True (Sound.Tidal.Transition.interpolate) i
interpolateIn i t = transition tidal True (Sound.Tidal.Transition.interpolateIn t) i
clutch i = transition tidal True (Sound.Tidal.Transition.clutch) i
clutchIn i t = transition tidal True (Sound.Tidal.Transition.clutchIn t) i
anticipate i = transition tidal True (Sound.Tidal.Transition.anticipate) i
anticipateIn i t = transition tidal True (Sound.Tidal.Transition.anticipateIn t) i
forId i t = transition tidal False (Sound.Tidal.Transition.mortalOverlay t) i
d1 = p 1 . (|< orbit 0)
d2 = p 2 . (|< orbit 1)
d3 = p 3 . (|< orbit 2)
d4 = p 4 . (|< orbit 3)
d5 = p 5 . (|< orbit 4)
d6 = p 6 . (|< orbit 5)
d7 = p 7 . (|< orbit 6)
d8 = p 8 . (|< orbit 7)
d9 = p 9 . (|< orbit 8)
d10 = p 10 . (|< orbit 9)
d11 = p 11 . (|< orbit 10)
d12 = p 12 . (|< orbit 11)
d13 = p 13
d14 = p 14
d15 = p 15
d16 = p 16
:}

:{
let getState = streamGet tidal
setI = streamSetI tidal
setF = streamSetF tidal
setS = streamSetS tidal
setR = streamSetR tidal
setB = streamSetB tidal
:}

:set prompt "tidal> "
:set prompt-cont ""

default (Pattern String, Integer, Double)

and then if I try to evaluate just this line i have this error :

d1
$ s "bd"

t> ghc-9.8.1: Network.Socket.sendBufTo: does not exist (No route to host)

then if I do this I have this error:

d1
$ s "bd" # param1 0.3 # param2 0.1 # param3 0.5

t>
[GHC-88464]
Variable not in scope: param :: t0 -> Pattern ValueMap
Suggested fix:
Perhaps use one of these:
‘param1’ (line 26), ‘param2’ (line 27), ‘param3’ (line 28)

so here I am.

Thanks, could you try changing "localhost" to "127.0.0.1"?

It might be an issue with ipv4 vs ipv6.

The second error is confusing - it says you're using param when you aren't..

done and now I have this if I evaluate this :

d1
$ s "bd*4"

t> [GHC-88464] Variable not in scope: d1 :: Pattern ValueMap -> b Suggested fix: Perhaps use ‘dt’ (imported from Sound.Tidal.Context)

[GHC-88464] Variable not in scope: param :: t2 -> Pattern ValueMap

[GHC-88464] Variable not in scope: param :: t1 -> Pattern ValueMap

[GHC-88464] Variable not in scope: param :: t0 -> Pattern ValueMap

I can't here any sound and the osc message to max is not sent

sorry the pattern was this :

d1
$ s "bd*4" # param 1 # param 2 # param 3

Aha, so this should be correct usage:

d1
$ s "bd" # param1 0.3 # param2 0.1 # param3 0.5

It doesn’t work at all @yaxu
I don’t here any sound and no osc message are sent to max with both messages

So nothing is changed

GoodMorning @yaxu

just for let you know that now everything work properly. I found where the issue was. I would like to share with you just because maybe you can tell me why it is happened.
In the all tests that I was doing before I created a directory where there was the new modified BootTidal.hs and the file.tidal where I was writing my pattern. I don't know why but the all mistakes was because I was reading the new BootTidal.hs from this directory.

Then I put the modified BootTidal.hs in the original installation folder and everything starting to be ok.

That's all ... and also You have to put the address 127.0.0.1 and not localhost

any Yes now I am able to send osc message to Max and work with supercollider

have a nuce sunday

Mario

2 Likes