import javax.sound.sampled.*;
import javax.sound.sampled.DataLine.Info;
/**
* Will create a new thread and play packets added to the ring buffer and set as ready
* @author bencall
*
*/
public class PCMPlayer extends Thread{
private AudioFormat audioFormat;
private Info info;
private SourceDataLine dataLine;
private AudioSession session;
private volatile long fix_volume = 0x10000;
private short rand_a, rand_b;
private AudioBuffer audioBuf;
private boolean stopThread = false;
public PCMPlayer(AudioSession session, AudioBuffer audioBuf){
super();
this.session = session;
this.audioBuf = audioBuf;
try {
audioFormat = new AudioFormat(44100, 16, 2, true, true);
info = new DataLine.Info(SourceDataLine.class, audioFormat);
dataLine = (SourceDataLine) AudioSystem.getLine(info);
dataLine.open(audioFormat);
dataLine.start();
} catch (LineUnavailableException e) {
e.printStackTrace();
}
}
public void run(){
boolean fin = stopThread;
while(!fin){
int[] buf = audioBuf.getNextFrame();
if(buf==null){
continue;
}
int[] outbuf = new int[session.OUTFRAME_BYTES()];
int k = stuff_buffer(session.getFilter().bf_playback_rate, buf, outbuf);
byte[] input = new byte[outbuf.length*2];
int j = 0;
for(int i=0; i<outbuf.length; i++){
input[j++] = (byte)(outbuf[i] >> 8);
input[j++] = (byte)(outbuf[i]);
}
dataLine.write(input, 0, k*4);
// Stop
synchronized(this) {
Thread.yield();
fin = this.stopThread;
}
}
}
public synchronized void stopThread(){
this.stopThread = true;
}
private int stuff_buffer(double playback_rate, int[] input, int[] output) {
int stuffsamp = session.getFrameSize();
int stuff = 0;
double p_stuff;
p_stuff = 1.0 - Math.pow(1.0 - Math.abs(playback_rate-1.0), session.getFrameSize());
if (Math.random() < p_stuff) {
stuff = playback_rate > 1.0 ? -1 : 1;
stuffsamp = (int) (Math.random() * (session.getFrameSize() - 2));
}
int j=0;
int l=0;
for (int i=0; i<stuffsamp; i++) { // the whole frame, if no stuffing
output[j++] = dithered_vol(input[l++]);
output[j++] = dithered_vol(input[l++]);
}
if (stuff!=0) {
if (stuff==1) {
// interpolate one sample
output[j++] = dithered_vol(((int)input[l-2] + (int)input[l]) >> 1);
output[j++] = dithered_vol(((int)input[l-1] + (int)input[l+1]) >> 1);
} else if (stuff==-1) {
l-=2;
}
for (int i=stuffsamp; i<session.getFrameSize() + stuff; i++) {
output[j++] = dithered_vol(input[l++]);
output[j++] = dithered_vol(input[l++]);
}
}
return session.getFrameSize() + stuff;
}
public void setVolume(double vol){
fix_volume = (long)vol;
}
private short dithered_vol(int sample) {
long out;
rand_b = rand_a;
rand_a = (short) (Math.random() * 65535);
out = (long)sample * fix_volume;
if (fix_volume < 0x10000) {
out += rand_a;
out -= rand_b;
}
return (short) (out>>16);
}
}