package org.pielot.openal; import android.app.Activity; import android.util.Log; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * The sound environment which hosts all sound buffers and sound sources. It * also allows to define the position and the orientation of the listener. * @author Martin Pielot */ public class SoundEnv { // ======================================================================== // Fields // ======================================================================== private static final String TAG = "SoundEnv"; private Activity activity; private List<Buffer> buffers; private List<Source> sources; private boolean released; // ======================================================================== // Singleton // ======================================================================== private SoundEnv(Activity activity) { this.activity = activity; System.loadLibrary("openal"); System.loadLibrary("openalwrapper"); this.buffers = new ArrayList<Buffer>(); this.sources = new ArrayList<Source>(); OpenAlBridge.init(); Log.i(TAG, "Initializing Sound Environment"); } private static SoundEnv instance; public static SoundEnv getInstance(Activity activity) { if (instance == null) instance = new SoundEnv(activity); return instance; } // ======================================================================== // Methods // ======================================================================== /** * Creates a new buffer with a sound file stored in the assets folder of the * Android project and adds it to the internal list of active buffers. * @param name Name of the sound file without file extension = "lake" * instead of "lake.wav". This will also become the name of the * buffer. * @throws java.io.IOException if the sound file cannot be found */ public Buffer addBuffer(String name) throws IOException { Buffer buffer = Buffer.createFrom(activity, name); Log.d(TAG, "addBuffer( " + buffer + " )"); this.buffers.add(buffer); return buffer; } /** * Creates a new buffer by specifying the file path of the .wav file and * adds it to the internal list of active buffers. * @param name Name of the buffer that can be used to retrieve the buffer * via {@link SoundEnv#findBufferByName(String)}. * @param path Path of the file containing the .wav sound * @throws java.io.IOException if the sound file cannot be found */ public Buffer addBuffer(String name, String path) throws IOException { Buffer buffer = Buffer.createFrom(name, path); Log.d(TAG, "addBuffer( " + buffer + " )"); this.buffers.add(buffer); return buffer; } /** * Allows retrieving a buffer by the name specified on its creation * @param name name of the buffer * @return the buffer of NULL if no buffer with the given name was found */ public Buffer findBufferByName(String name) { for (Buffer buffer : buffers) { if (name.equals(buffer.getName())) { return buffer; } } return null; } /** * Creates a new sound source for the given buffer and adds it to the * internal list of sources * @param buffer the buffer */ public Source addSource(Buffer buffer) { Source source = new Source(buffer); Log.d(TAG, "addSource( " + source + " )"); this.sources.add(source); return source; } /** * Moves the listener to the given coordinates. */ public void setListenerPos(float x, float y, float z) { OpenAlBridge.setListenerPos(x, y, z); } /** * Rotates the listener to face into the given direction. For simplification * this method assumes that the listener always stands upright, like a * person when walking. In fact, OpenAL also allows rotating the listener * around any of the three possible axis. * @param heading the direction the listener should face */ public void setListenerOrientation(double heading) { double zv = -Math.cos(Math.toRadians(heading)); double xv = Math.sin(Math.toRadians(heading)); this.setListenerOrientation((float) xv, 0, (float) zv); } /** * Rotates the listener to face into the given direction. */ public void setListenerOrientation(float xv, float yv, float zv) { OpenAlBridge.setListenerOrientation(xv, yv, zv); } /** * Plays all registered sources (useful for debugging). * @param loop 'true' = endlessly repeated, 'false' = played once only */ public void playAllSources(boolean loop) { for (Source source : sources) source.play(loop); } /** * Stops the playback of all registered sources. */ public void stopAllSources() { for (Source source : sources) source.stop(); } /** * Releases all sources and buffers and closes the OpenAL device. */ public synchronized void release() { if (!released) { Log.i(TAG, "release()"); for (Source source : sources) source.stop(); for (Source source : sources) source.release(); for (Buffer buffer : buffers) { buffer.release(); } OpenAlBridge.close(); released = true; } } private boolean memoryLow; public void onLowMemory() { Log.w(TAG, "memory is low, stopping to add buffers"); this.memoryLow = true; } void testMaxBuffers() { try { Log.i(TAG, "testMaxBuffers()"); int i = 0; do { i++; Buffer b = addBuffer("lake"); Log.d(TAG, "\tbuffer" + i + " = " + b); Thread.sleep(10); } while (!memoryLow); Log.i(TAG, "allocated " + i + " buffers"); } catch (Exception e) { Log.e(TAG, e + " in testEnv()", e); } } void testEnv0() { try { Log.i(TAG, "testEnv()"); Buffer lake = addBuffer("lake"); Buffer park = addBuffer("park"); Buffer building = addBuffer("building"); Source lake1 = addSource(lake); Source lake2 = addSource(lake); Source park1 = addSource(park); Source building1 = addSource(building); lake1.setPosition(0, 0, -10); lake2.setPosition(-6, 0, 4); park1.setPosition(6, 0, -12); building1.setPosition(10, 0, 15); lake2.setPitch(1.1f); lake1.play(true); lake2.play(true); park1.play(true); building1.play(true); } catch (Exception e) { Log.e(TAG, e + ", please copy file into 'assets' folder"); } } }