763 lines
23 KiB
Java
Executable File
763 lines
23 KiB
Java
Executable File
/*Modified by Tommy Montgomery (1/07) to read
|
|
in a file containing frequencies and create
|
|
.au files for each of the tones */
|
|
|
|
/*File AudioSynth01.java
|
|
Copyright 2003, R.G.Baldwin
|
|
|
|
This program demonstrates the ability to create
|
|
synthetic audio data, and to play it back
|
|
immediately, or to store it in an AU file for
|
|
later playback.
|
|
|
|
A GUI appears on the screen containing the
|
|
following components in the North position:
|
|
|
|
Generate button
|
|
Play/File button
|
|
Elapsed time meter (JTextField)
|
|
|
|
Several radio buttons appear in the Center
|
|
position of the GUI. Each radio button selects
|
|
a different format for synthetic audio data.
|
|
|
|
The South position of the GUI contains the
|
|
following components:
|
|
|
|
Listen radio button
|
|
File radio button
|
|
File Name text field
|
|
|
|
Select a radio button from the Center and click
|
|
the Generate button. A short segment of
|
|
synthetic audio data will be generated and saved
|
|
in memory. The segment length is two seconds
|
|
for monaural data and one second for stereo data,
|
|
at 16000 samp/sec and 16 bits per sample.
|
|
|
|
To listen to the audio data, select the Listen
|
|
radio button in the South position and click the
|
|
Play/File button. You can listen to the data
|
|
repeatedly if you so choose. In addition to
|
|
listening to the data, you can also save it in
|
|
an audio file.
|
|
|
|
To save the audio data in an audio file of type
|
|
AU, enter a file name (without extension) in the
|
|
text field in the South position, select the
|
|
File radio button in the South position, and
|
|
click the Play/File button.
|
|
|
|
You should be able to play the audio file back
|
|
with any standard media player that can handle
|
|
the AU file type, or with a program written in
|
|
Java, such as the program named AudioPlayer02
|
|
that was discussed in an earlier lesson.
|
|
|
|
Tested using SDK 1.4.0 under Win2000
|
|
************************************************/
|
|
|
|
import javax.swing.*;
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import javax.sound.sampled.*;
|
|
import java.io.*;
|
|
import java.nio.channels.*;
|
|
import java.nio.*;
|
|
import java.util.*;
|
|
|
|
public class sound extends JFrame{
|
|
|
|
//The following are general instance variables
|
|
// used to create a SourceDataLine object.
|
|
AudioFormat audioFormat;
|
|
AudioInputStream audioInputStream;
|
|
SourceDataLine sourceDataLine;
|
|
|
|
//The following are audio format parameters.
|
|
// They may be modified by the signal generator
|
|
// at runtime. Values allowed by Java
|
|
// SDK 1.4.1 are shown in comments.
|
|
float sampleRate = 16000.0F;
|
|
//Allowable 8000,11025,16000,22050,44100
|
|
int sampleSizeInBits = 16;
|
|
//Allowable 8,16
|
|
int channels = 1;
|
|
//Allowable 1,2
|
|
boolean signed = true;
|
|
//Allowable true,false
|
|
boolean bigEndian = true;
|
|
//Allowable true,false
|
|
|
|
//--------String used to denote the file to write to--------//
|
|
String writeFile = "";
|
|
double frequency = 0.0;
|
|
|
|
//A buffer to hold two seconds monaural and one
|
|
// second stereo data at 16000 samp/sec for
|
|
// 16-bit samples
|
|
byte audioData[] = new byte[16000*4];
|
|
|
|
//Following components appear in the North
|
|
// position of the GUI.
|
|
final JButton generateBtn =
|
|
new JButton("Generate");
|
|
final JButton playOrFileBtn =
|
|
new JButton("Play/File");
|
|
final JLabel elapsedTimeMeter =
|
|
new JLabel("0000");
|
|
|
|
//Following radio buttons select a synthetic
|
|
// data type. Add more buttons if you add
|
|
// more synthetic data types. They appear in
|
|
// the center position of the GUI.
|
|
final JRadioButton tones =
|
|
new JRadioButton("Tones",true);
|
|
final JRadioButton stereoPanning =
|
|
new JRadioButton("Stereo Panning");
|
|
final JRadioButton stereoPingpong =
|
|
new JRadioButton("Stereo Pingpong");
|
|
final JRadioButton fmSweep =
|
|
new JRadioButton("FM Sweep");
|
|
final JRadioButton decayPulse =
|
|
new JRadioButton("Decay Pulse");
|
|
final JRadioButton echoPulse =
|
|
new JRadioButton("Echo Pulse");
|
|
final JRadioButton waWaPulse =
|
|
new JRadioButton("WaWa Pulse");
|
|
|
|
//Following components appear in the South
|
|
// position of the GUI.
|
|
final JRadioButton listen =
|
|
new JRadioButton("Listen",true);
|
|
final JRadioButton file =
|
|
new JRadioButton("File");
|
|
final JTextField fileName =
|
|
new JTextField("junk",10);
|
|
|
|
//-------------------------------------------//
|
|
public static void main(
|
|
String args[]){
|
|
new sound();
|
|
}//end main
|
|
//-------------------------------------------//
|
|
|
|
public sound(){//constructor
|
|
//A panel for the North position. Note the
|
|
// etched border.
|
|
final JPanel controlButtonPanel =
|
|
new JPanel();
|
|
controlButtonPanel.setBorder(
|
|
BorderFactory.createEtchedBorder());
|
|
|
|
//A panel and button group for the radio
|
|
// buttons in the Center position.
|
|
final JPanel synButtonPanel = new JPanel();
|
|
final ButtonGroup synButtonGroup =
|
|
new ButtonGroup();
|
|
//This panel is used for cosmetic purposes
|
|
// only, to cause the radio buttons to be
|
|
// centered horizontally in the Center
|
|
// position.
|
|
final JPanel centerPanel = new JPanel();
|
|
|
|
//A panel for the South position. Note the
|
|
// etched border.
|
|
final JPanel outputButtonPanel =
|
|
new JPanel();
|
|
outputButtonPanel.setBorder(
|
|
BorderFactory.createEtchedBorder());
|
|
final ButtonGroup outputButtonGroup =
|
|
new ButtonGroup();
|
|
|
|
//Disable the Play button initially to force
|
|
// the user to generate some data before
|
|
// trying to listen to it or write it to a
|
|
// file.
|
|
playOrFileBtn.setEnabled(false);
|
|
|
|
//Register anonymous listeners on the
|
|
// Generate button and the Play/File button.
|
|
generateBtn.addActionListener(
|
|
new ActionListener(){
|
|
public void actionPerformed(
|
|
ActionEvent e){
|
|
//Don't allow Play during generation
|
|
playOrFileBtn.setEnabled(false);
|
|
//Generate synthetic data
|
|
|
|
try
|
|
{
|
|
String readFile = "freq.txt";
|
|
FileReader f = new FileReader(readFile);
|
|
BufferedReader reader = new BufferedReader(f);
|
|
|
|
String line = reader.readLine();
|
|
while (line != null)
|
|
{
|
|
writeFile = line;
|
|
frequency = Double.parseDouble(line);
|
|
System.out.println(line);
|
|
new SynGen().getSyntheticData(audioData);
|
|
line = reader.readLine();
|
|
}
|
|
}
|
|
catch (IOException error)
|
|
{
|
|
System.out.println(error.getMessage());
|
|
}
|
|
//Now it is OK for the user to listen
|
|
// to or file the synthetic audio data.
|
|
playOrFileBtn.setEnabled(true);
|
|
}//end actionPerformed
|
|
}//end ActionListener
|
|
);//end addActionListener()
|
|
|
|
playOrFileBtn.addActionListener(
|
|
new ActionListener(){
|
|
public void actionPerformed(
|
|
ActionEvent e){
|
|
//Play or file the data synthetic data
|
|
playOrFileData();
|
|
}//end actionPerformed
|
|
}//end ActionListener
|
|
);//end addActionListener()
|
|
|
|
//Add two buttons and a text field to a
|
|
// physical group in the North of the GUI.
|
|
controlButtonPanel.add(generateBtn);
|
|
controlButtonPanel.add(playOrFileBtn);
|
|
controlButtonPanel.add(elapsedTimeMeter);
|
|
|
|
//Add radio buttons to a mutually exclusive
|
|
// group in the Center of the GUI. Make
|
|
// additions here if you add new synthetic
|
|
// generator methods.
|
|
synButtonGroup.add(tones);
|
|
synButtonGroup.add(stereoPanning);
|
|
synButtonGroup.add(stereoPingpong);
|
|
synButtonGroup.add(fmSweep);
|
|
synButtonGroup.add(decayPulse);
|
|
synButtonGroup.add(echoPulse);
|
|
synButtonGroup.add(waWaPulse);
|
|
|
|
//Add radio buttons to a physical group and
|
|
// center it in the Center of the GUI. Make
|
|
// additions here if you add new synthetic
|
|
// generator methods.
|
|
synButtonPanel.setLayout(
|
|
new GridLayout(0,1));
|
|
synButtonPanel.add(tones);
|
|
synButtonPanel.add(stereoPanning);
|
|
synButtonPanel.add(stereoPingpong);
|
|
synButtonPanel.add(fmSweep);
|
|
synButtonPanel.add(decayPulse);
|
|
synButtonPanel.add(echoPulse);
|
|
synButtonPanel.add(waWaPulse);
|
|
|
|
//Note that the centerPanel has center
|
|
// alignment by default.
|
|
centerPanel.add(synButtonPanel);
|
|
|
|
//Add radio buttons to a mutually exclusive
|
|
// group in the South of the GUI.
|
|
outputButtonGroup.add(listen);
|
|
outputButtonGroup.add(file);
|
|
|
|
//Add radio buttons to a physical group in
|
|
// the South of the GUI.
|
|
outputButtonPanel.add(listen);
|
|
outputButtonPanel.add(file);
|
|
outputButtonPanel.add(fileName);
|
|
|
|
//Add the panels containing components to the
|
|
// content pane of the GUI in the appropriate
|
|
// positions.
|
|
getContentPane().add(
|
|
controlButtonPanel,BorderLayout.NORTH);
|
|
getContentPane().add(centerPanel,
|
|
BorderLayout.CENTER);
|
|
getContentPane().add(outputButtonPanel,
|
|
BorderLayout.SOUTH);
|
|
|
|
//Finish the GUI. If you add more radio
|
|
// buttons in the center, you may need to
|
|
// modify the call to setSize to increase
|
|
// the vertical component of the GUI size.
|
|
setTitle("Copyright 2003, R.G.Baldwin");
|
|
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
|
setSize(250,275);
|
|
setVisible(true);
|
|
}//end constructor
|
|
//-------------------------------------------//
|
|
|
|
//This method plays or files the synthetic
|
|
// audio data that has been generated and saved
|
|
// in an array in memory.
|
|
private void playOrFileData() {
|
|
try{
|
|
//Get an input stream on the byte array
|
|
// containing the data
|
|
InputStream byteArrayInputStream =
|
|
new ByteArrayInputStream(
|
|
audioData);
|
|
|
|
//Get the required audio format
|
|
audioFormat = new AudioFormat(
|
|
sampleRate,
|
|
sampleSizeInBits,
|
|
channels,
|
|
signed,
|
|
bigEndian);
|
|
|
|
//Get an audio input stream from the
|
|
// ByteArrayInputStream
|
|
audioInputStream = new AudioInputStream(
|
|
byteArrayInputStream,
|
|
audioFormat,
|
|
audioData.length/audioFormat.
|
|
getFrameSize());
|
|
|
|
//Get info on the required data line
|
|
DataLine.Info dataLineInfo =
|
|
new DataLine.Info(
|
|
SourceDataLine.class,
|
|
audioFormat);
|
|
|
|
//Get a SourceDataLine object
|
|
sourceDataLine = (SourceDataLine)
|
|
AudioSystem.getLine(
|
|
dataLineInfo);
|
|
//Decide whether to play the synthetic
|
|
// data immediately, or to write it into
|
|
// an audio file, based on the user
|
|
// selection of the radio buttons in the
|
|
// South of the GUI..
|
|
|
|
/*
|
|
|
|
if(listen.isSelected()){
|
|
//Create a thread to play back the data and
|
|
// start it running. It will run until all
|
|
// the data has been played back
|
|
new ListenThread().start();
|
|
}else{*/
|
|
//Disable buttons until existing data
|
|
// is written to the file.
|
|
generateBtn.setEnabled(false);
|
|
playOrFileBtn.setEnabled(false);
|
|
|
|
//Write the data to an output file with
|
|
// the name provided by the text field
|
|
// in the South of the GUI.
|
|
try{
|
|
AudioSystem.write(
|
|
audioInputStream,
|
|
AudioFileFormat.Type.AU,
|
|
new File(writeFile +
|
|
".au"));
|
|
}catch (Exception e) {
|
|
e.printStackTrace();
|
|
System.exit(0);
|
|
}//end catch
|
|
//Enable buttons for another operation
|
|
generateBtn.setEnabled(true);
|
|
playOrFileBtn.setEnabled(true);
|
|
//}//end else
|
|
}catch (Exception e) {
|
|
e.printStackTrace();
|
|
System.exit(0);
|
|
}//end catch
|
|
}//end playOrFileData
|
|
//=============================================//
|
|
|
|
//Inner class to play back the data that was
|
|
// saved.
|
|
class ListenThread extends Thread{
|
|
//This is a working buffer used to transfer
|
|
// the data between the AudioInputStream and
|
|
// the SourceDataLine. The size is rather
|
|
// arbitrary.
|
|
byte playBuffer[] = new byte[16384];
|
|
|
|
public void run(){
|
|
try{
|
|
//Disable buttons while data is being
|
|
// played.
|
|
generateBtn.setEnabled(false);
|
|
playOrFileBtn.setEnabled(false);
|
|
|
|
//Open and start the SourceDataLine
|
|
sourceDataLine.open(audioFormat);
|
|
sourceDataLine.start();
|
|
|
|
int cnt;
|
|
//Get beginning of elapsed time for
|
|
// playback
|
|
long startTime = new Date().getTime();
|
|
|
|
//Transfer the audio data to the speakers
|
|
while((cnt = audioInputStream.read(
|
|
playBuffer, 0,
|
|
playBuffer.length))
|
|
!= -1){
|
|
//Keep looping until the input read
|
|
// method returns -1 for empty stream.
|
|
if(cnt > 0){
|
|
//Write data to the internal buffer of
|
|
// the data line where it will be
|
|
// delivered to the speakers in real
|
|
// time
|
|
sourceDataLine.write(
|
|
playBuffer, 0, cnt);
|
|
}//end if
|
|
}//end while
|
|
|
|
//Block and wait for internal buffer of the
|
|
// SourceDataLine to become empty.
|
|
sourceDataLine.drain();
|
|
|
|
|
|
//Get and display the elapsed time for
|
|
// the previous playback.
|
|
int elapsedTime =
|
|
(int)(new Date().getTime() - startTime);
|
|
elapsedTimeMeter.setText("" + elapsedTime);
|
|
|
|
//Finish with the SourceDataLine
|
|
sourceDataLine.stop();
|
|
sourceDataLine.close();
|
|
|
|
//Re-enable buttons for another operation
|
|
generateBtn.setEnabled(true);
|
|
playOrFileBtn.setEnabled(true);
|
|
}catch (Exception e) {
|
|
e.printStackTrace();
|
|
System.exit(0);
|
|
}//end catch
|
|
|
|
}//end run
|
|
}//end inner class ListenThread
|
|
//=============================================//
|
|
|
|
//Inner signal generator class.
|
|
|
|
//An object of this class can be used to
|
|
// generate a variety of different synthetic
|
|
// audio signals. Each time the getSyntheticData
|
|
// method is called on an object of this class,
|
|
// the method will fill the incoming array with
|
|
// the samples for a synthetic signal.
|
|
class SynGen{
|
|
//Note: Because this class uses a ByteBuffer
|
|
// asShortBuffer to handle the data, it can
|
|
// only be used to generate signed 16-bit
|
|
// data.
|
|
ByteBuffer byteBuffer;
|
|
ShortBuffer shortBuffer;
|
|
int byteLength;
|
|
|
|
void getSyntheticData(byte[] synDataBuffer){
|
|
//Prepare the ByteBuffer and the shortBuffer
|
|
// for use
|
|
byteBuffer = ByteBuffer.wrap(synDataBuffer);
|
|
shortBuffer = byteBuffer.asShortBuffer();
|
|
|
|
byteLength = synDataBuffer.length;
|
|
|
|
//Decide which synthetic data generator
|
|
// method to invoke based on which radio
|
|
// button the user selected in the Center of
|
|
// the GUI. If you add more methods for
|
|
// other synthetic data types, you need to
|
|
// add corresponding radio buttons to the
|
|
// GUI and add statements here to test the
|
|
// new radio buttons. Make additions here
|
|
// if you add new synthetic generator
|
|
// methods.
|
|
|
|
if(tones.isSelected()) tones();
|
|
if(stereoPanning.isSelected()) stereoPanning();
|
|
if(stereoPingpong.isSelected()) stereoPingpong();
|
|
if(fmSweep.isSelected()) fmSweep();
|
|
if(decayPulse.isSelected()) decayPulse();
|
|
if(echoPulse.isSelected()) echoPulse();
|
|
if(waWaPulse.isSelected()) waWaPulse();
|
|
|
|
}//end getSyntheticData method
|
|
//-------------------------------------------//
|
|
|
|
//This method generates a monaural tone
|
|
// consisting of the sum of three sinusoids.
|
|
void tones(){
|
|
|
|
//-----------------//
|
|
//code to generate the tone and then write it to an .au file
|
|
channels = 1;//Java allows 1 or 2
|
|
//Each channel requires two 8-bit bytes per
|
|
// 16-bit sample.
|
|
int bytesPerSamp = 2;
|
|
sampleRate = 16000.0F;
|
|
// Allowable 8000,11025,16000,22050,44100
|
|
int sampLength = byteLength/bytesPerSamp;
|
|
sampLength = 5000;
|
|
for(int cnt = 0; cnt < sampLength; cnt++){
|
|
double time = cnt/sampleRate;
|
|
//double freq = 440.0;//arbitrary frequency
|
|
double sinValue = Math.sin(2*Math.PI*frequency*time);
|
|
shortBuffer.put((short)(16000*sinValue));
|
|
}//end for loop
|
|
playOrFileData();
|
|
}//end method tones
|
|
//-------------------------------------------//
|
|
|
|
//This method generates a stereo speaker sweep,
|
|
// starting with a relatively high frequency
|
|
// tone on the left speaker and moving across
|
|
// to a lower frequency tone on the right
|
|
// speaker.
|
|
void stereoPanning(){
|
|
channels = 2;//Java allows 1 or 2
|
|
int bytesPerSamp = 4;//Based on channels
|
|
sampleRate = 16000.0F;
|
|
// Allowable 8000,11025,16000,22050,44100
|
|
int sampLength = byteLength/bytesPerSamp;
|
|
for(int cnt = 0; cnt < sampLength; cnt++){
|
|
//Calculate time-varying gain for each
|
|
// speaker
|
|
double rightGain = 16000.0*cnt/sampLength;
|
|
double leftGain = 16000.0 - rightGain;
|
|
|
|
double time = cnt/sampleRate;
|
|
double freq = 600;//An arbitrary frequency
|
|
//Generate data for left speaker
|
|
double sinValue =
|
|
Math.sin(2*Math.PI*(freq)*time);
|
|
shortBuffer.put(
|
|
(short)(leftGain*sinValue));
|
|
//Generate data for right speaker
|
|
sinValue =
|
|
Math.sin(2*Math.PI*(freq*0.8)*time);
|
|
shortBuffer.put(
|
|
(short)(rightGain*sinValue));
|
|
}//end for loop
|
|
}//end method stereoPanning
|
|
//-------------------------------------------//
|
|
|
|
//This method uses stereo to switch a sound
|
|
// back and forth between the left and right
|
|
// speakers at a rate of about eight switches
|
|
// per second. On my system, this is a much
|
|
// better demonstration of the sound separation
|
|
// between the two speakers than is the
|
|
// demonstration produced by the stereoPanning
|
|
// method. Note also that because the sounds
|
|
// are at different frequencies, the sound
|
|
// produced is similar to that of U.S.
|
|
// emergency vehicles.
|
|
|
|
void stereoPingpong(){
|
|
channels = 2;//Java allows 1 or 2
|
|
int bytesPerSamp = 4;//Based on channels
|
|
sampleRate = 16000.0F;
|
|
// Allowable 8000,11025,16000,22050,44100
|
|
int sampLength = byteLength/bytesPerSamp;
|
|
double leftGain = 0.0;
|
|
double rightGain = 16000.0;
|
|
for(int cnt = 0; cnt < sampLength; cnt++){
|
|
//Calculate time-varying gain for each
|
|
// speaker
|
|
if(cnt % (sampLength/8) == 0){
|
|
//swap gain values
|
|
double temp = leftGain;
|
|
leftGain = rightGain;
|
|
rightGain = temp;
|
|
}//end if
|
|
|
|
double time = cnt/sampleRate;
|
|
double freq = 600;//An arbitrary frequency
|
|
//Generate data for left speaker
|
|
double sinValue =
|
|
Math.sin(2*Math.PI*(freq)*time);
|
|
shortBuffer.put(
|
|
(short)(leftGain*sinValue));
|
|
//Generate data for right speaker
|
|
sinValue =
|
|
Math.sin(2*Math.PI*(freq*0.8)*time);
|
|
shortBuffer.put(
|
|
(short)(rightGain*sinValue));
|
|
}//end for loop
|
|
}//end stereoPingpong method
|
|
//-------------------------------------------//
|
|
|
|
//This method generates a monaural linear
|
|
// frequency sweep from 100 Hz to 1000Hz.
|
|
void fmSweep(){
|
|
channels = 1;//Java allows 1 or 2
|
|
int bytesPerSamp = 2;//Based on channels
|
|
sampleRate = 16000.0F;
|
|
// Allowable 8000,11025,16000,22050,44100
|
|
int sampLength = byteLength/bytesPerSamp;
|
|
double lowFreq = 100.0;
|
|
double highFreq = 1000.0;
|
|
|
|
for(int cnt = 0; cnt < sampLength; cnt++){
|
|
double time = cnt/sampleRate;
|
|
|
|
double freq = lowFreq +
|
|
cnt*(highFreq-lowFreq)/sampLength;
|
|
double sinValue =
|
|
Math.sin(2*Math.PI*freq*time);
|
|
shortBuffer.put((short)(16000*sinValue));
|
|
}//end for loop
|
|
}//end method fmSweep
|
|
//-------------------------------------------//
|
|
|
|
//This method generates a monaural triple-
|
|
// frequency pulse that decays in a linear
|
|
// fashion with time.
|
|
void decayPulse(){
|
|
channels = 1;//Java allows 1 or 2
|
|
int bytesPerSamp = 2;//Based on channels
|
|
sampleRate = 16000.0F;
|
|
// Allowable 8000,11025,16000,22050,44100
|
|
int sampLength = byteLength/bytesPerSamp;
|
|
for(int cnt = 0; cnt < sampLength; cnt++){
|
|
//The value of scale controls the rate of
|
|
// decay - large scale, fast decay.
|
|
double scale = 2*cnt;
|
|
if(scale > sampLength) scale = sampLength;
|
|
double gain =
|
|
16000*(sampLength-scale)/sampLength;
|
|
double time = cnt/sampleRate;
|
|
double freq = 499.0;//an arbitrary freq
|
|
double sinValue =
|
|
(Math.sin(2*Math.PI*freq*time) +
|
|
Math.sin(2*Math.PI*(freq/1.8)*time) +
|
|
Math.sin(2*Math.PI*(freq/1.5)*time))/3.0;
|
|
shortBuffer.put((short)(gain*sinValue));
|
|
}//end for loop
|
|
}//end method decayPulse
|
|
//-------------------------------------------//
|
|
|
|
//This method generates a monaural triple-
|
|
// frequency pulse that decays in a linear
|
|
// fashion with time. However, three echoes
|
|
// can be heard over time with the amplitude
|
|
// of the echoes also decreasing with time.
|
|
void echoPulse(){
|
|
channels = 1;//Java allows 1 or 2
|
|
int bytesPerSamp = 2;//Based on channels
|
|
sampleRate = 16000.0F;
|
|
// Allowable 8000,11025,16000,22050,44100
|
|
int sampLength = byteLength/bytesPerSamp;
|
|
int cnt2 = -8000;
|
|
int cnt3 = -16000;
|
|
int cnt4 = -24000;
|
|
for(int cnt1 = 0; cnt1 < sampLength;
|
|
cnt1++,cnt2++,cnt3++,cnt4++){
|
|
double val = echoPulseHelper(
|
|
cnt1,sampLength);
|
|
if(cnt2 > 0){
|
|
val += 0.7 * echoPulseHelper(
|
|
cnt2,sampLength);
|
|
}//end if
|
|
if(cnt3 > 0){
|
|
val += 0.49 * echoPulseHelper(
|
|
cnt3,sampLength);
|
|
}//end if
|
|
if(cnt4 > 0){
|
|
val += 0.34 * echoPulseHelper(
|
|
cnt4,sampLength);
|
|
}//end if
|
|
|
|
shortBuffer.put((short)val);
|
|
}//end for loop
|
|
}//end method echoPulse
|
|
//-------------------------------------------//
|
|
|
|
double echoPulseHelper(int cnt,int sampLength){
|
|
//The value of scale controls the rate of
|
|
// decay - large scale, fast decay.
|
|
double scale = 2*cnt;
|
|
if(scale > sampLength) scale = sampLength;
|
|
double gain =
|
|
16000*(sampLength-scale)/sampLength;
|
|
double time = cnt/sampleRate;
|
|
double freq = 499.0;//an arbitrary freq
|
|
double sinValue =
|
|
(Math.sin(2*Math.PI*freq*time) +
|
|
Math.sin(2*Math.PI*(freq/1.8)*time) +
|
|
Math.sin(2*Math.PI*(freq/1.5)*time))/3.0;
|
|
return(short)(gain*sinValue);
|
|
}//end echoPulseHelper
|
|
|
|
//-------------------------------------------//
|
|
|
|
//This method generates a monaural triple-
|
|
// frequency pulse that decays in a linear
|
|
// fashion with time. However, three echoes
|
|
// can be heard over time with the amplitude
|
|
// of the echoes also decreasing with time.
|
|
//Note that this method is identical to the
|
|
// method named echoPulse, except that the
|
|
// algebraic sign was switched on the amplitude
|
|
// of two of the echoes before adding them to
|
|
// the composite synthetic signal. This
|
|
// resulted in a difference in the
|
|
// sound.
|
|
void waWaPulse(){
|
|
channels = 1;//Java allows 1 or 2
|
|
int bytesPerSamp = 2;//Based on channels
|
|
sampleRate = 16000.0F;
|
|
// Allowable 8000,11025,16000,22050,44100
|
|
int sampLength = byteLength/bytesPerSamp;
|
|
int cnt2 = -8000;
|
|
int cnt3 = -16000;
|
|
int cnt4 = -24000;
|
|
for(int cnt1 = 0; cnt1 < sampLength;
|
|
cnt1++,cnt2++,cnt3++,cnt4++){
|
|
double val = waWaPulseHelper(
|
|
cnt1,sampLength);
|
|
if(cnt2 > 0){
|
|
val += -0.7 * waWaPulseHelper(
|
|
cnt2,sampLength);
|
|
}//end if
|
|
if(cnt3 > 0){
|
|
val += 0.49 * waWaPulseHelper(
|
|
cnt3,sampLength);
|
|
}//end if
|
|
if(cnt4 > 0){
|
|
val += -0.34 * waWaPulseHelper(
|
|
cnt4,sampLength);
|
|
}//end if
|
|
|
|
shortBuffer.put((short)val);
|
|
}//end for loop
|
|
}//end method waWaPulse
|
|
//-------------------------------------------//
|
|
|
|
double waWaPulseHelper(int cnt,int sampLength){
|
|
//The value of scale controls the rate of
|
|
// decay - large scale, fast decay.
|
|
double scale = 2*cnt;
|
|
if(scale > sampLength) scale = sampLength;
|
|
double gain =
|
|
16000*(sampLength-scale)/sampLength;
|
|
double time = cnt/sampleRate;
|
|
double freq = 499.0;//an arbitrary freq
|
|
double sinValue =
|
|
(Math.sin(2*Math.PI*freq*time) +
|
|
Math.sin(2*Math.PI*(freq/1.8)*time) +
|
|
Math.sin(2*Math.PI*(freq/1.5)*time))/3.0;
|
|
return(short)(gain*sinValue);
|
|
}//end waWaPulseHelper
|
|
|
|
//-------------------------------------------//
|
|
}//end SynGen class
|
|
//=============================================//
|
|
|
|
}//end outer class AudioSynth01.java
|
|
|
|
|