Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tidal streams should process at a fixed rate per second #133

Closed
yaxu opened this issue Aug 18, 2016 · 21 comments
Closed

tidal streams should process at a fixed rate per second #133

yaxu opened this issue Aug 18, 2016 · 21 comments
Projects

Comments

@yaxu
Copy link
Member

yaxu commented Aug 18, 2016

Currently do it at a fixed rate per cycle, which means if cps is low, it takes a long time for changes to patterns to take effect.
It would still be nice if changes could be quantised, but this could be done with a transition function.

@telephon
Copy link
Contributor

telephon commented Sep 5, 2016

In sc, each node- or pattern-proxy has variables that allow you to specify when (and to a degree also how) the transition to the replacing code happens.

@lennart
Copy link
Contributor

lennart commented Dec 5, 2016

I hope no one considers the current behavior correct, so flagging this as a bug.

@yaxu
Copy link
Member Author

yaxu commented Dec 7, 2016

I'm a bit unsure how to approach this. It think that processing e.g. every 250ms would not be a good idea. It is nice to be able to evaluate a pattern just before the cycle boundary and have it come in at exactly the right moment. If the processing did not align with cycle boundaries this would be impossible.
The alternative is increasing/decreasing the granularity of processing in steps, e.g. 1/4 of a cycle for 1 cps which increases to 1/8 of a cycle for 0.5 cps. The question is what to do for the cps values in between?

@lennart
Copy link
Contributor

lennart commented Dec 7, 2016

thinking about this:

  1. cps bound
    tick patterns x times per cycle
    pro: changes to pattern at end of a cycle will take effect on the next cycle
    cons: time for evaled patterns to take effect could literally take ages!

  2. unbound, fixed
    tick patterns at fixed frequency
    pro: same time for evaled patterns to take effect, regardless of perceived "tempo"
    cons: might lead to unwanted glitches since eval frequency might not be in time with tidal clock

So what we want is:

  • changes just before end of cycle take effect on next cycle (glitchfree)
  • changes never take ages

?

@bgold-cosmos
Copy link
Contributor

Would it solve both issues to check x times per cycle, where x is something like floor (8/cps)? Any weird glitches that could cause?

@yaxu
Copy link
Member Author

yaxu commented Dec 11, 2016

If I understand you right that's the current arrangement @bgold-cosmos, with the current setting of 4 per cycle.

@lennart yes I agree in my case but others might have different needs, an alternative way of looking at it is you either want
1/ evaluation/change on the sam (cycle boundary)
2/ evaluation/change as soon as possible

Want you want might change depending on the situation.

@bgold-cosmos
Copy link
Contributor

Currently it's fixed at 4 (or 8?) per cycle and doesn't depend on cps; I was thinking it could just switch dynamically to more ticks per cycle if cps is changed to something very low.

@yaxu
Copy link
Member Author

yaxu commented Dec 12, 2016

Gotcha, yes that makes sense, maybe checking once per cycle is safest?

@dktr0
Copy link
Contributor

dktr0 commented Aug 4, 2017

Hmmm... When patterns execute over more than one cycle, for example as a result of using "slow", the same issue recurs. Once per cycle with a pattern that has had slow 8 applied to it will be redefined at the next 1/8 of the way through the pattern.

@vivid-synth
Copy link
Contributor

An idea:

At 60 cps it's 4x per cycle

|-----|-----|-----|-----| 4 x 250ms

At 30 cps, we want still to be updating at 250ms

|--|--|--|--|--|--|--|--| 8 x 250ms

So a possible algorithm is simply:

  • At minimum, process once per cycle
  • If the duration of that cycle is longer than some duration (let's call it 250ms here), then "subdivide"
  • If that subdivision is still greater than our max duration, subdivide again
  • Repeat until the duration is under our maximum window

This way we get changes on cycle boundaries but some sense of how much time there will be before another recalculation. Each change is also guaranteed to happen on a 4/4 quant. (3/3 and friends, I'm not sure how to tackle that)

@vivid-synth
Copy link
Contributor

Ah, looks like @d0kt0r0 had a similar thought a couple days ago on another ticket (#222 (comment))

@dktr0
Copy link
Contributor

dktr0 commented Aug 9, 2017

We'll have to remember both ways of describing it if we go this way, when we talk about in the documentation (which we really should!)! :)

@dktr0
Copy link
Contributor

dktr0 commented Aug 9, 2017

On a related tangent, I think the temporal threshold may come down about from the 1/4 - 1/8 second thresholds at some point too - there are better mechanisms (I'm speculating) for quantizing when changes to intentions take effect than playing with the way rendering is chunked. So when such other mechanisms are in place, there may be advantages - both performant (i.e. traditional computing efficiency measures) and performer-oriented (making things that can turn on a dime, i.e. less than the 50 milliseconds or so duration of a typical auditory grain, in relation to performer intentions).

@telephon
Copy link
Contributor

generally, the better you distribute osc sending over time, the more performant the superdirt side will be. Note that the latencies of packets may differ as needed.

@vivid-synth
Copy link
Contributor

@telephon do you mean that SuperDirt is more performant when there's a smaller number of bundles, each with a larger number of messages?

@telephon
Copy link
Contributor

no, rather the opposite. It is better we distribute messages evenly over time (reception time).

@yaxu
Copy link
Member Author

yaxu commented Aug 18, 2017

I think we want to support two interactions:

Make a change as soon as possible. For this we need to get latency down as much as possible by reducing this pre-processing window to a minimum and reducing latency between tidal and superdirt etc too. (The current 0.4 seconds feels too much to me, I don't think I was experiencing problems with 0.02 seconds before.)

Make a change on the next cycle. I thought before that this meant changing the granularity of processing in steps. I've now gone against this idea as I've realised we can just do this in the tidal language by using a transition.

From what I've seen, Tidal is more efficient if you have a much smaller pre-processing window than we have now. So I think it we would be able to find a sweet spot of quite a high fixed rate per second that superdirt will be happier with too. Processing in this way might turn up some bugs in Tidal but hey..

Then we can treat scheduling changes at the next cycle boundary (or some other quantum) separately.

@telephon
Copy link
Contributor

for this, it would be really good if we were able to change the latency in tidal on the fly. When you encounter late messages in supercollider, you just increase that value when you need it. That way we don't need so much headroom.

It is possible to simulate this by using lag but tha

@telephon
Copy link
Contributor

... that is not intended to be used that way.

@OscarSouth
Copy link

OscarSouth commented Sep 17, 2018

I'm not sure that I understand what's being discussed here with 100% accuracy (are we talking 'backend' between tidal and SuperDirt or in terms of user interaction/performance?).

In terms of performance, I think updating on the cycle boundaries is a favourable way of thinking when improvising with tidalcycles. It's a very intuitive transference of the 'clip launching' paradigm that's all over music production these days and it keeps the aesthetic nature of improvisation and composition with higher order functions uniform.

It could be useful in terms of expression to be able to update instantaneously, but I'm not sure if that's desirable as 'default'. I have a few drum sequencers that I use which can change pattern instantaneously and I find that I only very rarely want to use them like that - requires a lot of 'realtime' thought by the performer that is more desirably spent in that moment (before the already submitted one has hit) on the next subsequent change. In the cases where a realtime update is desired, adding a parameter specifying to submit the changes asap seems a logical mental methodology (ie. a # realtimeor something that can be included) -- not sure if this works technically but it feels like a logical approach in performance terms.

Also, I like the ideas of changing 'global' quantisation settings during a composition or improvisation. For example if you could just run a line like cps n >> cycleDiv n in order to set the tempo and the update frequency, then that feels like a very logical addition.

That said (as I mentioned above) I'm not 100% sure that I'm commenting accurately on the issue. Apologies if it's not exactly related to the discussion.

@yaxu
Copy link
Member Author

yaxu commented Sep 30, 2018

Ok two years later, fixed calculation rate is implemented now: https://github.com/tidalcycles/Tidal/blob/refactor/src/Sound/Tidal/Stream.hs

Still plenty of testing and work to do around it, but I'll close this issue for now

@OscarSouth yes updating on cycle boundaries by default is the way to go I think, this is more of an internal bugfix than a UI issue, as we can make that happen as a default transition function

@yaxu yaxu closed this as completed Sep 30, 2018
Refactor automation moved this from In progress to Done Sep 30, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Refactor
  
Done
Development

No branches or pull requests

7 participants