This is a special case, good for testing pan-stereo effects in your code. Here you don't nead access to a sound file or line In.This simplified technique generate a sin wave at a frequency of 500Hz on left cannel and 1KHz on right cannel at a samplerate of 44100 samples/second. You can easily change these settings to whatever you like.
First we construct our Stereo Oscillator
import java.util.Random;
public class StereoOscillator
{
public static final int NOTYPE= 0;
public static final int NOISE= 1;
public static final int SINEWAVE= 2;
public static final int TRIANGLEWAVE= 3;
public static final int SQUAREWAVE = 4;
private int frequencyL;
private int frequencyR;
private int posL;
private int posR;
private boolean toggle;
private double leftAmplitudeAdj;
private double rightAmplitudeAdj;
private int type;
private int sampleRate;
private int numberOfChannels;
private int pos;
private short [] waveTable;
private short [] tmp = new short[1];
public StereoOscillator(int type, int frequencyL, int frequencyR, int sampleRate)
{
this.type = type;
this.sampleRate = sampleRate;
this.frequencyL = frequencyL;
this.frequencyR = frequencyR;
this.toggle = false;
this.leftAmplitudeAdj = 1.0;
this.rightAmplitudeAdj = 1.0;
posL = posR = 0;
buildWaveTable();
}
public StereoOscillator(int frequencyL, int frequencyR, int sampleRate)
{
this(SINEWAVE, frequencyL, frequencyR, sampleRate);
}
public StereoOscillator(int frequencyL, int frequencyR)
{
this(SINEWAVE, frequencyL, frequencyR, 44100);
}
public StereoOscillator(int sampleRate)
{
this(SINEWAVE, 500, 1000, sampleRate);
}
public StereoOscillator()
{
this(SINEWAVE, 500, 1000, 44100);
}
private void buildWaveTable()
{
if (type == NOTYPE)
return;
// Initialize waveTable index as wave table is changing
pos = 0;
// Allocate a table for 1 cycle of waveform
waveTable = new short[sampleRate];
switch(type)
{
case NOISE:
// Create a random number generator for returning gaussian
// distributed numbers. The result is white noise.
Random random = new Random();
for (int sample = 0; sample < sampleRate; sample++)
waveTable[sample] = (short)((65535.0 * random.nextGaussian()) - 32768);
break;
case SINEWAVE:
double scale = (2.0 * Math.PI) / sampleRate;
for (int sample = 0; sample < sampleRate; sample++)
waveTable[sample] = (short)(32767.0 * Math.sin(sample * scale));
break;
case TRIANGLEWAVE:
double sign = 1.0;
double value = 0.0;
int oneQuarterWave = sampleRate / 4;
int threeQuarterWave = (3 * sampleRate) / 4;
scale = 32767.0 / oneQuarterWave;
for (int sample = 0; sample < sampleRate; sample++) {
if ((sample > oneQuarterWave) && (sample <= threeQuarterWave))
sign = -1.0;
else
sign = 1.0;
value += sign * scale;
waveTable[sample] = (short) value;
}
break;
case SQUAREWAVE:
for (int sample = 0; sample < sampleRate; sample++) {
if (sample < sampleRate / 2)
waveTable[sample] = 32767;
else
waveTable[sample] = -32768;
}
break;
}
}
public int read(byte[] buffer, int offs, int len)
{
int shLen = 2*len;
if(tmp.length != shLen)
tmp = new short[shLen];
int nrSamples = getSamples(tmp, shLen);
shortsToBytes(buffer, tmp, offs, nrSamples/2, true);
return len;
}
private void shortsToBytes(byte[] data, short[] shorts, int offset, int len, boolean isBigEndian)
{
int numberOfShorts = len/2;
/* convert the short back to byte array */
int index = offset;
for (int n=0; n < numberOfShorts; n++)
{
short s = shorts[n];
if(isBigEndian)
{
data[index++] = (byte)(s >> 8);
data[index++] = (byte)(s & 255);
}
else
{
data[index++] = (byte)(s & 255);
data[index++] = (byte)(s >> 8);
}
}
}
private int getSamples(short [] buffer, int length)
{
int sample = 0;
int count = length;
while(count-- != 0)
{
if (!toggle)
{
toggle = true;
buffer[sample++] = (short)(leftAmplitudeAdj * waveTable[posL]);
posL += frequencyL;
if (posL >= sampleRate)
posL -= sampleRate;
}
else
{
toggle = false;
buffer[sample++] = (short)(rightAmplitudeAdj * waveTable[posR]);
posR += frequencyR;
if (posR >= sampleRate)
posR -= sampleRate;
}
}
return length;
}
}
And then we create the player. We have pre-selected a renderer using javax.sound.sampled.SourceDataLine API to playback the signal.
import javax.sound.sampled.*;
.......
public void playFromOscillator()
{
try
{
int leftFreq= 500;
int rightFreq = 1000;
int sampleRate = 44100;
StereoOscillator lineIn = new StereoOscillator(leftFreq, rightFreq, sampleRate);
AudioFormat audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, sampleRate, 16, 2, 4, 44100, true);
DataLine.Info sourceLine = new DataLine.Info( SourceDataLine.class, audioFormat );
SourceDataLine lineOut = (SourceDataLine) AudioSystem.getLine(sourceLine);
int bufferSize = (int) audioFormat.getSampleRate() * audioFormat.getFrameSize();
lineOut.open(audioFormat, bufferSize);
lineOut.start();
// Create a buffer for moving data from the LineIn to the LineOut
byte [] buffer = new byte[ bufferSize ];
try
{
int bytesRead = 0;
while ( bytesRead >= 0 )
{
bytesRead = lineIn.read( buffer, 0, buffer.length );
if ( bytesRead >= 0 )
{
//Write the buffer to your monstrous speakers
lineOut.write(buffer, 0, bytesRead);
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
//Done so drain and close
lineOut.drain();
lineOut.close();
}
catch ( Exception e )
{
e.printStackTrace();
}
}