|
From: Steve H. <S.W...@ec...> - 2002-11-14 12:28:09
|
Hi all,
Benno and I were discussing envelope generation last night, I think that
the right way to generate an exponential envelope (I checked some synths
outputs too and it looks like this is that way its done) is to feed a
constant value into a LP filter with different parameters for each stage.
I suspect that, in general you need to calculate the amplitude of the
envelope for each sample, to avoid stairstepping (zipper noise).
I expect Paul Kellett knows the right approach, so should be able to say
if we're barking up the wrong tree.
Example code:
#include <math.h>
#include <stdio.h>
#define EVENTS 5
#define ENV_NONE 0
#define ENV_ATTACK 1
#define ENV_DECAY 2
#define ENV_SUSTAIN 3
#define ENV_RELEASE 4
void lp_set_par(double time, double *a, double *ai) {
*a = exp(-5.0 / time); // The 5.0 is a fudge factor
*ai = 1.0 - *a;
}
int main() {
unsigned int event_time[EVENTS] = {0, 100, 200, 400, 900};
unsigned int event_action[EVENTS]= {ENV_ATTACK, ENV_DECAY, ENV_SUSTAIN,
ENV_RELEASE, ENV_NONE};
unsigned int i, event = 0;
float env_input = 0.0f;
double env = 0.0f;
double a, ai;
float attack_level = 1.0f;
float sustain_level = 0.5f;
float release_level = 0.0f;
for (i=0; i<1000; i++) {
if (i == event_time[event]) {
switch (event_action[event]) {
case ENV_ATTACK:
env_input = attack_level;
break;
case ENV_DECAY:
env_input = sustain_level;
break;
case ENV_SUSTAIN:
env_input = sustain_level;
break;
case ENV_RELEASE:
env_input = release_level;
break;
}
lp_set_par((double)(event_time[event+1] -
event_time[event]), &a, &ai);
event++;
}
env = env_input * ai + env * a;
printf("%g\n", env);
}
return 0;
}
|
|
From: Steve H. <S.W...@ec...> - 2002-11-14 13:44:28
|
On Thu, Nov 14, 2002 at 12:28:02 +0000, Steve Harris wrote: > Example code: > Theres a graph of the output here: http://inanna.ecs.soton.ac.uk/~swh/foo.png - Steve |
|
From: Benno S. <be...@ga...> - 2002-11-15 12:00:59
|
Hi, the shapes do look nice, I was wondering about two things: What the ideal coefficients would be so that very fast envelopes can be achieved while slower ones are smoothed out so that no zipper noise is audibile. BTW since in most of cases (in absence of envelopes) the LP smoothing is not needed, it would be wise to skip that code (to save cycles) when the envelope reached the final point (up to a very small epsilon since we are talking of exponentials). What do you suggest here ? Regarding adding a let's say resonant LP filter to the sample output: I guess the coefficients need to be smoothed out in some way too, otherwise zipper noise will probably show up again. I've found these interesting msgs on the saol-users list but I do not have a deep understanding of the things mentioned in these mails. http://sound.media.mit.edu/mpeg4/saol-users/0179.html http://sound.media.mit.edu/mpeg4/saol-users/0178.html What would a good tradeoff for achieving low-CPU usage zippernoise-free filter modulation would be ? (some kind of interpolation of precomputed coefficients ?) Benno On Thu, 2002-11-14 at 13:28, Steve Harris wrote: > Hi all, > > Benno and I were discussing envelope generation last night, I think that > the right way to generate an exponential envelope (I checked some synths > outputs too and it looks like this is that way its done) is to feed a > constant value into a LP filter with different parameters for each stage. > -- http://linuxsampler.sourceforge.net Building a professional grade software sampler for Linux. Please help us designing and developing it. |
|
From: Steve H. <S.W...@ec...> - 2002-11-15 12:32:34
|
On Fri, Nov 15, 2002 at 01:12:42 +0100, Benno Senoner wrote: > Hi, > the shapes do look nice, I was wondering about two things: > What the ideal coefficients would be so that very fast envelopes can > be achieved while slower ones are smoothed out so that no zipper noise > is audibile. I dont understand the question, the envelope shape is dynamic depending on the length of the appropriate section (attack, decay, etc). The only thing that needsa to be selected is the fudge factor (4.0 ish IIRC) which determines epsilon for the envelope. > BTW since in most of cases (in absence of envelopes) the LP smoothing is > not needed, it would be wise to skip that code (to save cycles) when the > envelope reached the final point (up to a very small epsilon since we > are talking of exponentials). What do you suggest here ? Well, the code for this is created on demand right? (a JIT sampler ;) So if there is no envelope there is no envelope code. If there is an envelope, then when we have reached epsilon, the envelope has (in theory) come to an end, we can fade to 0.0 to make sure, and then the voice has finished. BTW do we know for sure than samplers use exponential envelopes? I guess we need linear ones too, but they are easy to implement. We should probably get some recordings from samples of high freq sinewaves with envelopes. I think Frank N. has done this allready for the S2000, Frank are you on the list? > Regarding adding a let's say resonant LP filter to the sample output: > I guess the coefficients need to be smoothed out in some way too, > otherwise zipper noise will probably show up again. Yes. Though you can get away without it if you know the cutoff isn;t chaging too rapidly (eg. a slow LFO). > What would a good tradeoff for achieving low-CPU usage zippernoise-free > filter modulation would be ? (some kind of interpolation of precomputed > coefficients ?) I think so. Not all filters like you doing this, but most are OK IIRC. I have some SVF and biquad test code around, so I can test this if you like. I think my LADSPA SVF uses linear interp. to smooth the coefficients. - Steve |
|
From: Frank N. <bea...@we...> - 2002-11-15 23:55:20
|
Hi, Steve wrote: > BTW do we know for sure than samplers use exponential envelopes? I guess we > need linear ones too, but they are easy to implement. We should probably > get some recordings from samples of high freq sinewaves with envelopes. > I think Frank N. has done this allready for the S2000, Frank are you on > the list? Yes, I'm here :-). I just made a short example of this for you; I hope this is what you were asking for, I have a hard time trying to catch up with all the mailing lists :-). I loaded a sine wave into the sampler, set the program's attack and release times to 0 and played a couple of high-pitched notes, sampled this into the PC (44.1 kHz) and had a closer look at the attack and release phase of the sample (for analysis, Conrad Parker's "sweep" is highly recommended; I just learnt recently that you can even vertically zoom into a sample by using Shift+Cursor up/down, which helps a lot in finding the exact starting point of a sample :-) Please find the following files for analysis: http://lakai.sf.net/sinetest.wav.gz (the sample, 440 KByte packed) http://lakai.sf.net/sine1_attack.png (a screenshot of a close look at the attack phase) http://lakai.sf.net/sine2_release.png (the same for the release phase) It was interesting for me to discover that the release phase is always faded out somewhat exponentially (and "rather slowly", taking about 2.2ms), while the attack phase is very short; looking _very_ close at it, I'd say it's about 28 samples long, ~0.6 ms. But both are _not zero_ (so really "hard clicks" can never occur). That's the same behaviour I'd expect from any software instrument, too. If you need more analysis, let me know; however, I'll be on a company trip (to Bristol - yeah, UK, unfortunately not So'ton :-}) from Sunday - Thursday, so don't expect answer before Friday. Greetings, Frank |
|
From: Steve H. <S.W...@ec...> - 2002-11-16 08:15:43
|
On Sat, Nov 16, 2002 at 12:48:20 +0100, Frank Neumann wrote: > Please find the following files for analysis: > http://lakai.sf.net/sinetest.wav.gz (the sample, 440 KByte packed) > http://lakai.sf.net/sine1_attack.png (a screenshot of a close look at the > attack phase) > http://lakai.sf.net/sine2_release.png (the same for the release phase) Thanks, thats great. > If you need more analysis, let me know; however, I'll be on a company trip > (to Bristol - yeah, UK, unfortunately not So'ton :-}) from Sunday - > Thursday, so don't expect answer before Friday. Shame up in that part of the cournty quite often, but not this week. Cheers, Steve |
|
From: Benno S. <be...@ga...> - 2002-11-15 13:04:09
|
On Fri, 2002-11-15 at 13:31, Steve Harris wrote:
>
> I dont understand the question, the envelope shape is dynamic depending on
> the length of the appropriate section (attack, decay, etc). The only thing
> that needsa to be selected is the fudge factor (4.0 ish IIRC) which
> determines epsilon for the envelope.
Ok.
>
> Well, the code for this is created on demand right? (a JIT sampler ;)
> So if there is no envelope there is no envelope code. If there is an
> envelope, then when we have reached epsilon, the envelope has (in theory)
> come to an end, we can fade to 0.0 to make sure, and then the voice has
> finished.
But I meant a voice that can be dyncamically enveloped at runtime where
the voice sucks up less CPU when no enveloping is active.
>
> BTW do we know for sure than samplers use exponential envelopes? I guess we
> need linear ones too, but they are easy to implement. We should probably
> get some recordings from samples of high freq sinewaves with envelopes.
> I think Frank N. has done this allready for the S2000, Frank are you on
> the list?
Since I'm a bit CPU-power paranoid at this design stage, I thought it it
really make sense to use the exponentials.
I mean: why not use only linear envelopes and "emulate" exponentials
with a few linear segments.
The big advantage of linear that it costs us only one addidional
addition whiche your exponential code is more expensive.
with linear envelopes we have:
while(1) {
output_sample(pos);
sample_pos += pitch;
pitch += pitch_delta;
volume += volume_delta;
}
These two _delta additions add very little to the total CPU cost but
provide us with good flexibility.
When no pitch/volume enveloping is occurring, simply set
pitch_delta and volume_delta to 0
When applying modulation generate events that set the *_delta variables
in appropriate intervals.
Assume we want to emulate exponentials: generate events at let's say 1/8
samplingrate and I think it is probably impossible to distinguish the
approximation from the exact curve. (we are talking of relatively "slow"
volume and pitch modulation).
For example assume we process MIDI pitch bend events with the linear
interpolator.
It is probably the easiest to keep track of the previous pitch value
and then simply interpolate linearly to the new value within a let's say
0.5 - 1 msec time frame. I think this would smooth out things nicely,
right ?
(Steve, others, do you agree ?)
>
> Yes. Though you can get away without it if you know the cutoff isn;t
> chaging too rapidly (eg. a slow LFO).
Ok but assume these nice LP filter wah-wah effects.
The LFO frequency in this case is up to a few Hz, but the modulation
frequency (filter coefficient change rate) ? How low can it go so that
zipper noise can be avoided ?
>
> > What would a good tradeoff for achieving low-CPU usage zippernoise-free
> > filter modulation would be ? (some kind of interpolation of precomputed
> > coefficients ?)
>
> I think so. Not all filters like you doing this, but most are OK IIRC.
> I have some SVF and biquad test code around, so I can test this if you
> like. I think my LADSPA SVF uses linear interp. to smooth the coefficients.
ok let us know.
Benno.
--
http://linuxsampler.sourceforge.net
Building a professional grade software sampler for Linux.
Please help us designing and developing it.
|
|
From: Josh G. <jg...@us...> - 2002-11-16 03:12:58
|
I was just having a few thoughts concerning different implementations of synth primitives (envelopes, LFOs, wavetable interpolation, filters, etc). It seems like if LinuxSampler is going to be modular (or JIT based) there could be several ways to do things for different CPU/quality trade offs. It might even be nice to have very expensive algorithms created (6+ point interpolation or something :) This could be useful as one could create a MIDI sequence of a song, compose it with less CPU hungry synth primitives and then have the option of rendering the final piece in non-realtime using all the highest quality primitives :) So it seems building the modular framework is probably the most important stage, no? Or is the current focus to create something that just works? Anyways, just some thoughts. Cheers. Josh Green |