View Javadoc

1   package com.melloware.jukes.gui.tool;
2   
3   import java.io.File;
4   import java.util.Map;
5   
6   import javazoom.jlgui.basicplayer.BasicController;
7   import javazoom.jlgui.basicplayer.BasicPlayer;
8   import javazoom.jlgui.basicplayer.BasicPlayerEvent;
9   import javazoom.jlgui.basicplayer.BasicPlayerException;
10  import javazoom.jlgui.basicplayer.BasicPlayerListener;
11  
12  import org.apache.commons.logging.Log;
13  import org.apache.commons.logging.LogFactory;
14  
15  import com.jgoodies.uif.action.ActionManager;
16  
17  
18  /**
19   * This class implements a simple player based on BasicPlayer.
20   * BasicPlayer is a threaded class providing most features  of a music player.
21   * BasicPlayer works with underlying JavaSound  SPIs to support multiple audio
22   * formats. Basically JavaSound supports  WAV, AU, AIFF audio formats.
23   * Add MP3 SPI and Vorbis  SPI in your CLASSPATH to play MP3 and
24   * Ogg Vorbis file.
25   * <p>
26   * Copyright (c) 1999-2007 Melloware, Inc. <http://www.melloware.com>
27   * @author Emil A. Lefkof III <info@melloware.com>
28   * @version 4.0
29   * AZ Development 2010
30   */
31  public final class Player
32      implements BasicPlayerListener {
33  
34      private static final Log LOG = LogFactory.getLog(Player.class);
35      private static final String ERROR_PAUSE_PLAY = Resources.getString("messages.ErrorPausingPlayingFile");
36      private float volume = MainModule.SETTINGS.getPlayerVolume();
37      private JukesPlayer player;
38      private long elapsedTime = 0;
39  
40      /**
41       * Default constructor.
42       *
43       */
44      public Player() {
45      	super();
46      }
47  
48      /**
49       * Gets the player.
50       * <p>
51       * @return Returns the player.
52       */
53      public JukesPlayer getPlayer() {
54          // instantiate one if we need to
55          if (this.player == null) {
56              this.player = new JukesPlayer();
57  
58              // set buffer size to control skips
59              BasicPlayer.EXTERNAL_BUFFER_SIZE = MainModule.SETTINGS.getPlayerBufferSize();
60  
61              // Register BasicPlayerTest to BasicPlayerListener events.
62              // It means that this object will be notified on BasicPlayer
63              // events such as : opened(...), progress(...), stateUpdated(...)
64              this.player.addBasicPlayerListener(this);
65          }
66          return this.player;
67      }
68  
69      /**
70       * @return the player status
71       */
72      public int getStatus() {
73          if (player == null) {
74          	return -1;
75          } else {
76          	return getPlayer().getStatus();  
77          }
78      }
79  
80      /**
81       * Gets the volume.
82       * <p>
83       * @return Returns the volume.
84       */
85      public float getVolume() {
86          return this.volume;
87      }
88  
89      /**
90       * A handle to the BasicPlayer, plugins may control the player through
91       * the controller (play, stop, ...)
92       * <p>
93       * @param controller : a handle to the player
94       */
95      public void setController(BasicController controller) {
96          LOG.debug("setController : " + controller);
97      }
98  
99      /*
100      * (non-Javadoc)
101      *
102      * @see org.jajuk.base.IPlayerImpl#setVolume(float)
103      */
104     public void setVolume(float aVolume)
105                    throws Exception {
106         this.volume = aVolume;
107         MainModule.SETTINGS.setPlayerVolume(aVolume);
108         if (getPlayer().hasGainControl()) {
109         getPlayer().setGain(aVolume);
110         } 
111     }
112 
113     /**
114      * Fades the volume in.
115      * <p>
116      * @throws BasicPlayerException if any player error occurs
117      * @throws InterruptedException if any thread error occurs
118      */
119     public void fadeIn()
120                 throws BasicPlayerException, InterruptedException {
121         if (MainModule.SETTINGS.isFadeInOnPlay()) {
122             for (float i = 0.0f; i < this.volume; i = i + 0.01f) {
123                 getPlayer().setGain(i);
124                 Thread.sleep(25);
125             }
126         }
127     }
128 
129     /**
130      * Fades the volume out.
131      * <p>
132      * @throws BasicPlayerException if any player error occurs
133      * @throws InterruptedException if any thread error occurs
134      */
135     public void fadeOut()
136                  throws BasicPlayerException, InterruptedException {
137 
138         for (double i = this.volume; i > 0.0f; i = i - 0.01f) {
139             getPlayer().setGain(i);
140             Thread.sleep(25);
141         }
142     }
143 
144     /**
145      * Open callback, stream is ready to play.
146      *
147      * properties map includes audio format dependant features such as
148      * bitrate, duration, frequency, channels, number of frames, vbr flag, ...
149      * <p>
150      * @param stream could be File, URL or InputStream
151      * @param properties audio stream properties.
152      */
153     public void opened(Object stream, Map properties) {
154         // Pay attention to properties. It's useful to get duration,
155         // bitrate, channels, even tag such as ID3v2.
156         if (LOG.isDebugEnabled()) {
157             LOG.debug("opened : " + properties.toString());
158         }
159     }
160 
161     /**
162      * Pause if playing, play if paused.
163      */
164     public void pause() {
165         try {
166             if (getPlayer().getStatus() == BasicPlayer.PLAYING) {
167                 if (MainModule.SETTINGS.isFadeOutOnPause()) {
168                     fadeOut();
169                 }
170                 getPlayer().pause();
171             } else if (getPlayer().getStatus() == BasicPlayer.PAUSED) {
172                 getPlayer().resume();
173                 fadeIn();
174             }
175         } catch (BasicPlayerException ex) {
176             LOG.error(ERROR_PAUSE_PLAY, ex);
177         } catch (InterruptedException ex) {
178             LOG.error(ERROR_PAUSE_PLAY, ex);
179         } catch (Exception ex) {
180             LOG.error(ERROR_PAUSE_PLAY, ex);
181         }
182     }
183 
184     /**
185      * Plays the file located at filename.
186      */
187     public void play() {
188         try {
189             if (getPlayer().getStatus() == BasicPlayer.PAUSED) {
190                 getPlayer().resume();
191                 fadeIn();
192             } else if (getPlayer().getStatus() != BasicPlayer.STOPPED) { //NOPMD
193                 // make sure to stop any current player
194                 if (MainModule.SETTINGS.isFadeOutOnStop()) {
195                     fadeOut();
196                 }
197                 getPlayer().stop();
198                 getPlayer().play();
199             } else {
200                 getPlayer().play();
201             }
202         } catch (BasicPlayerException ex) {
203             LOG.error(ERROR_PAUSE_PLAY, ex);
204         } catch (InterruptedException ex) {
205             LOG.error(ERROR_PAUSE_PLAY, ex);
206         } catch (Exception ex) {
207             LOG.error(ERROR_PAUSE_PLAY, ex);
208         }
209     }
210 
211     /**
212      * Plays the file located at filename.
213      * <p>
214      * @param filename the filename to play
215      */
216     public void play(String filename) {
217         if (LOG.isDebugEnabled()) {
218             LOG.debug("Play " + filename);
219         }
220         try {
221             // instantiate one if we need to
222             if (player == null) {
223                 player = new JukesPlayer();
224 
225                 // Register BasicPlayerTest to BasicPlayerListener events.
226                 // It means that this object will be notified on BasicPlayer
227                 // events such as : opened(...), progress(...), stateUpdated(...)
228                 player.addBasicPlayerListener(this);
229             }
230 
231             // make sure to stop any current player
232             if (player.getStatus() != BasicPlayer.STOPPED) {
233                 if ((player.getStatus() == BasicPlayer.PLAYING) && (MainModule.SETTINGS.isFadeOutOnChange())) {
234                    fadeOut();
235                 }
236                 player.stop();
237             }
238             // Open file, or URL or Stream (shoutcast, icecast) to play.
239             player.open(new File(filename));
240             
241 
242             // control.open(new URL("http://yourshoutcastserver.com:8000"));
243             // Start playback in a thread. control.play();
244             // If you want to pause/resume/pause the played file then
245             // write a Swing player and just call control.pause(),
246             // control.resume() or control.stop().
247             // Use control.seek(bytesToSkip) to seek file
248             // (i.e. fast forward and rewind). seek feature will
249             // work only if underlying JavaSound SPI implements
250             // skip(...). True for MP3SPI and SUN SPI's
251             // (WAVE, AU, AIFF). // Set Volume (0 to 1.0). control.setGain(0.85);
252             // Set Pan (-1.0 to 1.0).
253             if (player.hasPanControl()) {
254             	player.setPan(0.0);
255 			}
256             if (player.hasGainControl()) {
257             	setVolume(volume);
258             }
259             player.play();
260         } catch (BasicPlayerException ex) {
261             LOG.error(Resources.getString("messages.ErrorPlayingFile"), ex);
262         } catch (InterruptedException ex) {
263             LOG.error(Resources.getString("messages.ErrorPlayingFile"), ex);
264         } catch (Exception ex) {
265             LOG.error(Resources.getString("messages.ErrorPlayingFile"), ex);
266         }
267     }
268 
269     /**
270      * Progress callback while playing.
271      *
272      * This method is called severals time per seconds while playing.
273      * properties map includes audio format features such as
274      * instant bitrate, microseconds position, current frame number, ...
275      * <p>
276      * @param bytesread from encoded stream.
277      * @param microseconds elapsed (<b>reseted after a seek !</b>).
278      * @param pcmdata PCM samples.
279      * @param properties audio stream parameters.
280      */
281     public void progress(int bytesread, long microseconds, byte[] pcmdata, Map properties) {
282         // Pay attention to properties. It depends on underlying JavaSound SPI MP3SPI provides mp3.equalizer.
283         // LOG.debug("progress : " + properties.toString());
284     	
285     	//set the elapsed time property
286     	this.elapsedTime = microseconds;
287     }
288 
289     /**
290      * Stop the currently playing song.
291      */
292     public void resume() {
293         try {
294             LOG.debug("Resume");
295             getPlayer().resume();
296         } catch (BasicPlayerException ex) {
297             LOG.error(Resources.getString("messages.ErrorResumingFile"), ex);
298         } catch (Exception ex) {
299             LOG.error(Resources.getString("messages.ErrorResumingFile"), ex);
300         }
301     }
302 
303     /**
304      * Seeks to a position in the song.
305      * <p>
306      * @param aPosition the position to seek to
307      */
308     public void seek(float aPosition) {
309         LOG.debug("Seeking");
310         try {
311             getPlayer().seek((long)aPosition);
312         } catch (BasicPlayerException ex) {
313             LOG.error(Resources.getString("messages.ErrorSeekingFile"), ex);
314         } catch (Exception ex) {
315             LOG.error(Resources.getString("messages.ErrorSeekingFile"), ex);
316         }
317     }
318 
319     /**
320      * Notification callback for basicplayer events such as opened, eom ...
321      * <p>
322      * @param event the BasicPlayerEvent fired.
323      */
324     public void stateUpdated(BasicPlayerEvent event) {
325         if (LOG.isDebugEnabled()) {
326             LOG.debug("stateUpdated : " + event.toString());
327         }
328         try {
329             switch (event.getCode()) {
330                 case BasicPlayerEvent.OPENED:
331                 case BasicPlayerEvent.PLAYING: {
332                     ActionManager.get(Actions.PLAYER_PLAY_ID).setEnabled(false);
333                     ActionManager.get(Actions.PLAYER_PAUSE_ID).setEnabled(true);
334                     ActionManager.get(Actions.PLAYER_STOP_ID).setEnabled(true);
335                     break;
336                 }
337                 case BasicPlayerEvent.RESUMED: {
338                     ActionManager.get(Actions.PLAYER_PLAY_ID).setEnabled(false);
339                     ActionManager.get(Actions.PLAYER_PAUSE_ID).setEnabled(true);
340                     ActionManager.get(Actions.PLAYER_STOP_ID).setEnabled(true);
341                     break;
342                 }
343                 case BasicPlayerEvent.PAUSED: {
344                     ActionManager.get(Actions.PLAYER_PLAY_ID).setEnabled(true);
345                     ActionManager.get(Actions.PLAYER_PAUSE_ID).setEnabled(false);
346                     ActionManager.get(Actions.PLAYER_STOP_ID).setEnabled(true);
347                     break;
348                 }
349                 case BasicPlayerEvent.STOPPED: {
350                     ActionManager.get(Actions.PLAYER_PLAY_ID).setEnabled(true);
351                     ActionManager.get(Actions.PLAYER_PAUSE_ID).setEnabled(false);
352                     ActionManager.get(Actions.PLAYER_STOP_ID).setEnabled(false);
353                     break;
354                 }
355                 default: {
356                     break;
357                 }
358             }
359         } catch (Exception ex) {
360             LOG.error("Exception", ex);
361         }
362     }
363 
364     /**
365      * Stop the currently playing song.
366      */
367     public void stop() {
368         try {
369             LOG.debug("Stop");
370             switch (getPlayer().getStatus()) {
371                 case BasicPlayer.PLAYING:
372                 case BasicPlayer.PAUSED: {
373                     if (MainModule.SETTINGS.isFadeOutOnStop()) {
374                         fadeOut();
375                     }
376                     getPlayer().stop();
377                     break;
378                 }
379                 default: {
380                     break;
381                 }
382             }
383         } catch (BasicPlayerException ex) {
384             LOG.error(Resources.getString("messages.ErrorStoppingFile"), ex);
385         } catch (InterruptedException ex) {
386             LOG.error(Resources.getString("messages.ErrorStoppingFile"), ex);
387         } catch (Exception ex) {
388             LOG.error(Resources.getString("messages.ErrorStoppingFile"), ex);
389         }
390     }
391 
392 	/**
393 	 * Gets the elapsedTime.
394 	 * <p>
395 	 * @return Returns the elapsedTime.
396 	 */
397 	public long getElapsedTime() {
398 		return (this.elapsedTime/1000);
399 	}
400  
401 }