AudioTK library

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

OK... I don't understand what you want to achieve with a constant array, but yes, you can use an std::vector to stream values in (just use vector.data(), vector.size() for the arguments).
Don't forget that ApplyGain is just multiplying the two streams together, the envelope is the output of the AR filter.

Post

Miles1981 wrote:OK... I don't understand what you want to achieve with a constant array, but yes, you can use an std::vector to stream values in (just use vector.data(), vector.size() for the arguments).
Don't forget that ApplyGain is just multiplying the two streams together, the envelope is the output of the AR filter.
In order to get an envelope that's the envelope in the value range of [a,b], a and b doubles, I need to start with e.g. taking the envelope on the range [0, 1].

I.e. on a signal that's only 1.0s since the attack release filter generates the envelope from 0 to some max value (which I want to be 1.0) until it falls back to 0?

This should be typical for all kinds of parameter modulation, e.g. controlling a filter cutoff, where you want the envelope to go through a predefined range of values.

Post

On a signal that is only 1, the envelope is a constant 1... OK, at the beginning it start with 0, so there is a small ramp up. And then it keeps on trying to reach 1, it never goes down, that's the envelope of the "constant" 1 (which actually is not a constant 1, it's a Heaviside function).
Still don't understand why you want to start with [0, 1] when you want an envelope of something else that lies inside [a, b]. It may be easier to explain to me what you want to achieve (even by MP) so I can give you the proper pipeline!

Post

I want an envelope that has a predefined range [a,b], a and b doubles.
I.e. whatever values it produces (and whatever signal it's given) a is the min value it can reach and b is the max value it can reach.

Post

OK, I think I understand. The easiest would be to constrain the input values with a custom filter. Check the VolumeFilter and instead of multiplying witht he gain, just do a min/max with your values.

Post

Can you explain that a bit more? I didn't understand.

Post

For instance, this is the volume filter: https://github.com/mbrucher/AudioTK/blo ... Filter.cpp
Let's say that you copy the header and cpp in your project and rename VolumeFilter to MinMaxFilter.
Add the following prototypes:

Code: Select all

public:
set_min(double min);
set_max(double max);
private:
double min;
double max;
Write the implementation to set these values, and write the following process_impl

Code: Select all

template<typename DataType_>
  void MinMaxFilter<DataType_>::process_impl(int64_t size) const
  {
    for(int channel = 0; channel < nb_input_ports; ++channel)
    {
      const DataType* ATK_RESTRICT input = converted_inputs[channel];
      DataType* ATK_RESTRICT output = outputs[channel];
      for(int64_t i = 0; i < size; ++i)
      {
        output[i] = input[i] > max ? max : (input[i] < min ? min : input[i]);
      }
    }
  }
And then use this filter as any other in your pipeline.

Post

Is this technically the same as a threshold level?
That is, that it "chops" the envelope and takes only the part that fits into [min, max].

But how does this lead to the "same envelope" as the attack release filter generates on a full-band signal, but just "packed into" a different value range.

Post

I'm confused but that isn't unusual. :)

Fluky, using a generic AR envelope follower for generating a synthesizer type AR one shot envelope would probably take more cpu than writing a standalone object which might use the same principles.

But for instance, if you set attack for 10 ms and release at 100 ms or what you like. Start with the envelope follower initted to zero. Begin feeding the envelope follower 1.0 values until the state of the follower reaches some threshold level.

Maybe pick 0.99 or what you like. When you notice the envelope follower is >= 0.99, stop feeding the follower with 1.0, and start feeding it with 0.0, and it will enter the release phase. Keep feeding the follower 0.0 until maybe the value of the follower reaches <= 0.01 or whatever. Then the cycle is complete.

You could write a one shot object that does the same as above, but runs a little faster and may be easier to read and use.

You could also as simply write a linear one shot envelope that might work good enough on some tasks, where each sample just adds a small number to env value until it reaches the upper thresh, then subtracts another small number until the env reaches a lower threshold. For instance if the samplerate is 44.1 KHz and you want 10 ms, the attack increment on each sample is 1.0 / (TimeInSeconds * SampleRate) = 1.0 / (0.01 * 44100) = 0.0023.

Add 0.0023 to the envelope each sample and after 441 samples it has reached 1.0.

Or you could write similar things with different shaped curves.

One thing to watch in this kind of coding-- If any number, such as the envelope value, could possibly eventually get very tiny, it is good to check and if you get "too close" to zero, set the value to zero and declare the task done.

Or for instance exponential rising to 1.0, if your math subtracts the current value from 1.0 on every cycle, when getting close to 1.0 the subtraction result can get very tiny. So just set to 1.0 and declare the task done.

If numbers get too small, you can get denormal float numbers, which can make the computer dramatically slow down. So set tiny numbers to zero before they get small enough to denormal.
Last edited by JCJR on Fri Aug 28, 2015 10:53 pm, edited 1 time in total.

Post

Fluky wrote:Is this technically the same as a threshold level?
That is, that it "chops" the envelope and takes only the part that fits into [min, max].

But how does this lead to the "same envelope" as the attack release filter generates on a full-band signal, but just "packed into" a different value range.
Yes, that's what this one does. If you want something more waveshaper like, you may want to use something custom and use a modified version of GainFilter.h. In any case, it will change the spectrum of your original signal, unless you know the original extent of your signal, and then you can use the OffsetVolumeFilter.

Post

Actually it might be easier to write a custom envelope class. Since I can't figure out how that "constraining" above compares to an envelope follower that follows the actual peaks of the input signal.

I was thinking of whether it would be possible to set a threshold, then take the envelope out of the part that goes over it and then determine how this part should fit into the [a,b] range so that it retains the ratios between the signal peaks. That is, the max peak amplitude of the signal on a given interval should generate the max envelope value.

Moving to another thread:
http://www.kvraudio.com/forum/viewtopic ... 3&t=445164

Post

Using AudioTK's attack release filter now.

Is it a feature of the filter that using settings 100.0 attack 1.0 release it seems to work as an expander? I.e. it produces higher gain than input gain. Actually it seems to only relate to small release values, e.g. in the range 1.0-15.0. Then it "expands" i.e. "leaks" higher amplitude than what went in.

It could also be caused in my code, if the envelope someone produces negative values. Which I would need to solve by taking some absolute values. This is actually partially the case. I'm multiplying the envelope with another negative value, which means that -a*-b produces ab, which is a positive value, eventhough I only want negative values.

Any idea how to do taking the absolute value? Should I be enveloping only the positive half of the signal? Or absolute valued signal?

Post

Attack and release must be between 0 and 1. To convert a time to such a number, it's as I've said in other posts, use exp(1/(dt * sampling_rate)).
I forgot to add an exception to forbid values higher than 1.

The envelope is never be negative. An envelope is in R+, not R. As I've said as well, you should use a filter before to compute the type of norm you want. I always use PoweFilter because I want RMS like envelope, but you can use abs if you wanted. It's as easy to write as the InvertFilter.

Post

Oops.

But, I'm converting the numbers using
exp( -1 / t * 1e-3 * samplerate), where t is the time value in seconds.

So is the [0,1] in seconds or what? Or I can use the above conversion so that dt is time in seconds?

The envelope produces some expansion since I'm applying it to a parameter that does not produce overgain in its value range. Still adding the envelope by multiplying the parameter with it leads to overflowing values, i.e. expansion, at some attack/release values. This must mean that the produced envelope does contain negative values (since the parameter I'm multiplying is all negatives, so it can't produce positive unless there's two negatives multiplied).
Last edited by soundmodel on Sun Aug 30, 2015 7:35 pm, edited 1 time in total.

Post

Miles1981 wrote:Attack and release must be between 0 and 1. To convert a time to such a number, it's as I've said in other posts, use exp(1/(dt * sampling_rate)).
I forgot to add an exception to forbid values higher than 1.

The envelope is never be negative. An envelope is in R+, not R. As I've said as well, you should use a filter before to compute the type of norm you want. I always use PoweFilter because I want RMS like envelope, but you can use abs if you wanted. It's as easy to write as the InvertFilter.
Why does it lead to that using that exp-equation above it leads to constantly increasing values, to infinity. I.e. converting attack 1.0 and release 1.0 and what i see is a value that's increasing and increasing and increasing... like it never stops.

Post Reply

Return to “DSP and Plugin Development”