@Override public void run() {
/* code running in separate thread */
playingUrl = null;
AudioInputStream audioInputStream = null;
SourceDataLine audioOutputLine = null;
AudioFormat audioFormat = null;
byte[] abData = new byte[(int)chunk];
for (;;) {
try {
switch (state) {
case INITIALIZING:
// we're ready to take interrupts
state = State.NOTPLAYING;
break;
case NOTPLAYING:
case PAUSED:
sleep(200);
break;
case PLAYING:
command.possiblyInterrupt();
for(;;) {
int nBytesRead = 0;
nBytesRead = audioInputStream.read(abData, 0, abData.length);
position += nBytesRead / bytesPerSecond;
command.possiblyInterrupt();
if (nBytesRead < 0) { break; }
audioOutputLine.write(abData, 0, nBytesRead); // => int nBytesWritten
command.possiblyInterrupt();
}
// end of audio, clean up
audioOutputLine.drain();
audioOutputLine.close();
audioOutputLine = null;
Utils.close(audioInputStream);
audioInputStream = null;
playingUrl = null;
state = State.NOTPLAYING;
command.possiblyInterrupt();
break;
}
} catch (InterruptedException e) {
interrupted(); // just in case we get an interrupt
State stateChange = state;
state = State.INTERRUPTED;
try {
switch (command.command()) {
case PLAY:
double offset = command.offset();
speed = command.speed();
if (playingUrl != command.url() ||
stateChange != State.PAUSED ||
offset != 0.0)
{
if (audioInputStream != null) {
Utils.close(audioInputStream);
audioInputStream = null;
}
playingUrl = command.url();
audioInputStream = AudioSystem.getAudioInputStream(playingUrl);
audioFormat = audioInputStream.getFormat();
long nBytesRead = 0;
position = 0.0;
offset -= leadIn;
double calibratedOffset = offset * calibration;
bytesPerSecond = audioFormat.getFrameRate() /* frames per second */
* audioFormat.getFrameSize() /* bytes per frame */;
if (speed * bytesPerSecond > 256000.0) {
speed = 256000 / bytesPerSecond;
}
if (calibratedOffset > 0.0) {
long bytesToSkip = (long)(
calibratedOffset /* seconds (double) */ * bytesPerSecond);
/* skip doesn't seem to want to skip big chunks, so
* reduce it to smaller ones
*/
// audioInputStream.skip(bytesToSkip);
while (bytesToSkip > chunk) {
nBytesRead = audioInputStream.skip(chunk);
if (nBytesRead <= 0)
throw new IOException(tr("This is after the end of the recording"));
bytesToSkip -= nBytesRead;
}
while (bytesToSkip > 0) {
long skippedBytes = audioInputStream.skip(bytesToSkip);
bytesToSkip -= skippedBytes;
if (skippedBytes == 0) {
// Avoid inifinite loop
Main.warn("Unable to skip bytes from audio input stream");
bytesToSkip = 0;
}
}
position = offset;
}
if (audioOutputLine != null) {
audioOutputLine.close();
}
audioFormat = new AudioFormat(audioFormat.getEncoding(),
audioFormat.getSampleRate() * (float) (speed * calibration),
audioFormat.getSampleSizeInBits(),
audioFormat.getChannels(),
audioFormat.getFrameSize(),
audioFormat.getFrameRate() * (float) (speed * calibration),
audioFormat.isBigEndian());
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
audioOutputLine = (SourceDataLine) AudioSystem.getLine(info);
audioOutputLine.open(audioFormat);
audioOutputLine.start();
}
stateChange = State.PLAYING;
break;
case PAUSE:
stateChange = State.PAUSED;