/*
* Copyright (c) 2008, 2009, 2010, 2011 Denis Tulskiy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with this work. If not, see <http://www.gnu.org/licenses/>.
*/
package com.tulskiy.musique.audio.player;
import com.tulskiy.musique.audio.player.io.AudioOutput;
import com.tulskiy.musique.audio.player.io.Buffer;
import com.tulskiy.musique.playlist.Track;
import com.tulskiy.musique.util.AudioMath;
import javax.sound.sampled.AudioFormat;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.tulskiy.musique.audio.player.PlayerEvent.PlayerEventCode;
/**
* Author: Denis Tulskiy
* Date: 1/15/11
*/
public class PlayingThread extends Actor implements Runnable {
public final Logger logger = Logger.getLogger(getClass().getName());
private static final int BUFFER_SIZE = AudioOutput.BUFFER_SIZE;
private AudioFormat format;
private Player player;
private Buffer buffer;
private final Object lock = new Object();
private AudioOutput output = new AudioOutput();
private Track currentTrack;
private long currentByte;
private boolean active = false;
private double playbackTime;
private long playbackBytes;
public PlayingThread(Player player, Buffer buffer) {
this.player = player;
this.buffer = buffer;
}
@Override
public void process(Message message) {
switch (message) {
case PAUSE:
setState(!active);
break;
case PLAY:
setState(true);
break;
case STOP:
stop();
break;
case FLUSH:
output.flush();
break;
}
}
private void stop() {
output.flush();
setState(false);
output.close();
updatePlaybackTime();
player.fireEvent(PlayerEventCode.STOPPED);
}
private void setState(boolean newState) {
if (active != newState) {
active = newState;
synchronized (lock) {
lock.notifyAll();
}
}
}
@SuppressWarnings({"InfiniteLoopStatement"})
@Override
public void run() {
byte[] buf = new byte[BUFFER_SIZE];
while (true) {
synchronized (lock) {
try {
while (!active) {
if (output.isOpen())
player.fireEvent(PlayerEventCode.PAUSED);
output.stop();
System.gc();
lock.wait();
}
output.start();
player.fireEvent(PlayerEventCode.PLAYING_STARTED);
out : while (active) {
int len = buffer.read(buf, 0, BUFFER_SIZE);
while (len == -1) {
if (!openNext()) {
stop();
break out;
}
len = buffer.read(buf, 0, BUFFER_SIZE);
}
currentByte += len;
playbackBytes += len;
output.write(buf, 0, len);
}
} catch (Exception e) {
logger.log(Level.WARNING, "Exception while playing. Stopping now", e);
currentTrack = null;
stop();
}
}
}
}
private boolean openNext() {
try {
logger.fine("Getting next track");
Buffer.NextEntry nextEntry = buffer.pollNextTrack();
if (nextEntry.track == null) {
return false;
}
currentTrack = nextEntry.track;
if (nextEntry.forced) {
output.flush();
}
format = nextEntry.format;
output.init(format);
if (nextEntry.startSample >= 0) {
currentByte = AudioMath.samplesToBytes(nextEntry.startSample, format.getFrameSize());
player.fireEvent(PlayerEventCode.SEEK_FINISHED);
} else {
currentByte = 0;
updatePlaybackTime();
player.fireEvent(PlayerEventCode.FILE_OPENED);
}
return true;
} catch (Exception e) {
logger.log(Level.WARNING, "Could not open next track", e);
return false;
}
}
private void updatePlaybackTime() {
if (format != null) {
playbackTime = AudioMath.bytesToMillis(
playbackBytes, format);
}
playbackBytes = 0;
}
public Track getCurrentTrack() {
return currentTrack;
}
public AudioOutput getOutput() {
return output;
}
public boolean isActive() {
return active;
}
public long getCurrentSample() {
if (format != null) {
return AudioMath.bytesToSamples(currentByte, format.getFrameSize());
} else return 0;
}
public double getPlaybackTime() {
return playbackTime;
}
}