elemaudio / elementary

Elementary is a JavaScript library for digital audio signal processing.
https://www.elementary.audio/
MIT License
315 stars 28 forks source link

Changing a parameter between gate triggers #30

Closed cephasteom closed 7 months ago

cephasteom commented 9 months ago

Hi all. Thanks for your work on Elementary. I'm enjoying using it. I've been using RNBO so far, but it's great to have a performant DSP tool for web with a JS API.

I have a particular use case. I want to change a parameter when a voice is playing. I don't know in advance when that parameter will change, so I can't use el.train or a sequence. The parameter therefore needs to set to the requested value immediately when the voice is triggered, but be smoothed when altered during the voice's lifecycle.

What would be the best way to achieve this?

Many thanks in advance.

Pete.

nick-thompson commented 9 months ago

Hey @cephasteom thanks and so sorry for the delay!

I'm not sure I fully understand your question so bear with me, I think you can generally just update the parameter value as needed at the time you receive the event. For example–

let state = { frequency: 800, cutoff: 800 };

function render(state) {
  core.render(el.lowpass(el.const({ key: 'fc', value: state.cutoff }), 1, el.blepsaw(el.const({key: 'ff', value: state.frequency}))));
}

// Don't know when this is going to fire
eventEmitter.on('event', function(e) {
  state.cutoff = scale(e.value, 20, 18000);
  render(state); // re-render when we have the new value
});

This is a very simplified example of course, but the idea is simple: as soon as you know what value you need, update your state, and re-render your audio graph. Using the key property on el.const there is an effective way to alert Elementary to instantaneously change values instead of trying to apply structural graph changes.

You can also use the new refs feature introduced in elem v3 if you want scoped value updates:

let [freq, setFreqProps] = core.createRef("const", {value: 800}, []); // initial value of 800
let [cutoff, setCutoffProps] = core.createRef("const", {value: 800}, []); // initial value of 800

function render(state) {
  core.render(el.lowpass(cutoff, 1, el.blepsaw(el.const({key: 'ff', value: freq}))));
}

// Don't know when this is going to fire
eventEmitter.on('event', function(e) {
  setCutoffProps({value: scale(e.value, 20, 18000)}); // instantaneously apply a value update
});

Does that cover your question?