Rhythm
The program in the previous article is able to replay simple melody. However, the interpretor doesn't have ways to define rhythm. That means every musical note has the same duration. In order to play simple song, we need to define duration modifier in our simple interpretor. Let's define the modifiers as follows :
"-"The duration of the previous note is doubled"/"The duration of the previous note is reduced by half"."The duration of the previous note is extended by a factor of 1.5
With the above modifier, the song "Jingle Bells" can be represented by the following string :
String m="eee-eee-egc.d/e---fff.f/feee/e/edded-g-eee-eee-egc.d/e---fff.f/feee/e/ggfdc---";
Jingle Bells
The following is a program that play the song "Jingle Bells". It is a modification of the program in the previous post./******************************************************************************
* File : Jingle.java
* Author : http://java.macteki.com/
* Description :
* Play "Jingle Bells"
* Tested with : JDK 1.6
******************************************************************************/
class Jingle
{
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 song="eee-eee-egc.d/e---fff.f/feee/e/edded-g-eee-eee-egc.d/e---fff.f/feee/e/ggfdc---";
int full=250; int half=full/2;
for (int i=0;i<song.length();i++)
{
int duration=full;
String s=song.substring(i,i+1);
// get pitch
Integer freq = frequencyTable.get(s);
// get duration modifier
int j=i+1;
while (j<song.length())
{
String s1=song.substring(j,j+1);
if (s1.equals("/")) { duration-=half; }
else if (s1.equals(".")) { duration+=half; }
else if (s1.equals("-")) duration+=full;
else break;
j++;
}
if (freq!=null) beep(freq, duration);
}
}
}
this tutorial and website are great, it's really helping me get my head around java.
ReplyDeleteThanks for reading.
DeleteGreat blog man, thank you for sharing the knowledge!
ReplyDeleteThanks for reading. Be sure to share this site with a friend.
Delete