Non-functional MP3 playing support.
git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@152 ed5b42cb-e716-0410-a449-f6a68f950b19
This commit is contained in:
@@ -22,5 +22,6 @@
|
|||||||
<include name="jme-terrain.jar"/>
|
<include name="jme-terrain.jar"/>
|
||||||
<include name="jme.jar"/>
|
<include name="jme.jar"/>
|
||||||
<include name="jme-bui.jar"/>
|
<include name="jme-bui.jar"/>
|
||||||
|
<include name="jl1.0.jar"/>
|
||||||
</fileset>
|
</fileset>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -0,0 +1,174 @@
|
|||||||
|
//
|
||||||
|
// $Id$
|
||||||
|
//
|
||||||
|
// Nenya library - tools for developing networked games
|
||||||
|
// Copyright (C) 2002-2006 Three Rings Design, Inc., All Rights Reserved
|
||||||
|
// http://www.threerings.net/code/nenya/
|
||||||
|
//
|
||||||
|
// This library 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 2.1 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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 along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
package com.threerings.openal;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ShortBuffer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.lwjgl.openal.AL10;
|
||||||
|
|
||||||
|
import javazoom.jl.decoder.Bitstream;
|
||||||
|
import javazoom.jl.decoder.Decoder;
|
||||||
|
import javazoom.jl.decoder.Header;
|
||||||
|
import javazoom.jl.decoder.JavaLayerException;
|
||||||
|
import javazoom.jl.decoder.SampleBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An audio stream read from one or more mp3 files.
|
||||||
|
*/
|
||||||
|
public class Mp3FileStream extends Stream
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates a new stream for the specified file.
|
||||||
|
*
|
||||||
|
* @param loop whether or not to play the file in a continuous loop if there's nothing on the
|
||||||
|
* queue
|
||||||
|
*/
|
||||||
|
public Mp3FileStream (SoundManager soundmgr, File file, boolean loop)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super(soundmgr);
|
||||||
|
_file = new Mp3File(file, loop);
|
||||||
|
_istream = new Bitstream(new FileInputStream(file));
|
||||||
|
_decoder = new Decoder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a file to the queue of files to play.
|
||||||
|
*
|
||||||
|
* @param loop if true, play this file in a loop if there's nothing else on the queue
|
||||||
|
*/
|
||||||
|
public void queueFile (File file, boolean loop)
|
||||||
|
{
|
||||||
|
_queue.add(new Mp3File(file, loop));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override // from Stream
|
||||||
|
public void dispose ()
|
||||||
|
{
|
||||||
|
super.dispose();
|
||||||
|
|
||||||
|
// TODO: close our bitstream...
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
protected int getFormat ()
|
||||||
|
{
|
||||||
|
return AL10.AL_FORMAT_STEREO16;
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
protected int getFrequency ()
|
||||||
|
{
|
||||||
|
return _frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
protected int populateBuffer (ByteBuffer buf)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
if (_buffer == null) {
|
||||||
|
try {
|
||||||
|
Header h = _istream.readFrame();
|
||||||
|
for (int ii = 0; ii < 10 && (h == null); ii++) {
|
||||||
|
_istream.close();
|
||||||
|
Mp3File file = getNextFile();
|
||||||
|
if (file == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_istream = new Bitstream(new FileInputStream(file.file));
|
||||||
|
h = _istream.readFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
_buffer = (SampleBuffer)_decoder.decodeFrame(h, _istream);
|
||||||
|
_frequency = _buffer.getSampleFrequency();
|
||||||
|
|
||||||
|
} catch (JavaLayerException jle) {
|
||||||
|
throw (IOException)new IOException("JavaLayer decoding error").initCause(jle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int srem = buf.remaining();
|
||||||
|
ShortBuffer sbuf = buf.asShortBuffer();
|
||||||
|
int wrote = Math.min(_buffer.getBufferLength()-_offset, sbuf.remaining());
|
||||||
|
sbuf.put(_buffer.getBuffer(), _offset, wrote);
|
||||||
|
if (_offset + wrote >= _buffer.getBufferLength()) {
|
||||||
|
_buffer = null;
|
||||||
|
_offset = 0;
|
||||||
|
_istream.closeFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 2 * wrote;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Mp3File getNextFile ()
|
||||||
|
{
|
||||||
|
if (_file.loop) {
|
||||||
|
return _file;
|
||||||
|
} else if (!_queue.isEmpty()) {
|
||||||
|
return _queue.remove(0);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The file currently being played. */
|
||||||
|
protected Mp3File _file;
|
||||||
|
|
||||||
|
/** Handles reading the mp3 data. */
|
||||||
|
protected Bitstream _istream;
|
||||||
|
|
||||||
|
/** Handles decoding the mp3 data. */
|
||||||
|
protected Decoder _decoder;
|
||||||
|
|
||||||
|
/** The currently decoded frame, if any. */
|
||||||
|
protected SampleBuffer _buffer;
|
||||||
|
|
||||||
|
/** Our offset into the currently decoded frame. */
|
||||||
|
protected int _offset;
|
||||||
|
|
||||||
|
/** The frequency of the most recently processed frame. */
|
||||||
|
protected int _frequency;
|
||||||
|
|
||||||
|
/** The queue of files to play after the current one. */
|
||||||
|
protected ArrayList<Mp3File> _queue = new ArrayList<Mp3File>();
|
||||||
|
|
||||||
|
/** A file queued for play. */
|
||||||
|
protected class Mp3File
|
||||||
|
{
|
||||||
|
/** The file to play. */
|
||||||
|
public File file;
|
||||||
|
|
||||||
|
/** Whether or not to play the file in a loop when there's nothing in the queue. */
|
||||||
|
public boolean loop;
|
||||||
|
|
||||||
|
public Mp3File (File file, boolean loop) {
|
||||||
|
this.file = file;
|
||||||
|
this.loop = loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,11 +35,9 @@ import org.lwjgl.openal.AL10;
|
|||||||
public abstract class Stream
|
public abstract class Stream
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Creates a new stream. Call {@link #dispose} when finished with the
|
* Creates a new stream. Call {@link #dispose} when finished with the stream.
|
||||||
* stream.
|
|
||||||
*
|
*
|
||||||
* @param soundmgr a reference to the sound manager that will update the
|
* @param soundmgr a reference to the sound manager that will update the stream
|
||||||
* stream
|
|
||||||
*/
|
*/
|
||||||
public Stream (SoundManager soundmgr)
|
public Stream (SoundManager soundmgr)
|
||||||
{
|
{
|
||||||
@@ -130,8 +128,8 @@ public abstract class Stream
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fades this stream in over the specified interval. If the stream isn't
|
* Fades this stream in over the specified interval. If the stream isn't playing, it will be
|
||||||
* playing, it will be started.
|
* started.
|
||||||
*/
|
*/
|
||||||
public void fadeIn (float interval)
|
public void fadeIn (float interval)
|
||||||
{
|
{
|
||||||
@@ -157,8 +155,7 @@ public abstract class Stream
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Releases the resources held by this stream and removes it from
|
* Releases the resources held by this stream and removes it from the manager.
|
||||||
* the manager.
|
|
||||||
*/
|
*/
|
||||||
public void dispose ()
|
public void dispose ()
|
||||||
{
|
{
|
||||||
@@ -180,9 +177,8 @@ public abstract class Stream
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the state of this stream, loading data into buffers and
|
* Updates the state of this stream, loading data into buffers and adjusting gain as necessary.
|
||||||
* adjusting gain as necessary. Called periodically by the
|
* Called periodically by the {@link SoundManager}.
|
||||||
* {@link SoundManager}.
|
|
||||||
*
|
*
|
||||||
* @param time the amount of time elapsed since the last update
|
* @param time the amount of time elapsed since the last update
|
||||||
*/
|
*/
|
||||||
@@ -211,8 +207,7 @@ public abstract class Stream
|
|||||||
// enqueue up to the number of buffers played
|
// enqueue up to the number of buffers played
|
||||||
queueBuffers(played);
|
queueBuffers(played);
|
||||||
|
|
||||||
// find out if we're still playing; if not and we have buffers queued,
|
// find out if we're still playing; if not and we have buffers queued, we must restart
|
||||||
// we must restart
|
|
||||||
_state = AL10.alGetSourcei(_sourceId, AL10.AL_SOURCE_STATE);
|
_state = AL10.alGetSourcei(_sourceId, AL10.AL_SOURCE_STATE);
|
||||||
if (_qlen > 0 && _state != AL10.AL_PLAYING) {
|
if (_qlen > 0 && _state != AL10.AL_PLAYING) {
|
||||||
play();
|
play();
|
||||||
@@ -262,9 +257,8 @@ public abstract class Stream
|
|||||||
/**
|
/**
|
||||||
* Populates the identified buffer with as much data as it can hold.
|
* Populates the identified buffer with as much data as it can hold.
|
||||||
*
|
*
|
||||||
* @return true if data was read into the buffer and it should be enqueued,
|
* @return true if data was read into the buffer and it should be enqueued, false if the end of
|
||||||
* false if the end of the stream has been reached and no data was read
|
* the stream has been reached and no data was read into the buffer
|
||||||
* into the buffer
|
|
||||||
*/
|
*/
|
||||||
protected boolean populateBuffer (int bufferId)
|
protected boolean populateBuffer (int bufferId)
|
||||||
{
|
{
|
||||||
@@ -299,8 +293,8 @@ public abstract class Stream
|
|||||||
/**
|
/**
|
||||||
* Populates the given buffer with audio data.
|
* Populates the given buffer with audio data.
|
||||||
*
|
*
|
||||||
* @return the total number of bytes read into the buffer, or -1 if the
|
* @return the total number of bytes read into the buffer, or -1 if the end of the stream has
|
||||||
* end of the stream has been reached
|
* been reached
|
||||||
*/
|
*/
|
||||||
protected abstract int populateBuffer (ByteBuffer buf)
|
protected abstract int populateBuffer (ByteBuffer buf)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
@@ -314,8 +308,7 @@ public abstract class Stream
|
|||||||
/** The buffers through which we cycle. */
|
/** The buffers through which we cycle. */
|
||||||
protected int[] _bufferIds = new int[NUM_BUFFERS];
|
protected int[] _bufferIds = new int[NUM_BUFFERS];
|
||||||
|
|
||||||
/** The starting index and length of the current queue in
|
/** The starting index and length of the current queue in {@link #_bufferIds}. */
|
||||||
* {@link #_bufferIds}. */
|
|
||||||
protected int _qidx, _qlen;
|
protected int _qidx, _qlen;
|
||||||
|
|
||||||
/** The pitch of the stream. */
|
/** The pitch of the stream. */
|
||||||
|
|||||||
Reference in New Issue
Block a user