/* * Copyright (c) 2007 - 2008 by Damien Di Fede <ddf@compartmental.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as published * by the Free Software Foundation; either version 2 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package ddf.minim; import ddf.minim.spi.AudioOut; import ddf.minim.spi.AudioRecordingStream; /** * An <code>AudioPlayer</code> provides a self-contained way of playing a * sound file by streaming it from disk (or the internet). It * provides methods for playing and looping the file, as well * as methods for setting the position in the file and * looping a section of the file. You can obtain an * <code>AudioPlayer</code> by using the loadFile method of the Minim * class. * * @example Basics/PlayAFile * * @related Minim * * @author Damien Di Fede */ public class AudioPlayer extends AudioSource implements Playable { // the rec that this plays private AudioRecordingStream recording; private AudioOut output; // only set to true is pause is called private boolean isPaused; /** * Constructs an <code>AudioPlayer</code> that plays <code>recording</code> using * the <code>AudioOut</code> provided. Generally you will not call this directly * and will instead use the <code>Minim.loadFile</code> method. * * @see Minim#loadFile(String) * * @param recording * the <code>AudioRecordingStream</code> to play * * @param out the <code>AudioOut</code> to play the recording on * * @invisible */ public AudioPlayer(AudioRecordingStream recording, AudioOut out) { super(out); this.recording = recording; output = out; // output.setAudioSignal( new StreamSignal(recording, output.bufferSize()) ); output.setAudioStream(recording); } /** * Starts playback from the current position. * If this was previously set to loop, looping will be disabled. * * @shortdesc Starts playback from the current position. * * @example Basics/PlayAFile * * @related AudioPlayer */ public void play() { recording.play(); isPaused = false; } /** * Starts playback some number of milliseconds into the file. * If this was previously set to loop, looping will be disabled. * * @shortdesc Starts playback some number of milliseconds into the file. * * @param millis * int: how many milliseconds from the beginning of the file to begin playback from * * @related AudioPlayer */ public void play(int millis) { cue(millis); play(); } /** * Pauses playback. * * @example AudioPlayer/pause * * @related AudioPlayer */ public void pause() { recording.pause(); isPaused = true; } /** * Rewinds to the beginning. This <i>does not</i> stop playback. * * @example AudioPlayer/rewind * * @related AudioPlayer */ public void rewind() { cue(0); } /** * Set the <code>AudioPlayer</code> to loop some number of times. * If it is already playing, the position * <i>will not</i> be reset to the beginning. * If it is not playing, it will start playing. * If you previously called this method and then paused the * <code>AudioPlayer</code>, you can resume looping * by using the result of <code>getLoopCount()</code> as * the argument for this method. * To loop indefinitely, use <code>loop()</code>. * * @shortdesc Set the <code>AudioPlayer</code> to loop some number of times. * * @param num * int: the number of times to loop * * @example AudioPlayer/loopNum * * @related AudioPlayer */ public void loop(int num) { // if we were paused, we need to grab the current state // because calling loop totally resets it if ( isPaused ) { int pos = recording.getMillisecondPosition(); recording.loop( num ); recording.setMillisecondPosition(pos); } else { recording.loop(num); } isPaused = false; } /** * Sets the <code>AudioPlayer</code> to loop indefinitely. * If it is already playing, the position * <i>will not</i> be reset to the beginning. * If it is not playing, it will start playing. * * @shortdesc Sets the <code>AudioPlayer</code> to loop indefinitely. * * @example AudioPlayer/loop * * @related AudioPlayer */ public void loop() { loop(Minim.LOOP_CONTINUOUSLY); } /** * Returns the number of loops left to do. * * @return int: the number of loops left * * @example AudioPlayer/loopNum * * @related AudioPlayer */ public int loopCount() { return recording.getLoopCount(); } /** * Returns the length of the sound in milliseconds. If for any reason the * length could not be determined, this will return -1. However, an unknown * length should not impact playback. * * @shortdesc Returns the length of the sound in milliseconds. * * @return int: the length of the sound in milliseconds * * @example Advanced/CueAnAudioPlayer * * @related AudioPlayer */ public int length() { return recording.getMillisecondLength(); } /** * Returns the current position of the "playhead" in milliseconds * (ie how much of the sound has already been played). * * @example Advanced/CueAnAudioPlayer * * @return int: the current position of the "playhead" in milliseconds * * @related AudioPlayer */ public int position() { return recording.getMillisecondPosition(); } /** * Sets the position to <code>millis</code> milliseconds from * the beginning. This will not change the play state. If an error * occurs while trying to cue, the position will not change. * If you try to cue to a negative position or to a position * that is greater than <code>length()</code>, the amount will be clamped * to zero or <code>length()</code>. * * @shortdesc Sets the position to <code>millis</code> milliseconds from * the beginning. * * @example Advanced/CueAnAudioPlayer * * @param millis * int: the millisecond position to place the "playhead" * * @related length ( ) * @related AudioPlayer */ public void cue(int millis) { if (millis < 0) { millis = 0; } else if (millis > length()) { millis = length(); } recording.setMillisecondPosition(millis); } /** * Skips <code>millis</code> milliseconds from the current position. * <code>millis</code> can be negative, which will make this skip backwards. * If the skip amount would result in a negative position or a position that is greater than * <code>length()</code>, the new position will be clamped to zero or * <code>length()</code>. * * @shortdesc Skips <code>millis</code> milliseconds from the current position. * * @param millis * int: how many milliseconds to skip, sign indicates direction * * @example AudioPlayer/skip * * @related AudioPlayer */ public void skip(int millis) { int pos = position() + millis; if (pos < 0) { pos = 0; } else if (pos > length()) { pos = length(); } Minim.debug("AudioPlayer.skip: skipping " + millis + " milliseconds, new position is " + pos); recording.setMillisecondPosition(pos); } /** * Returns true if the <code>AudioPlayer</code> is currently playing * and has more than one loop left to play. * * @return true if this is looping, false if not * * @example AudioPlayer/loopNum * * @related AudioPlayer */ public boolean isLooping() { return recording.getLoopCount() != 0; } /** * Indicates if the <code>AudioPlayer</code> is currently playing. * * @return true if this is currently playing, false if not * * @example AudioPlayer/loopNum * * @related AudioPlayer */ public boolean isPlaying() { return recording.isPlaying(); } /** * Returns the meta data for the recording being played by this player. * * @return AudioMetaData: the meta data for this player's recording * * @example Basics/GetMetaData * * @related AudioPlayer * @related AudioMetaData */ public AudioMetaData getMetaData() { return recording.getMetaData(); } /** * Sets the loop points used when looping. * * @param start * int: the start of the loop in milliseconds * @param stop * int: the end of the loop in milliseconds * * @example AudioPlayer/setLoopPoints * * @related AudioPlayer */ public void setLoopPoints(int start, int stop) { recording.setLoopPoints(start, stop); } /** * Release the resources associated with playing this file. * All AudioPlayers returned by Minim's loadFile method * will be closed by Minim when it's stop method is called. * If you are using Processing, Minim's stop method will be * called automatically when your application exits. * * @shortdesc Release the resources associated with playing this file. * * @related AudioPlayer * * @invisible */ public void close() { recording.close(); super.close(); } }