/*
* Copyright (c) 2011-2012 by Stefan Laubenberger.
*
* MusicPlayer is free software: you can redistribute it and/or modify
* it under the terms of the General Public License v2.0.
*
* MusicPlayer is distributed together with Tyr 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 General Public License for more details:
* <http://www.gnu.org/licenses>
*
* This distribution is available at:
* <http://code.google.com/p/tyr/>
* <http://dev.laubenberger.net/tyr/>
*
* Contact information:
* Stefan Laubenberger
* Bullingerstrasse 53
* CH-8004 Zuerich
*
* <http://www.laubenberger.net>
*
* <laubenberger@gmail.com>
*/
package net.laubenberger.tyr.module.musicplayer;
import java.awt.event.ActionEvent;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.SwingUtilities;
import javax.xml.bind.JAXBException;
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.advanced.AdvancedPlayer;
import javazoom.jl.player.advanced.PlaybackEvent;
import javazoom.jl.player.advanced.PlaybackListener;
import net.laubenberger.bogatyr.helper.HelperCollection;
import net.laubenberger.bogatyr.helper.HelperIO;
import net.laubenberger.bogatyr.helper.HelperLog;
import net.laubenberger.bogatyr.helper.HelperNumber;
import net.laubenberger.bogatyr.helper.HelperString;
import net.laubenberger.bogatyr.helper.HelperTime;
import net.laubenberger.bogatyr.helper.HelperXml;
import net.laubenberger.bogatyr.misc.Constants;
import net.laubenberger.bogatyr.misc.Event;
import net.laubenberger.bogatyr.model.application.ModelApplication;
import net.laubenberger.bogatyr.model.misc.Language;
import net.laubenberger.bogatyr.service.localizer.Localizer;
import net.laubenberger.bogatyr.service.property.Property;
import net.laubenberger.bogatyr.view.View;
import net.laubenberger.bogatyr.view.swing.ActionAbstract;
import net.laubenberger.bogatyr.view.swing.Dialog;
import net.laubenberger.bogatyr.view.swing.MenuItemCheckBox;
import net.laubenberger.bogatyr.view.swing.Separator;
import net.laubenberger.bogatyr.view.swing.worker.Worker;
import net.laubenberger.bogatyr.view.swing.worker.WorkerAbstract;
import net.laubenberger.tyr.controller.ModuleAbstract;
import net.laubenberger.tyr.misc.Callback;
import net.laubenberger.tyr.misc.ScalableIcon;
import net.laubenberger.tyr.model.FileType;
import net.laubenberger.tyr.model.Icon;
import net.laubenberger.tyr.model.ModuleConfig;
import net.laubenberger.tyr.model.ModuleConfigImpl;
import net.laubenberger.tyr.model.ModuleData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* MusicPlayer module
*
* @author Stefan Laubenberger
* @version 0.8.7 (20120531)
* @since 0.0.1
*/
public class MusicPlayerImpl extends ModuleAbstract implements MusicPlayer {
static final Logger log = LoggerFactory.getLogger(MusicPlayerImpl.class);
static final int FILE_BUFFER_SIZE = HelperNumber.NUMBER_131072.intValue(); //128kB (equals 8 seconds buffer@128kpbs)
private static final String KEY_DIRECTORY = "key.directory"; //$NON-NLS-1$
private static final String KEY_IS_RANDOM = "key.isRandom"; //$NON-NLS-1$
private static final String KEY_IS_ENQUEUE = "key.isEnqueue"; //$NON-NLS-1$
private static final String KEY_IS_ON_TOP = "key.isOnTop"; //$NON-NLS-1$
final ModelApplication model = getModel();
final Property property = model.getProperty();
final Localizer localizer = model.getLocalizer();
final Dialog dialogAbout;
final Collection<Dialog> dialogs = new HashSet<Dialog>();
ActionAbstract actionOpen;
ActionAbstract actionStart;
ActionAbstract actionStop;
ActionAbstract actionSkipBackward;
ActionAbstract actionSkipForward;
final Dialog commander;
boolean isRunning;
int index;
List<File> list = new ArrayList<File>();
static AdvancedPlayer player;
private boolean isExit = false;
public static void main(final String[] args) {
final ModuleConfig cm = new ModuleConfigImpl();
cm.setName("MusicPlayer"); //$NON-NLS-1$
cm.setVersion(new BigDecimal("0.87")); //$NON-NLS-1$
cm.setBuild(247);
cm.setCreated(HelperTime.getDate(2012, 5, 31, 8, 13, 0));
cm.setLanguage(Language.ENGLISH);
cm.setUUID(UUID.fromString("aa9d8103-a786-4a68-8f7c-ff1fdcebe06e")); //$NON-NLS-1$
cm.addPerson(Constants.BOGATYR.getPersons().get(0)); //hopefully it's me :-)
cm.setJars(HelperCollection.getList("tyr-module-musicplayer-0.87.jar", "lib/jlayer.jar")); //$NON-NLS-1$//$NON-NLS-2$
cm.setModuleClass("net.laubenberger.tyr.module.musicplayer.MusicPlayerImpl"); //$NON-NLS-1$
cm.setLocalizerBase("net/laubenberger/tyr/module/musicplayer/musicplayer"); //$NON-NLS-1$
cm.setLogo("net/laubenberger/tyr/module/musicplayer/icon/small/logo.png"); //$NON-NLS-1$
cm.setLogoLarge("net/laubenberger/tyr/module/musicplayer/icon/large/logo.png"); //$NON-NLS-1$
try {
cm.setUrl(new URL("http://dev.laubenberger.net/")); //$NON-NLS-1$
// cm.setUpdateLocation(new
// URL("file://User/slaubenberger/Desktop/musicplayer_update.xml"));
} catch (MalformedURLException ex) {
// should never happen!
log.error("URL invalid", ex); //$NON-NLS-1$
}
try {
HelperXml.serialize(new File(cm.getName() + FileType.CONFIGURATION.getExtension()), cm);
} catch (JAXBException ex) {
log.error("Could not write the configration file", ex); //$NON-NLS-1$
}
}
public MusicPlayerImpl(final ModuleConfig moduleConfig, final ModuleData moduleData, final Callback callback,
final ScalableIcon logo, final File dirDB) {
super(moduleConfig, moduleData, callback, logo, dirDB);
if (log.isTraceEnabled()) log.trace(HelperLog.constructor(moduleConfig, moduleData, callback, logo, dirDB));
getModuleData().addValue(KEY_IS_RANDOM,
null == getModuleData().getBoolean(KEY_IS_RANDOM) ? false : getModuleData().getBoolean(KEY_IS_RANDOM));
getModuleData().addValue(KEY_IS_ENQUEUE,
null == getModuleData().getBoolean(KEY_IS_ENQUEUE) ? false : getModuleData().getBoolean(KEY_IS_ENQUEUE));
getModuleData().addValue(KEY_IS_ON_TOP,
null == getModuleData().getBoolean(KEY_IS_ON_TOP) ? false : getModuleData().getBoolean(KEY_IS_ON_TOP));
dialogAbout = new DialogAbout(this);
commander = new DialogCommander(this);
commander.setAlwaysOnTop(getModuleData().getBoolean(KEY_IS_ON_TOP));
}
/*
* Private methods
*/
private void createLayout() {
actionOpen = new ActionOpen();
actionStart = new ActionStart();
actionStop = new ActionStop();
actionSkipBackward = new ActionSkipBackward();
actionSkipForward = new ActionSkipForward();
commander.createAndShowGUI();
enableControls();
}
void play(final File file) {
final Worker worker = new WorkerAbstract<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
fireWorkerStart();
BufferedInputStream bis = null;
try {
bis = new BufferedInputStream(new FileInputStream(file), FILE_BUFFER_SIZE);
try {
player = new AdvancedPlayer(bis);
player.setPlayBackListener(new PlaybackListener() {
@Override
public void playbackStarted(final PlaybackEvent playbackevent) {
isRunning = true;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
commander.setTitle(file.getName());
commander.getRootPane().setToolTipText(file.getAbsolutePath());
enableControls();
}
});
}
@Override
public void playbackFinished(final PlaybackEvent playbackevent) {
if (isRunning) {
index++;
if (index >= list.size()) {
index = 0;
}
player.close();
play(list.get(index));
}
}
});
player.play();
} catch (JavaLayerException e) {
log.error("Could not process MP3 file", e); //$NON-NLS-1$
}
} catch (FileNotFoundException ex) {
log.error("File not found", ex); //$NON-NLS-1$
} finally {
if (null != bis) {
try {
bis.close();
} catch (IOException ex) {
log.error("Could not close file", ex); //$NON-NLS-1$
}
}
}
return null;
}
};
worker.addListener(this);
worker.execute();
}
void stop() {
isRunning = false;
if (null != player) {
player.close();
}
enableControls();
commander.setTitle(localizer.getValue(HelperResource.RES_LABEL_STOPPED));
commander.getRootPane().setToolTipText(localizer.getValue(HelperResource.RES_LABEL_STOPPED));
}
void skipBackward() {
actionSkipBackward.setEnabled(false);
actionSkipForward.setEnabled(false);
if (null != player) {
player.close();
}
index--;
if (0 > index) {
index = list.size() - 1;
}
play(list.get(index));
}
void skipForward() {
actionSkipBackward.setEnabled(false);
actionSkipForward.setEnabled(false);
if (null != player) {
player.close();
}
index++;
if (index >= list.size()) {
index = 0;
}
play(list.get(index));
}
void enableControls() {
actionStart.setEnabled(!isRunning);
actionStop.setEnabled(isRunning);
actionSkipBackward.setEnabled(isRunning);
actionSkipForward.setEnabled(isRunning);
}
/*
* Implemented methods
*/
@Override
public void exit(final int returnCode) {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart(returnCode));
isExit = true;
if (null != player) {
player.close();
}
if (log.isDebugEnabled()) log.debug("Musicplayer says byebye!");
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit());
}
@Override
public List<Object> getMenuItems() {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart());
createLayout();
final List<Object> result = new ArrayList<Object>();
result.add(actionOpen);
result.add(new Separator());
result.add(actionStart);
result.add(actionStop);
result.add(actionSkipBackward);
result.add(actionSkipForward);
result.add(new Separator());
result.add(new MenuItemCheckBox(getModuleData().getBoolean(KEY_IS_RANDOM), new ActionRandom()));
result.add(new MenuItemCheckBox(getModuleData().getBoolean(KEY_IS_ENQUEUE), new ActionEnqueue()));
result.add(new MenuItemCheckBox(getModuleData().getBoolean(KEY_IS_ON_TOP), new ActionOnTop()));
result.add(new Separator());
result.add(new ActionAbout());
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit(result));
return result;
}
@Override
public void localeChanged(final Event<Localizer> event) {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart(event));
for (final Dialog dialog : dialogs) {
dialog.createAndShowGUI();
}
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit());
}
@Override
public void lookAndFeelChanged() {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart());
for (final JDialog dialog : dialogs) {
SwingUtilities.updateComponentTreeUI(dialog);
dialog.pack();
}
SwingUtilities.updateComponentTreeUI(commander); // TODO probably remove
commander.pack(); // TODO probably remove
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit());
}
@Override
public void fontSizeChanged() {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart());
for (final JDialog dialog : dialogs) {
dialog.pack();
}
commander.pack(); // TODO probably remove
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit());
}
@Override
public void iconSizeChanged() {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart());
for (final Dialog dialog : dialogs) {
dialog.createAndShowGUI();
}
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit());
}
@Override
public Collection<Dialog> getDialogs() {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart());
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit(dialogs));
return dialogs;
}
@Override
public void start(final Event<Worker> event) {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart(event));
model.getModelWorker().add(event.getSource());
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit());
}
@Override
public void done(final Event<Worker> event) {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart(event));
getCallback().removeWorker(event.getSource());
if (!isExit) {
model.getModelWorker().remove(event.getSource());
}
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit());
}
@Override
public View getView() {
// must not be implemented
return null;
}
@Override
public void setModel(final ModelApplication arg0) {
// must not be implemented
}
@Override
public void setView(final View arg0) {
// must not be implemented
}
@Override
public ActionAbstract getActionOpen() {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart());
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit(actionOpen));
return actionOpen;
}
@Override
public ActionAbstract getActionStart() {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart());
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit(actionStart));
return actionStart;
}
@Override
public ActionAbstract getActionStop() {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart());
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit(actionStop));
return actionStop;
}
@Override
public ActionAbstract getActionSkipBackward() {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart());
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit(actionSkipBackward));
return actionSkipBackward;
}
@Override
public ActionAbstract getActionSkipForward() {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart());
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit(actionSkipForward));
return actionSkipForward;
}
@Override
public boolean isRunning() {
if (log.isDebugEnabled()) log.debug(HelperLog.methodStart());
if (log.isDebugEnabled()) log.debug(HelperLog.methodExit(isRunning));
return isRunning;
}
/*
* Inner classes
*/
private class ActionStart extends ActionAbstract {
private static final long serialVersionUID = -1827618193627476092L;
public ActionStart() {
super(localizer.getValue(HelperResource.RES_ACTION_START), getCallback().getScaledIcon(Icons.START), localizer
.getTooltip(HelperResource.RES_ACTION_START), localizer.getMnemonic(HelperResource.RES_ACTION_START),
localizer.getAccelerator(HelperResource.RES_ACTION_START));
}
@Override
public void actionPerformed(final ActionEvent e) {
if (null != list && !list.isEmpty()) {
play(list.get(index));
}
}
}
private class ActionStop extends ActionAbstract {
private static final long serialVersionUID = -5234893885750190039L;
public ActionStop() {
super(localizer.getValue(HelperResource.RES_ACTION_STOP), getCallback().getScaledIcon(Icons.STOP), localizer
.getTooltip(HelperResource.RES_ACTION_STOP), localizer.getMnemonic(HelperResource.RES_ACTION_STOP),
localizer.getAccelerator(HelperResource.RES_ACTION_STOP));
}
@Override
public void actionPerformed(final ActionEvent e) {
stop();
}
}
private class ActionSkipBackward extends ActionAbstract {
private static final long serialVersionUID = 3457604027141642201L;
public ActionSkipBackward() {
super(localizer.getValue(HelperResource.RES_ACTION_SKIP_BACKWARD), getCallback().getScaledIcon(
Icons.SKIP_BACKWARD), localizer.getTooltip(HelperResource.RES_ACTION_SKIP_BACKWARD), localizer
.getMnemonic(HelperResource.RES_ACTION_SKIP_BACKWARD), localizer
.getAccelerator(HelperResource.RES_ACTION_SKIP_BACKWARD));
}
@Override
public void actionPerformed(final ActionEvent e) {
skipBackward();
}
}
private class ActionSkipForward extends ActionAbstract {
private static final long serialVersionUID = -7641175548721760165L;
public ActionSkipForward() {
super(localizer.getValue(HelperResource.RES_ACTION_SKIP_FORWARD), getCallback().getScaledIcon(
Icons.SKIP_FORWARD), localizer.getTooltip(HelperResource.RES_ACTION_SKIP_FORWARD), localizer
.getMnemonic(HelperResource.RES_ACTION_SKIP_FORWARD), localizer
.getAccelerator(HelperResource.RES_ACTION_SKIP_FORWARD));
}
@Override
public void actionPerformed(final ActionEvent e) {
skipForward();
}
}
private class ActionOpen extends ActionAbstract {
private static final long serialVersionUID = -866755820221487028L;
public ActionOpen() {
super(localizer.getValue(HelperResource.RES_ACTION_OPEN), getCallback().getScaledIcon(Icons.OPEN), localizer
.getTooltip(HelperResource.RES_ACTION_OPEN), localizer.getMnemonic(HelperResource.RES_ACTION_OPEN),
localizer.getAccelerator(HelperResource.RES_ACTION_OPEN));
}
@Override
public void actionPerformed(final ActionEvent e) {
getCallback().setPopupEnabled(false);
final JFileChooser fc = new JFileChooser(getModuleData().getFile(KEY_DIRECTORY));
fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
fc.setAcceptAllFileFilterUsed(false);
fc.addChoosableFileFilter(new javax.swing.filechooser.FileFilter() {
@Override
public boolean accept(final File file) {
return file.isDirectory() || HelperString.endsWith(file.getName(), ".mp3"); //$NON-NLS-1$
}
@Override
public String getDescription() {
return localizer.getValue(HelperResource.RES_LABEL_MP3_FILES);
}
});
if (JFileChooser.APPROVE_OPTION == fc.showOpenDialog(getOwner())) {
actionOpen.setEnabled(false);
actionStart.setEnabled(false);
actionStop.setEnabled(false);
actionSkipBackward.setEnabled(false);
actionSkipForward.setEnabled(false);
final File file = fc.getSelectedFile();
if (file.isDirectory()) {
final Worker worker = new WorkerAbstract<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
fireWorkerStart();
final FileFilter filter = new FileFilter() {
@Override
public boolean accept(final File file) {
return HelperString.endsWith(file.getName(), ".mp3"); //$NON-NLS-1$
}
};
if (isRunning) {
stop();
}
if (getModuleData().getBoolean(KEY_IS_ENQUEUE)) {
list.addAll(HelperIO.getFiles(file, filter));
} else {
list = HelperIO.getFiles(file, filter);
index = 0;
}
if (getModuleData().getBoolean(KEY_IS_RANDOM)) {
Collections.shuffle(list);
} else {
Collections.sort(list);
}
return null;
}
@Override
protected void done() {
actionOpen.setEnabled(true);
enableControls();
super.done();
}
};
worker.addListener(MusicPlayerImpl.this);
// Thread thread = new Thread(worker);
// thread.start();
worker.execute();
getCallback().addWorker(worker, localizer.getValue(HelperResource.RES_ACTION_START),
localizer.getValue(HelperResource.RES_LABEL_SCANNING), Icons.START);
} else {
if (isRunning) {
stop();
}
if (getModuleData().getBoolean(KEY_IS_ENQUEUE)) {
list.add(file);
} else {
list = HelperCollection.getList(file);
index = 0;
}
if (getModuleData().getBoolean(KEY_IS_RANDOM)) {
Collections.shuffle(list);
} else {
Collections.sort(list);
}
actionOpen.setEnabled(true);
enableControls();
}
// actionStart.setEnabled(!isRunning);
getModuleData().addValue(KEY_DIRECTORY, fc.getSelectedFile().getParentFile());
// } else {
// System.err.println("No file selected");
}
getCallback().setPopupEnabled(true);
}
}
private class ActionRandom extends ActionAbstract {
private static final long serialVersionUID = -5950982112296104265L;
public ActionRandom() {
super(localizer.getValue(HelperResource.RES_ACTION_RANDOM), getCallback().getScaledIcon(Icons.RANDOM),
localizer.getTooltip(HelperResource.RES_ACTION_RANDOM), localizer
.getMnemonic(HelperResource.RES_ACTION_RANDOM), localizer
.getAccelerator(HelperResource.RES_ACTION_RANDOM));
}
@Override
public void actionPerformed(final ActionEvent e) {
getModuleData().addValue(KEY_IS_RANDOM, !getModuleData().getBoolean(KEY_IS_RANDOM));
if (null != list && !list.isEmpty()) {
final File current = list.get(index);
if (getModuleData().getBoolean(KEY_IS_RANDOM)) {
Collections.shuffle(list);
} else {
Collections.sort(list);
}
index = list.indexOf(current);
}
}
}
private class ActionEnqueue extends ActionAbstract {
private static final long serialVersionUID = 5562060994350997702L;
public ActionEnqueue() {
super(localizer.getValue(HelperResource.RES_ACTION_ENQUEUE), getCallback().getScaledIcon(Icons.ENQUEUE),
localizer.getTooltip(HelperResource.RES_ACTION_ENQUEUE), localizer
.getMnemonic(HelperResource.RES_ACTION_ENQUEUE), localizer
.getAccelerator(HelperResource.RES_ACTION_ENQUEUE));
}
@Override
public void actionPerformed(final ActionEvent e) {
getModuleData().addValue(KEY_IS_ENQUEUE, !getModuleData().getBoolean(KEY_IS_ENQUEUE));
}
}
private class ActionOnTop extends ActionAbstract {
private static final long serialVersionUID = 7816358890755776190L;
public ActionOnTop() {
super(localizer.getValue(HelperResource.RES_ACTION_ON_TOP), getCallback().getScaledIcon(Icons.ON_TOP),
localizer.getTooltip(HelperResource.RES_ACTION_ON_TOP), localizer
.getMnemonic(HelperResource.RES_ACTION_ON_TOP), localizer
.getAccelerator(HelperResource.RES_ACTION_ON_TOP));
}
@Override
public void actionPerformed(final ActionEvent e) {
getModuleData().addValue(KEY_IS_ON_TOP, !getModuleData().getBoolean(KEY_IS_ON_TOP));
commander.setAlwaysOnTop(getModuleData().getBoolean(KEY_IS_ON_TOP));
}
}
private class ActionAbout extends ActionAbstract {
private static final long serialVersionUID = 5531234662723584055L;
public ActionAbout() {
super(localizer.getValue(HelperResource.RES_ACTION_ABOUT), getCallback().getScaledIcon(Icon.ABOUT), localizer
.getTooltip(HelperResource.RES_ACTION_ABOUT), localizer.getMnemonic(HelperResource.RES_ACTION_ABOUT),
localizer.getAccelerator(HelperResource.RES_ACTION_ABOUT));
}
@Override
public void actionPerformed(final ActionEvent e) {
dialogAbout.createAndShowGUI();
}
}
}