Thursday, April 21, 2011

Sound Programming - Part 3

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


Musical Notes

So far we have implemented a beep() method which will produce a sound with specified frequency. Different frequencies corresponds to different pitches. If we predefine a table of frequency for every musical notes. We can play simple melody in our program.

    java.util.Hashtable<String,Integer> frequencyTable
      = new java.util.Hashtable<String,Integer>();

    frequencyTable.put("c",262);   // frequency of middle C
    frequencyTable.put("d",294);
    frequencyTable.put("e",330);
    frequencyTable.put("f",349);
    frequencyTable.put("g",392);
    frequencyTable.put("a",440);
    frequencyTable.put("b",494);
    frequencyTable.put("C",523);
    frequencyTable.put("D",587);
    frequencyTable.put("E",659);


Playing the melody

We can write a simple interpretor for playing simple melody.
    String melody="c d e f g a b C b a g f e d c";

    for (int i=0;i<melody.length();i++)
    {
      int duration=250;
      String s=melody.substring(i,i+1);
      Integer freq = frequencyTable.get(s);
      if (freq!=null) beep(freq, duration);
    }


Full Source Code

/******************************************************************************
* File : MusicalNote.java
* Author : http://java.macteki.com/
* Description :
*   Play simple musical note
* Tested with : JDK 1.6
******************************************************************************/
  
class MusicalNote
{
  
  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
  {

    java.util.Hashtable<String,Integer> frequencyTable=
      new java.util.Hashtable<String,Integer>();

    frequencyTable.put("c",262);   // frequency of middle C
    frequencyTable.put("d",294);
    frequencyTable.put("e",330);
    frequencyTable.put("f",349);
    frequencyTable.put("g",392);
    frequencyTable.put("a",440);
    frequencyTable.put("b",494);
    frequencyTable.put("C",523);
    frequencyTable.put("D",587);
    frequencyTable.put("E",659);

    String melody="c d e f g a b C b a g f e d c";

    for (int i=0;i<melody.length();i++)
    {
      int duration=250;
      String s=melody.substring(i,i+1);
      Integer freq = frequencyTable.get(s);
      if (freq!=null) beep(freq, duration);
    }

  }
}

No comments:

Post a Comment