On Thu, Oct 11, 2012 at 4:26 PM, John Clements <[email protected]> wrote: > As part of the rsound package, I find myself reinventing data flow languages; > or, at least, the parts that I need. I've come to a design question that I > don't know how to answer, and I'm hoping that those with more experience can > make suggestions. > > First: I'm re-inventing dataflow languages because: > - the natural way to specify audio signals is as dataflow networks (e.g., I > have a low-frequency oscillator controlling the frequency of this other > oscillator…), and > - FrTime is sadly not fast enough to use, here.
Yes. There are significant opportunities for performance improvement in FrTime, but for this sort of scenario, I think a "pull" evaluator will be hard to beat. > Also, I'm fine with dataflow, here, I don't need full FRP. > > The other connection here is to World-style programming; in a big-bang, you > specify an initial world and then a transition function (or functions), that > map one world to the next. I can't use this framework as-is because of > parallel composition issues; specifically, if I have the two oscillators I > describe above, then the natural way to represent their state would be as a > structure containing the state of each individual component. Allocating a new > one of these 44K times per second uses up a lot of memory really quickly. So, > I've developed my own framework, that uses mutation but hides it from the > user. > > Using my "network" syntax, I might specify the above as: > > (network () > [lfo (sine-wave 2)] > [out (sine-wave (+ 200 (* 10 lfo)))]) > > This creates a sine wave at 2 Hz, controlling another sine wave whose > frequency varies between 210 and 190 Hz twice per second. > > Everything fine so far. > > In order to make this work, we need signals that refer to old values of > themselves, as in the world model. To allow this, I have a "prev" primitive > that allows you to refer to the prior value of some signal node. So, for > instance, here's a simple counter that goes up one each tick: > > (define simple-ctr > (network () > [out (add1 (prev out)) #:init 0])) > > So, this counter rises by one on each tick. Note that we need to specify an > initial value, as with big-bang. > > BUT, HERE COMES THE PROBLEM. > > What should the first value of this signal be? Should it be zero, or should > it be one? Put differently: does this node's updater get called on the first > tick, producing 1, or should we just use the initial value, zero? > > Zero is the one I really want, but then I have a problem: how do I > distinguish between clauses, like this one, that should not be evaluated the > first time through, from others, something like this: Can you not just use the presence of the #:init keyword to disable evaluation for the first step (or the first n steps in general)? That's probably what I'd do, though I may be missing something that makes this problematic... --Greg > [sum (+ sig-a sig-b)] > > … that *should* be evaluated the first time through? > > My current solution is to treat them uniformly, and to evaluate all of them > each time. This means that I wind up with dreadful hacks like this one: > > [out (add1 (prev out)) #:init -1] > > … so that the -1 gets bumped up to a zero on the first output. It turns out > that this trick is fragile; if you don't know what the increment will be, you > can't accurately "pre-decrement" it. > > > I can imagine doing something more complicated, but what I really want to ask > is this: for those of you with experience in other dataflow languages, how do > they solve this? > > > Sorry for the long e-mail; I'm hoping that someone can point to a nice > solution that exists in another language! > > > John > > > ____________________ Racket Users list: http://lists.racket-lang.org/users

