/* * Copyright 2004-2010 Information & Software Engineering Group (188/1) * Institute of Software Technology and Interactive Systems * Vienna University of Technology, Austria * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.ifs.tuwien.ac.at/dm/somtoolbox/license.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package at.tuwien.ifs.somtoolbox.apps.viewer.controls.player; import java.awt.Component; import java.awt.Font; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; import java.util.Vector; import javax.swing.AbstractListModel; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.ListCellRenderer; import javax.swing.ListSelectionModel; import at.tuwien.ifs.somtoolbox.apps.viewer.CommonSOMViewerStateData; import at.tuwien.ifs.somtoolbox.data.metadata.AudioVectorMetaData; public class PlayList implements PlayerListener { private static final long serialVersionUID = 1L; private List<AudioVectorMetaData> plContent; private PlayListModel lm = null; private int currentSong; private boolean isPlaying = false; private AudioPlayer player; private Vector<PlayerListener> listeners; private Vector<PlayListListener> plListeners; private NotificationPostponer pllNP; private boolean showFilenames = false; public PlayList(AudioPlayer player) { listeners = new Vector<PlayerListener>(); plListeners = new Vector<PlayListListener>(); plContent = new ArrayList<AudioVectorMetaData>(); player.addMP3PlayerListener(this); this.player = player; } public PlayList() { this(new AudioPlayer()); } public boolean addAllSongs(List<String> songs) { List<File> songfiles = new ArrayList<File>(); for (String song : songs) { songfiles.add(new File(CommonSOMViewerStateData.fileNamePrefix + song + CommonSOMViewerStateData.fileNameSuffix)); } return addAllSongs(songs, songfiles); } public boolean addAllSongs(List<String> songs, List<File> songfiles) { // System.out.println("addAllSongs"); boolean e = true; for (File file : songfiles) { try { e = plContent.add(AudioVectorMetaData.createMetaData(file)) & e; } catch (FileNotFoundException e1) { e = false; } } if (lm != null) { lm.fireContentsChanged(this, 0, plContent.size()); } informPlayListListeners(); return e; } public boolean addSong(String song, File songfile) { // System.out.println("addSong"); boolean e; try { e = plContent.add(AudioVectorMetaData.createMetaData(song, songfile)); } catch (FileNotFoundException e1) { e = false; } if (lm != null) { lm.fireContentsChanged(this, 0, plContent.size()); } informPlayListListeners(); return e; } public boolean addSong(String song) { String path = CommonSOMViewerStateData.fileNamePrefix + song + CommonSOMViewerStateData.fileNameSuffix; return addSong(song, new File(path)); } public void addSong(String song, File songfile, int position) { try { plContent.add(position, AudioVectorMetaData.createMetaData(songfile)); if (lm != null) { lm.fireContentsChanged(this, 0, plContent.size()); } informPlayListListeners(); } catch (FileNotFoundException e) { } } public void addSong(String song, int position) { String path = CommonSOMViewerStateData.fileNamePrefix + song + CommonSOMViewerStateData.fileNameSuffix; addSong(song, new File(path), position); } public List<AudioVectorMetaData> getPlayListItems() { return plContent; } public List<File> getSongs() { List<File> fList = new ArrayList<File>(); for (AudioVectorMetaData md : plContent) { fList.add(md.getAudioFile()); } return fList; } public AudioVectorMetaData getPlayListItem(int index) { if (plContent.size() == 0) { return null; } return plContent.get(index); } public List<String> getDataItems() { List<String> sList = new ArrayList<String>(); for (AudioVectorMetaData md : plContent) { sList.add(md.getID()); } return sList; } public int getIndexOf(final String id) { for (int i = 0; i < plContent.size(); i++) { if (id.equals(plContent.get(i).getID())) { return i; } } return 0; } public AudioVectorMetaData removeSong(int index) { AudioVectorMetaData f = plContent.remove(index); if (lm != null) { lm.fireContentsChanged(this, 0, plContent.size()); } informPlayListListeners(); return f; } public List<AudioVectorMetaData> remove(int from, int to) { List<AudioVectorMetaData> sl = plContent.subList(from, to); plContent.removeAll(sl); if (lm != null) { lm.fireContentsChanged(this, 0, plContent.size()); } informPlayListListeners(); return sl; } public void clearPlaylist() { stop(); currentSong = 0; plContent.clear(); if (lm != null) { lm.fireContentsChanged(this, 0, plContent.size()); } informPlayListListeners(); } public void next() { skip(1); } public void prev() { skip(-1); } @SuppressWarnings("unused") private boolean moreToPlay() { return currentSong > plContent.size() - 2; } /** * @param count How many songs to skip * @return <c>true</c> if there was a song to skip to, <c>false</c> otherwise. */ public boolean skip(int count) { boolean result = false; currentSong += count; if (currentSong < 0 || currentSong > plContent.size() - 1) { currentSong = Math.min(Math.max(currentSong, 0), plContent.size() - 1); isPlaying = false; player.stop(); } else { if (isPlaying) { player.play(plContent.get(currentSong)); result = true; } else { player.stop(); } } if (lm != null) { lm.fireContentsChanged(this, 0, plContent.size()); } return result; } @Override public void playStarted(int mode, AudioVectorMetaData song) { // System.out.println("Playing " + title); for (PlayerListener pl : listeners) { pl.playStarted(mode, song); } } @Override public void playStopped(int reason, AudioVectorMetaData song) { // System.out.println("Finished " + title); boolean sendNotification = false; if (reason == PlayerListener.STOP_REASON_ENDED) { // If song ended and there is more in the PL, we do not send notifications sendNotification = !skip(1); } else { sendNotification = !isPlaying; } // System.out.printf("R: %d, T: %s, isPlaying:%b, sentNotify:%b%n", reason, title, isPlaying, sendNotification); if (sendNotification) { for (PlayerListener pl : listeners) { pl.playStopped(reason, song); } } } public void play(int selectedIndex) { currentSong = Math.min(Math.max(selectedIndex, 0), plContent.size() - 1); if (lm != null) { lm.fireContentsChanged(this, 0, plContent.size()); } play(); } public void play() { if (currentSong < 0 || currentSong >= plContent.size()) { return; } isPlaying = true; player.play(plContent.get(currentSong)); } public void stop() { isPlaying = false; player.stop(); } public void addPlayerListener(PlayerListener l) { listeners.add(l); } public void removePlayerListener(PlayerListener l) { listeners.remove(l); } public void addPlayListListener(PlayListListener pll) { plListeners.add(pll); } public void removePlayListListener(PlayListListener pll) { plListeners.remove(pll); } private void informPlayListListeners() { if (pllNP == null) { pllNP = new NotificationPostponer(); } if (pllNP.isAlive()) { pllNP.postpone(); } else { pllNP = new NotificationPostponer(); pllNP.start(); } } public int getCurrentSongIndex() { return currentSong; } public JList createMatchingJList(boolean addDefaultListeners) { if (lm == null) { lm = new PlayListModel(); } final JList list = new JList(); list.setModel(lm); list.setCellRenderer(new PlayListCellRenderer()); if (addDefaultListeners) { list.addKeyListener(new KeyAdapter() { @Override public void keyTyped(KeyEvent e) { if (e.getKeyChar() == '\n') { PlayList.this.play(list.getSelectedIndex()); } } }); list.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { PlayList.this.play(list.getSelectedIndex()); } } }); } list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.setVisibleRowCount(7); return list; } private final class NotificationPostponer extends Thread { private boolean doPostpone = true; private static final int WAIT = 500; // 0.5s @Override public void run() { setName(this.getClass().getSimpleName()); while (doPostpone) { doPostpone = false; try { Thread.sleep(WAIT); } catch (InterruptedException e) { } } // System.out.println("--> Notify"); for (PlayListListener pll : plListeners) { pll.playListContentChanged(); } } public void postpone() { doPostpone = true; } } private class PlayListModel extends AbstractListModel { private static final long serialVersionUID = 1L; @Override public Object getElementAt(int index) { return plContent.get(index); } @Override public int getSize() { return plContent.size(); } @Override protected void fireContentsChanged(Object source, int index0, int index1) { super.fireContentsChanged(source, index0, index1); } @Override protected void fireIntervalAdded(Object source, int index0, int index1) { super.fireIntervalAdded(source, index0, index1); } @Override protected void fireIntervalRemoved(Object source, int index0, int index1) { super.fireIntervalRemoved(source, index0, index1); } } private class PlayListCellRenderer implements ListCellRenderer { private JLabel l; public PlayListCellRenderer() { l = new JLabel(); l.setOpaque(true); new JLabel(); } @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { AudioVectorMetaData f = (AudioVectorMetaData) value; // System.err.println(index + " is " + // value.getClass().getCanonicalName()); // isSelected if (isSelected) { l.setBackground(list.getSelectionBackground()); l.setForeground(list.getSelectionForeground()); } else { l.setBackground(list.getBackground()); l.setForeground(list.getForeground()); } // isPlaying if (PlayList.this.getPlayListItem(PlayList.this.currentSong).getID().equals(f.getID())) { l.setFont(l.getFont().deriveFont(Font.BOLD)); } else { l.setFont(l.getFont().deriveFont(Font.PLAIN)); } if (showFilenames) { l.setText(f.getAudioFile().getName()); } else { l.setText(f.getDisplayLabel()); } return l; } } public boolean isPlaying() { return isPlaying; } public void setCurrentSong(int index) { currentSong = Math.min(Math.max(index, 0), plContent.size() - 1); if (lm != null) { lm.fireContentsChanged(this, 0, plContent.size()); } } }