Bubobubobubobubo / sardine

Python's missing "algorave" module. Live code music with Python using MIDI, OSC and/or SuperCollider.
https://sardine.raphaelforment.fr
GNU General Public License v3.0
192 stars 31 forks source link

Supporting Vortex patterns with Sardine #214

Closed Bubobubobubobubo closed 2 months ago

Bubobubobubobubo commented 1 year ago

It would be nice to support yet another flavor of patterning using Tidal's Vortex port made by @munshkr, @yaxu and others. It shouldn't be hard to get right as the two codebases have quite similar architectures. I have opened a vortex branch and started moving the pattern logic + the mini-notation in there. I have also added a few methods to my version of the link-clock in clock/ to support the pub/sub pattern Vortex uses.

Hopefully, most of the work should be replacing liblo by osc4py3 + making it talk with the FishBowl. @munshkr, @yaxu, I would be happy to know what you think about the project and to work a bit on this new port with you if you have a bit of time to sink into it. More specifically, I want to be sure not to infringe any license or moral property of Vortex by porting it into Sardine.

yaxu commented 1 year ago

Hey @Bubobubobubobubo, looks like we both use the GPL 3 so thankfully talk about licenses can end there :) No objections here, only encouragement.

I am planning to focus work on haskell tidal towards version 2.0 for the next months so probably won't have much time to share on the python side, but feel free to ask questions

Bubobubobubobubo commented 1 year ago

I did some work on it and it is now 'usable' with the following syntax:

However, my initial conversion work got rid of some of the anti-drifting (or so I suppose) calculations that were installed by Vortex. The tempo is also incredibly fast (why?). A decent tempo for testing is clock.tempo = 10. I'm really happy to see that it works though.

yaxu commented 1 year ago

In tidal there are no beats, so tempo is in cycles (measures) per second not beats. Also there should be no drifting as time as rational.

Bubobubobubobubo commented 1 year ago

In tidal there are no beats, so tempo is in cycles (measures) not beats. Also there should be no drifting as time as rational.

Yes, I think that I somehow messed up with the timestamp that comes with every SuperDirt message. It's really encouraging to see it working so fast though so it's just probably some fine tuning to do still :)

Bubobubobubobubo commented 1 year ago

The tempo is now super stable. It's just... not the right tempo :) Still working on it. I should also look into freely nudging the tidal streams in time. This will open up the possibility of blending the different patterning flavours.

PS: I can actually still hear some micro fluctuations, very noticeable for high tempos.

Bubobubobubobubo commented 1 year ago

Completely stuck with the notify_tick function. It looks like I am not computing the diff correctly with my port causing some noticeable delay with other types of patterns:

https://github.com/Bubobubobubobubo/sardine/blob/b3267f31a163992774eeaf4c93b3eccf4563edd1/sardine/sequences/tidal_parser/stream.py#L42-L47

Solving this issue should resolve the timing issue that prevents it from being usable.

The original version was using liblo.time() instead of that weird datetime thing.

Bubobubobubobubo commented 1 year ago

Tried different corrections / tweaks / fixes / bleeps and bloops but I still can't manage to make them cooperate. The different flavors of patterns stay really close together, at the same tempo, but I can't manage to superpose them correctly. I've resorted to trimming the initial logic quite a lot in the vortex branch to make it easy to understand in order to implement a working solution.

You can get them to superpose by playing with the .nudge attribute attached to each Handler but this will only work for n playbacks as the two pattern types will slowly drift in and out of phase again. Once locked, they now stay a little bit longer together before drifting which means that they will also take a lot more time to resync later in time again.

The tidal patterns are using an unconventional method to be scheduled compared to the base Sardine temporal model. I don't think that it should cause a problem though. The tidal patterns are really working great as is and are extremely stable.

yaxu commented 1 year ago

Can you check the drift for both at 60bpm / 1cps against a 'known good' time source?

Bubobubobubobubo commented 1 year ago

Not quite sure how to do that but I've noticed something interesting by looking at the OSC messages received by SuperCollider.

This is a message sent by Sardine-Vortex:

OSC Message Received:
    time: -1682850773.2439
    address: a NetAddr(127.0.0.1, 59285)
    recvPort: 57120
    msg: [ /dirt/play, s, bd, cps, 0.5, cycle, 2.75, delta, 0.5 ]

This is a message sent by Sardine:

OSC Message Received:
    time: 3329.1294950701
    address: a NetAddr(127.0.0.1, 58175)
    recvPort: 57120
    msg: [ /dirt/play, cps, 0.4995000064373, cycle, 26, orbit, 0, sound, hh ]

Very obviously, time is negative for Vortex and the cps isn't quite exactly equal to 0.5 for Sardine.

NOTE: testing can only be done using the link_clock and not the internal_clock (for the moment).

Bubobubobubobubo commented 1 year ago

I converted the tidal_loop from an async background loop to an AsyncRunner following Sardine's main temporal logic. It is now working quite well but I need to rewrite some stuff and fully integrate Vortex before pushing to main. I also want to provide extensive documentation about it. It'll take some time but it will eventually be available as the third pattern language :)

EDIT: Trying to understand clock._ticks. It looks like it's the key to getting it right.

Bubobubobubobubo commented 1 year ago

Now in the main branch but the algorithm for the query rate is still a bit wonky. The loop itself is in run.py (init) and the other part in the clock itself link_clock.py (for testing). The remaining issue is stability + precision for fast and intricate rhythms.

EDIT: after talking with Zalastax about the Tidal scheduler, it appears that the current implementation is quite naïve and misses a large part of the logic:

Bubobubobubobubo commented 1 year ago

I have made some progress with the Vortex integration:

I am ready to backport some of the stuff I do to main Vortex if needed.

yaxu commented 1 year ago

@Bubobubobubobubo Ah ply has a beautiful definition that is helpful in understanding the monadic stuff etc.

I think the priority should be modularising vortex into a form that sardine can use directly so we're not duplicating effort though. Otherwise if you want to keep it as a hard fork it would be best to rename it to avoid confusion, I think that would be a bit of a shame though.

I'm deep in Haskell land at the moment but want to do the same there, mirroring the structure of the strudel packages: https://github.com/tidalcycles/strudel/tree/main/packages

Bubobubobubobubo commented 1 year ago

Solved most of the issues mentioned earlier:

I think the priority should be modularising vortex into a form that sardine can use directly so we're not duplicating effort though. Otherwise if you want to keep it as a hard fork it would be best to rename it to avoid confusion, I think that would be a bit of a shame though.

@yaxu: I totally agree with you, Vortex should be modularized and I would be happy to help with the effort. I don't want to hard fork it, I just want to support it :) I didn't dwell on it too much for the moment because I'm trying to keep up with my doctoral research schedule: my funding is running out soon and I want to show some results. Naturally, I will then spend time consolidating this effort by contributing to more interoperability between languages/pattern systems, which is one of Sardine's goals. For a similar project, look at ziffers-python with Miika Alonen.

I don't want to rush this though, which would also be annoying to everyone involved. It is probably best to find a time when everyone will be available to speak about the refactoring as separate modules. There are many low hanging fruits on the Vortex side and it could be fun to add some varnish while splitting the project into multiple components.

yaxu commented 1 year ago

@Bubobubobubobubo sounds like a good plan!

Bubobubobubobubo commented 2 months ago

Closing because of a general lack of interest, will probably re-open in the future. The Vortex integration will stay as is and will remain available. I have no plan to continue improving it before the next release but I don't want to remove it either.