## Tuesday, April 19, 2011

### Sound Programming - Part 2

This is part 2 of the series. It is suggested to read the whole series from the beginning.

### Attack, Decay, Sustain, Release

Let's recall the sound wave of a pure tone.

The amplitude of the vibration doesn't change. Hence the sound volume doesn't change over time.
In reality, sound volume is seldom constant. For example, if we hit a key on the piano, the sound volume go through four phases.
1. Attack
The volume bursts to the maximum level in a short period of time
2. Decay
The volume drops a little bit
3. Sustain
The volume holds constant for a period
4. Release
The sound starts to fade out.

This is known as the ADSR envelope. The following is a visualization of the ADSR envelope.

You may think of the envelope as a multiplier function. If we apply the envelope to our pure tone sine wave, the result would become :

### Simplified Model

While ADSR is a good mathematical model for musical intrusment, it is a bit too complicated for our simple program. Hence we will use a simpler model. Our new model will contain two phases only, the sustain and the release phase. To further simplified the model, we will use linear fade out. The multiplier function would become :

### Implementation

The implementation of linear fade out is very simple. It just involves a few lines of modifications of the beeping program in part 1.
```// modifications of Beep.java
double fade=1;
int decay=sampleLength*1/3;  // start fade out at 2/3 of the total time
if (i>=sampleLength-1-decay) fade=(double)(sampleLength-1-i)/decay;

short amplitude = (short) (volume*fade*sinValue);

```

### Full Source Code

```/******************************************************************************
* File : FadeBeep.java
* Author : http://java.macteki.com/
* Description :
*   Play a pure tone with specified frequency, with fade out effect
* Tested with : JDK 1.6
******************************************************************************/

class FadeBeep
{

static void beep(double frequency, int duration) throws Exception
{

int nChannel = 1;         // number of channel : 1 or 2

// samples per second
float sampleRate = 16000;  // valid:8000,11025,16000,22050,44100
int nBit = 16;             // 8 bit or 16 bit sample

int bytesPerSample = nChannel*nBit/8;

double durationInSecond = (double) duration/1000.0;
int bufferSize = (int) (nChannel*sampleRate*durationInSecond*bytesPerSample);
byte[] audioData = new byte[bufferSize];

// "type cast" to ShortBuffer
java.nio.ByteBuffer byteBuffer = java.nio.ByteBuffer.wrap(audioData);
java.nio.ShortBuffer shortBuffer = byteBuffer.asShortBuffer();

int sampleLength = audioData.length/bytesPerSample;

// generate the sine wave
double volume = 8192;   // 0-32767
double PI = Math.PI;
for(int i = 0; i < sampleLength; i++){
double time = i/sampleRate;
double freq = frequency;
double angle = 2*PI*freq*time;
double sinValue = Math.sin(angle);

double fade=1;
int decay=sampleLength*1/3;  // start fade out at 2/3 of the total time
if (i>=sampleLength-1-decay) fade=(double)(sampleLength-1-i)/decay;

short amplitude = (short) (volume*fade*sinValue);

for (int c=0;c<nChannel;c++)
{
shortBuffer.put(amplitude);
}

}//end generating sound wave sample

boolean isSigned=true;
boolean isBigEndian=true;

// Define audio format
javax.sound.sampled.AudioFormat audioFormat =
new javax.sound.sampled.AudioFormat(sampleRate, nBit, nChannel, isSigned,isBigEndian);

javax.sound.sampled.DataLine.Info dataLineInfo =
new javax.sound.sampled.DataLine.Info(
javax.sound.sampled.SourceDataLine.class, audioFormat);

// get the SourceDataLine object
javax.sound.sampled.SourceDataLine sourceDataLine =
(javax.sound.sampled.SourceDataLine)
javax.sound.sampled.AudioSystem.getLine(dataLineInfo);

sourceDataLine.open(audioFormat);
sourceDataLine.start();

// actually play the sound
sourceDataLine.write(audioData,0,audioData.length);

// "flush",  wait until the sound is completed
sourceDataLine.drain();

}

public static void main(String[] args) throws Exception
{
int frequency=400;   // hz
int duration =2000;  // milliseconds
beep(frequency,duration);
}
}
```

### Result

The program will play a sound for 2 seconds, which will fade out gradually. The sound waveform is something like this (illustration only, not in accurate scale):

#### 3 comments:

1. Thanks for sharing this example. I have a question though, would it be the same using for instance a wav file that is on a device? You have any tutorials with that?

1. Yes, go to the featured page and locate part 5 of sound programming.
Thanks for reading.

2. Your example in Lesson 5 helped me. Thank you very much for sharing :D