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.

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:

[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



Attachment: smime.p7s
Description: S/MIME cryptographic signature

____________________
  Racket Users list:
  http://lists.racket-lang.org/users

Reply via email to