From e699b5817a648882ad1506a8b1d9212dd23cff0f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 5 May 2011 00:58:32 +0000 Subject: [PATCH] Turn FileStream into a more general URLStream so that we can stream music from jar/resource URLs (but retain FileStream for compatibility). git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@1161 ed5b42cb-e716-0410-a449-f6a68f950b19 --- .../com/threerings/openal/FileStream.java | 70 ++--------- .../com/threerings/openal/StreamDecoder.java | 23 +++- .../java/com/threerings/openal/URLStream.java | 117 ++++++++++++++++++ 3 files changed, 142 insertions(+), 68 deletions(-) create mode 100644 src/main/java/com/threerings/openal/URLStream.java diff --git a/src/main/java/com/threerings/openal/FileStream.java b/src/main/java/com/threerings/openal/FileStream.java index c489b691..7854554f 100644 --- a/src/main/java/com/threerings/openal/FileStream.java +++ b/src/main/java/com/threerings/openal/FileStream.java @@ -21,19 +21,17 @@ package com.threerings.openal; -import java.util.ArrayList; - import java.io.File; import java.io.IOException; -import java.nio.ByteBuffer; +import java.net.MalformedURLException; -import com.google.common.collect.Lists; +import static com.threerings.openal.Log.log; /** * An audio stream read from one or more files. */ -public class FileStream extends Stream +public class FileStream extends URLStream { /** * Creates a new stream for the specified file. @@ -44,9 +42,7 @@ public class FileStream extends Stream public FileStream (SoundManager soundmgr, File file, boolean loop) throws IOException { - super(soundmgr); - _file = new QueuedFile(file, loop); - _decoder = StreamDecoder.createInstance(file); + super(soundmgr, file.toURI().toURL(), loop); } /** @@ -57,60 +53,10 @@ public class FileStream extends Stream */ public void queueFile (File file, boolean loop) { - _queue.add(new QueuedFile(file, loop)); - } - - @Override - protected int getFormat () - { - return _decoder.getFormat(); - } - - @Override - protected int getFrequency () - { - return _decoder.getFrequency(); - } - - @Override - protected int populateBuffer (ByteBuffer buf) - throws IOException - { - int read = _decoder.read(buf); - while (buf.hasRemaining() && (!_queue.isEmpty() || _file.loop)) { - if (!_queue.isEmpty()) { - _file = _queue.remove(0); - } - _decoder = StreamDecoder.createInstance(_file.file); - read = Math.max(0, read); - read += _decoder.read(buf); - } - return read; - } - - /** The file currently being played. */ - protected QueuedFile _file; - - /** The stream decoder for the current file. */ - protected StreamDecoder _decoder; - - /** The queue of files to play after the current one. */ - protected ArrayList _queue = Lists.newArrayList(); - - /** A file queued for play. */ - protected class QueuedFile - { - /** 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 QueuedFile (File file, boolean loop) - { - this.file = file; - this.loop = loop; + try { + _queue.add(new QueuedFile(file.toURI().toURL(), loop)); + } catch (MalformedURLException e) { + log.warning("Invalid file url.", "file", file, e); } } } diff --git a/src/main/java/com/threerings/openal/StreamDecoder.java b/src/main/java/com/threerings/openal/StreamDecoder.java index 3db67ff8..5cf02125 100644 --- a/src/main/java/com/threerings/openal/StreamDecoder.java +++ b/src/main/java/com/threerings/openal/StreamDecoder.java @@ -28,6 +28,8 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URL; + import java.nio.ByteBuffer; import com.google.common.collect.Maps; @@ -53,27 +55,36 @@ public abstract class StreamDecoder public static StreamDecoder createInstance (File file) throws IOException { - String path = file.getPath(); + return createInstance(file.toURI().toURL()); + } + + /** + * Creates and initializes a stream decoder for the specified URL. + */ + public static StreamDecoder createInstance (URL url) + throws IOException + { + String path = url.getPath(); int idx = path.lastIndexOf('.'); if (idx == -1) { - log.warning("Missing extension for file [file=" + path + "]."); + log.warning("Missing extension for URL.", "url", url); return null; } String extension = path.substring(idx+1); Class clazz = _extensions.get(extension); if (clazz == null) { - log.warning("No decoder registered for extension [extension=" + extension + - ", file=" + path + "]."); + log.warning("No decoder registered for extension.", + "extension", extension, "url", url); return null; } StreamDecoder decoder; try { decoder = clazz.newInstance(); } catch (Exception e) { - log.warning("Error instantiating decoder [file=" + path + ", error=" + e + "]."); + log.warning("Error instantiating decoder.", "url", url, e); return null; } - decoder.init(new FileInputStream(file)); + decoder.init(url.openStream()); return decoder; } diff --git a/src/main/java/com/threerings/openal/URLStream.java b/src/main/java/com/threerings/openal/URLStream.java new file mode 100644 index 00000000..ced2026b --- /dev/null +++ b/src/main/java/com/threerings/openal/URLStream.java @@ -0,0 +1,117 @@ +// +// $Id$ +// +// Nenya library - tools for developing networked games +// Copyright (C) 2002-2011 Three Rings Design, Inc., All Rights Reserved +// http://code.google.com/p/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.util.ArrayList; + +import java.io.IOException; + +import java.net.URL; + +import java.nio.ByteBuffer; + +import com.google.common.collect.Lists; + +/** + * An audio stream read from one or more URLs. + */ +public class URLStream extends Stream +{ + /** + * Creates a new stream for the specified URL. + * + * @param loop whether or not to play the file in a continuous loop + * if there's nothing on the queue + */ + public URLStream (SoundManager soundmgr, URL url, boolean loop) + throws IOException + { + super(soundmgr); + _file = new QueuedFile(url, loop); + _decoder = StreamDecoder.createInstance(url); + } + + /** + * Adds an element to the queue of URLs to play. + * + * @param loop if true, play this file in a loop if there's nothing else + * on the queue + */ + public void queueFile (URL url, boolean loop) + { + _queue.add(new QueuedFile(url, loop)); + } + + @Override + protected int getFormat () + { + return _decoder.getFormat(); + } + + @Override + protected int getFrequency () + { + return _decoder.getFrequency(); + } + + @Override + protected int populateBuffer (ByteBuffer buf) + throws IOException + { + int read = _decoder.read(buf); + while (buf.hasRemaining() && (!_queue.isEmpty() || _file.loop)) { + if (!_queue.isEmpty()) { + _file = _queue.remove(0); + } + _decoder = StreamDecoder.createInstance(_file.url); + read = Math.max(0, read); + read += _decoder.read(buf); + } + return read; + } + + /** The file currently being played. */ + protected QueuedFile _file; + + /** The stream decoder for the current file. */ + protected StreamDecoder _decoder; + + /** The queue of files to play after the current one. */ + protected ArrayList _queue = Lists.newArrayList(); + + /** A file queued for play. */ + protected class QueuedFile + { + /** The URL of the file to play. */ + public URL url; + + /** Whether or not to play the file in a loop when there's nothing + * in the queue. */ + public boolean loop; + + public QueuedFile (URL url, boolean loop) + { + this.url = url; + this.loop = loop; + } + } +}