TonicAudio / Tonic

Easy and efficient audio synthesis in C++
The Unlicense
522 stars 63 forks source link

ControlGenerator::initialOutput is problematic when controlgenerator is used as a trigger #234

Open morganpackard opened 10 years ago

morganpackard commented 10 years ago

This leads us to the larger problem of the trigger system having evolved from the hasChanged flagged, and the rules of when something should trigger or not never having been been very clear.

Consider the following situation:

ControlGenerator stepTrigger = addParameter("stepTrigger");
ControlGenerator stepper = ControlStepper().start(0).end(10).trigger( stepTrigger  );
setOutputGen( SineWave() * cg.smoothed() +  SineWave() * cg.smoothed());

Every called to smoothed calls ControlGenerator::initialOutput, which forces ControlValue to trigger. The stepper winds up advancing twice before any audio is calculated.

ndonald2 commented 10 years ago

Yeah, it's definitely not ideal. I think the whole generator communication protocol needs to be rethought. The problem this was trying to fix was that a ramped value would initially start at zero and then smooth up to whatever the first output was, rather than starting at the first output. A quick fix for this particular situation would be to get rid of initialOutput completely (which I definitely think is a good idea) and just modify the ramped value generator so a flag can be set on it in smoothed() to make it immediately jump to whatever the first target input is.

I still think changing from "hasChanged" to "triggered" was the right decision. "hasChanged" implies a change, which is confusing if you want to propagate an action down the chain even though the numeric output remains the same (repeated "triggers" of value 1, for example), whereas "triggered" is a much clearer name for that functionality.

The clearest explanation I can give for the "rules" about when something is triggered is that it should behave almost identically to a "bang" in pd or max/msp. That's what I had in mind from the beginning. Modifying the control generator output signals to be polymorphic might make this more flexible, so a "trigger" can exist on its own with no numeric value attached.

morganpackard commented 10 years ago

In some ways, I prefer the old "hasChanged" flag, because the rules of when to set it to true and when to false are clearer.

ndonald2 commented 10 years ago

In some ways, I prefer the old "hasChanged" flag, because the rules of when to set it to true and when to false are clearer.

I strongly disagree, see above. There really needs to be the concept of a "bang", or as we call it "trigger", separate from numeric value. "hasChanged" implies the value has changed which is not always true.

morganpackard commented 10 years ago

I agree that "hasChanged" should never have been true if the actual numerical value hadn't changed. I can imagine an approach where there's no bang or trigger, only a convention that some generators trigger when their children's values change. In that case, "hasChanged" would be a literal representation of whether the value changed since the last tick, and would simply be a convenience flag so that dependent objects don't have to track the history themselves.

SuperCollider uses a concept of "gate" for triggering UGens. When a value changes from negative to positive, that's considered a trigger for triggerable objects, which isn't so off from the hypothetical I just described.

Admittedly, I'm not crazy about SuperCollider's trigger convention, but mention it because there is some precedent for triggering without an explicit bang message.

That said, I trust your feeling that a bang message, and polymorphic messages in general are the way to go. Though I'm not sure either of us is going to want to invest the time to make that switch any time soon.

ndonald2 commented 10 years ago

Though I'm not sure either of us is going to want to invest the time to make that switch any time soon.

Truth. I've been putting it off for awhile now. But every time I do something new with Tonic, I want to do it more. So eventually that desire will win out, I think.