2

I made a simple sine wave tone generator. The problem is that when the tone is played a strong click can be heard, and I need to implement a fast fade in (attack time) to avoid this.

I tried using tweening (like tweenmax) but it induces distortion to the audio (maybe the steps in the tweening?). I found some vague tutorials on the subject but nothing regarding attack time specifically.

How can I do this?

NPN328
  • 1,863
  • 3
  • 27
  • 47

1 Answers1

3

For the fade to sound smooth, it has to be incremented on a per-sample basis, inside your synthesis loop. A tween engine may update many times a second, but your ear can still hear the changes as a click.

In your sampleData event handler, you will have to multiply the individual samples by a volume modifier, with the range of 0 to 1, incrementing for every sample.

To quickly fade in the sound, start by setting the volume to 0, and adding a small value to it for each sample, until it reaches 1.0. You can later expand this into a more complex envelope controller.

This is a rough example of what you might start with:

for( i = 0; i < length; i++ ) {
    _count++;
    factor = _frequency * Math.PI * 2 / 4400;
    volume += 1.0 / 4400;
    if( volume > 1.0 ) volume = 1.0; //don't actually do it like this, ok?
    n = Math.sin( (_count) * factor ) * volume;
    _buffer.writeFloat(n); 
    _buffer.writeFloat(n); 
}

NOTE: I haven't tested this snippet, nor would I recommend using it for production. It's just to show you roughly what I mean.

Another technique that may work for you is to put an ease / delay on the volume. Use a volumeEase variable that always 'chases' the target volume at a certain speed. This will prevent clicks when changing volumes and can be used to make longer envelopes:

var volume:Number = 0; // the target volume
var volumeEase:Number = 1.0; // the value to use in the signal math
var volumeEaseSpeed:Number = 0.001; // tweak this to control responsiveness of ease

for( i = 0; i < length; i++ ) {
    _count++;

    // bring the volumeEase closer to the target:
    volumeEase += ( volume - volumeEase ) * volumeEaseSpeed;

    factor = _frequency * Math.PI * 2 / 4400;

    //use volumeEase in the maths, rather than 'volume':
    n = Math.sin( (_count) * factor ) * volumeEase;
    _buffer.writeFloat(n); 
    _buffer.writeFloat(n); 
}

If you wanted to, you could just use a linear interpolation, and just go 'toward' the target at a constant speed.

Once again, the snippet is not tested, so you may have to tweak volumeEaseSpeed.

null
  • 1,187
  • 1
  • 8
  • 20