Hi list, Here's an abstract question for a change: If I'd like to create a large-scale patch in which various UGens need to work at various different, and possibly changeable rates, what would be a good strategy to go about this? What kind of tacs do you currently use? Here's a fictitious example. Say I have two of my OneOfN switches for oscs that are updated at 10 Hz to save CPU cycles, an envelope follower that is updated every sample and a couple of short delays that are synchronized based on the notion of a musical beat and tempo. Computing musical time given the meter and tempo in bpm is not a problem nor is figuring out the sampling rate, though utility functions would be appreciated. The basic issue is that when you do add something to the now time, pardon my Cakewalk lingo, you'll have to take that addition into account when timing all the rest of the modules. And if you have multiple instances of a module that should run at the same rate, you'll have to structure the code in such a way that these modules get updated at the same time, if I've understood things correctly. This can soon get very messy. Here are some tacs that come to mind: Have a counter that is inced every sample and divide its frequency by various rates using the modulo operator to continuously, say poll the input attached to my OneOfN switch. This is the method they use in basic digital logic frequency dividing a high frequency clock source to get various nicer rates out. But the counter might overflow some day and branching takes a bit of CPU. Or another thing. IMplement an infrastructure for timers in which, a particular method of all attached objects gets called when-ever a specified amount of time has elapsed. THen spork a child process for each such timer and have them run concurrently , without having to worry about timing after the initial setup. I'm not sure how heavy context-switches are inside the VM shreads, though. Lastly, this came to mind. Or don't try to save CPU at all and let chucK update everything at virtually every sample, even though you know a human is not able to generate controller events very fast, for example. I think SynthEDit uses this last tak. I have a nagging feeling somewhere in my head that I've misunderstood something about timing. That if I just keep adding an arbitrary amount to the now time everything will just get automagically updated every sample. The only thing i'd have to explicitly worry about then Would be the stuff I need to poll periodically. I'm still a bit unsure. So any comments and hints appreciated. With kind regards Veli-Pekka Tätilä (vtatila@mail.student.oulu.fi) Accessibility, game music, synthesizers and programming: http://www.student.oulu.fi/~vtatila/
Veli-Pekka Tätilä wrote:
[...] IMplement an infrastructure for timers in which, a particular method of all attached objects gets called when-ever a specified amount of time has elapsed. [...]
Coming from a Max background, one of the first things that I worked out was a Metro object: http://developer.kde.org/~wheeler/files/Metro.ck That is overly simple, but a reasonable example. From there you can do stuff like: fun void beat(Metro m) { while(m.pulse => now) { <<< "beat" >>>; } } Metro m; m.start(); spork ~ beat(m); 30::second => now; ChucK lets you wait on events just like it lets you wait on arbitrary time or sample amounts, so it makes it easy to synchronize multiple shreds based on them. -Scott
participants (2)
-
Scott Wheeler
-
Veli-Pekka Tätilä