changeset 61:ceaf645ed930

Add (non-working) audio code.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 24 Feb 2011 03:58:33 +0200
parents 4a984e3b27d2
children caf67c7e0814
files game/SoundManager.java
diffstat 1 files changed, 459 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/game/SoundManager.java	Thu Feb 24 03:58:33 2011 +0200
@@ -0,0 +1,459 @@
+/*
+ * Ristipolku Game Engine
+ * (C) Copyright 2011 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
+ */
+package game;
+
+import java.util.*;
+import java.io.*;
+import game.*;
+import javax.sound.sampled.*;
+
+
+public class SoundManager extends ThreadGroup
+{
+    private boolean alive;
+    private LinkedList queue;
+    private int id;
+    private static int poolID;
+
+    private AudioFormat playbackFormat;
+    private ThreadLocal localLine;
+    private ThreadLocal localBuffer;
+    private Object pausedLock;
+    private boolean paused;
+
+
+    public SoundManager(AudioFormat format)
+    {
+        this(format, getMaxSimultaneousSounds(format));
+    }
+
+
+    public SoundManager(AudioFormat format, int maxSounds)
+    {
+        super("SoundManagerPool-" + (poolID++));
+
+        int numThreads = Math.min(maxSounds, getMaxSimultaneousSounds(playbackFormat));
+        
+        System.out.print("SMGR.SoundManager() initializing with " + numThreads +" max sounds\n");
+        
+        setDaemon(true);
+        alive = true;
+
+        queue = new LinkedList();
+        for (int i = 0; i < numThreads; i++)
+            new PooledThread().start();
+
+        playbackFormat = format;
+        localLine = new ThreadLocal();
+        localBuffer = new ThreadLocal();
+        pausedLock = new Object();
+
+        synchronized (this)
+        {
+            notifyAll();
+        }
+    }
+
+
+    public static int getMaxSimultaneousSounds(AudioFormat playbackFormat)
+    {
+        DataLine.Info lineInfo = new DataLine.Info(SourceDataLine.class, playbackFormat);
+
+        Mixer.Info[] info = AudioSystem.getMixerInfo();
+        System.out.print("getMaxSimultaneousSounds() mixer information:\n");
+        Mixer.Info select = null;
+        for (Mixer.Info i : info)
+        {
+            System.out.print("#"+i.getName()+"\n");
+            if (i.getName().equals("Java Sound Audio Engine"))
+                select = i;
+        }
+
+        Mixer mixer = AudioSystem.getMixer(select);
+
+        Mixer.Info i = mixer.getMixerInfo();
+        System.out.print("selected: "+i.getName()+"\n");
+
+        int maxLines = mixer.getMaxLines(lineInfo);
+        if (maxLines == AudioSystem.NOT_SPECIFIED)
+            maxLines = 16;
+
+        System.out.print("SMGR.getMaxSimultaneousSounds() maxLines="+maxLines+"\n");
+
+        return maxLines;
+    }
+
+
+    protected void cleanUp()
+    {
+        System.out.print("SMGR.cleanUp()\n");
+        // signal to unpause
+        setPaused(false);
+
+        System.out.print("SMGR.cleanUp(): closing mixer\n");
+
+        // close the mixer (stops any running sounds)
+        Mixer mixer = AudioSystem.getMixer(null);
+        System.out.print("SMGR.cleanUp(): foo\n");
+        if (mixer.isOpen())
+            mixer.close();
+
+        System.out.print("SMGR.cleanUp(): leaving\n");
+    }
+
+
+    public void setPaused(boolean paused)
+    {
+        if (this.paused != paused)
+        {
+            synchronized (pausedLock)
+            {
+                this.paused = paused;
+                if (!paused)
+                {
+                    // restart sounds
+                    pausedLock.notifyAll();
+                }
+            }
+        }
+    }
+
+
+    public boolean isPaused()
+    {
+        return paused;
+    }
+
+
+    public Sound getSound(String filename)
+    {
+        return getSound(getAudioInputStream(filename));
+    }
+
+
+    public Sound getSound(InputStream is)
+    {
+        return getSound(getAudioInputStream(is));
+    }
+
+
+    public Sound getSound(AudioInputStream audioStream)
+    {
+        if (audioStream == null)
+            return null;
+
+        // get the number of bytes to read
+        int length = (int)(audioStream.getFrameLength() * audioStream.getFormat().getFrameSize());
+
+        // read the entire stream
+        byte[] samples = new byte[length];
+        DataInputStream is = new DataInputStream(audioStream);
+        try {
+            is.readFully(samples);
+            is.close();
+        }
+        catch (IOException ex)
+        {
+            ex.printStackTrace();
+        }
+
+        // return the samples
+        return new Sound(samples);
+    }
+
+
+    public AudioInputStream getAudioInputStream(String filename)
+    {
+        ResourceLoader res = new ResourceLoader(filename);
+        if (res == null || res.getStream() == null)
+        {
+            System.out.print("Could not load audio resource '"+ filename +"'.\n");
+            return null;
+        }
+        try {
+            return getAudioInputStream(res.getStream());
+        }
+        catch (Exception ex)
+        {
+            System.out.print("Could not get AudioInputStream for '"+ filename +"'\n");
+            return null;
+        }
+    }
+
+
+    public AudioInputStream getAudioInputStream(InputStream is)
+    {
+        try {
+            if (!is.markSupported())
+                is = new BufferedInputStream(is);
+
+            // open the source stream
+            AudioInputStream source = AudioSystem.getAudioInputStream(is);
+
+            // convert to playback format
+            return AudioSystem.getAudioInputStream(playbackFormat, source);
+        }
+
+        catch (UnsupportedAudioFileException ex) {
+            ex.printStackTrace();
+        }
+        catch (IOException ex) {
+            ex.printStackTrace();
+        }
+        catch (IllegalArgumentException ex) {
+            ex.printStackTrace();
+        }
+
+        return null;
+    }
+
+
+    public InputStream play(Sound sound)
+    {
+        InputStream is;
+        if (sound != null)
+        {
+            is = new ByteArrayInputStream(sound.getSamples());
+            return play(is);
+        }
+        return null;
+    }
+
+
+    public InputStream play(InputStream is)
+    {
+        System.out.print("SMGR.play()\n");
+        if (is != null)
+            runTask(new SoundPlayer(is));
+
+        return is;
+    }
+
+
+    protected void threadStarted()
+    {
+        synchronized (this)
+        {
+            try {
+                wait();
+            }
+            catch (InterruptedException ex) { }
+        }
+
+        System.out.print("SMGR.threadStarted()\n");
+
+        // use a short, 100ms (1/10th sec) buffer for filters that
+        // change in real-time
+        int bufferSize = playbackFormat.getFrameSize() *
+            Math.round(playbackFormat.getSampleRate() / 10);
+
+        // create, open, and start the line
+        SourceDataLine line;
+        DataLine.Info lineInfo = new DataLine.Info(SourceDataLine.class, playbackFormat);
+
+        try {
+            line = (SourceDataLine) AudioSystem.getLine(lineInfo);
+            line.open(playbackFormat, bufferSize);
+        }
+        catch (LineUnavailableException ex)
+        {
+            // the line is unavailable - signal to end this thread
+            Thread.currentThread().interrupt();
+            return;
+        }
+        
+/*
+        Control[] ctrls = line.getControls();
+        for (Control c : ctrls)
+        {
+            System.out.print("#" + c.toString() +"\n");
+        }
+*/
+        Control.Type ct = FloatControl.Type.MASTER_GAIN;
+        if (line.isControlSupported(ct))
+        {
+            FloatControl c = (FloatControl) line.getControl(ct);
+            c.setValue(-20f);
+        }
+
+        line.start();
+
+        // create the buffer
+        byte[] buffer = new byte[bufferSize];
+
+        // set this thread's locals
+        localLine.set(line);
+        localBuffer.set(buffer);
+    }
+
+
+    protected void threadStopped()
+    {
+        System.out.print("SMGR.threadStopped()\n");
+        SourceDataLine line = (SourceDataLine) localLine.get();
+        if (line != null)
+        {
+            line.drain();
+            line.close();
+        }
+    }
+
+
+    protected class SoundPlayer implements Runnable
+    {
+        private InputStream source;
+
+        public SoundPlayer(InputStream source)
+        {
+            this.source = source;
+        }
+
+        public void run()
+        {
+            // get line and buffer from ThreadLocals
+            SourceDataLine line = (SourceDataLine) localLine.get();
+            byte[] buffer = (byte[])localBuffer.get();
+
+            if (line == null || buffer == null)
+                return;
+            
+            System.out.print("SMGR.SoundPlayer.run()\n");
+
+            // copy data to the line
+            try {
+                int numBytesRead = 0;
+
+                while (numBytesRead != -1) {
+                    // if paused, wait until unpaused
+                    synchronized (pausedLock)
+                    {
+                        if (paused) {
+                            try {
+                                pausedLock.wait();
+                            }
+                            catch (InterruptedException ex) {
+                                return;
+                            }
+                        }
+                    }
+                    
+                    // copy data
+                    numBytesRead = source.read(buffer, 0, buffer.length);
+                    if (numBytesRead != -1)
+                        line.write(buffer, 0, numBytesRead);
+                }
+            }
+
+            catch (IOException ex) {
+                ex.printStackTrace();
+            }
+
+        }
+    }
+
+    public synchronized void runTask(Runnable task)
+    {
+        if (!alive)
+        {
+            throw new IllegalStateException();
+        }
+        if (task != null)
+        {
+            queue.add(task);
+            notify();
+        }
+
+    }
+
+    protected synchronized Runnable getTask() throws InterruptedException
+    {
+        while (queue.size() == 0)
+        {
+            if (!alive)
+                return null;
+
+            wait();
+        }
+        return (Runnable) queue.removeFirst();
+    }
+
+
+    public synchronized void close()
+    {
+        System.out.print("SMGR.close()\n");
+
+        if (alive)
+        {
+            System.out.print("SMGR.close(): alive queue clear\n");
+            // Clear queue
+            alive = false;
+            queue.clear();
+            interrupt();
+        }
+
+        cleanUp();
+        System.out.print("SMGR.close(): leaving\n");
+    }
+
+
+    public void join()
+    {
+        System.out.print("SMGR.join()\n");
+        cleanUp();
+
+        synchronized (this)
+        {
+            alive = false;
+            notifyAll();
+        }
+
+        Thread[] threads = new Thread[activeCount()];
+        int count = enumerate(threads);
+        for (int i = 0; i < count; i++)
+        {
+            try {
+                threads[i].join();
+            }
+            catch (InterruptedException ex)
+            {
+            }
+        }
+    }
+
+
+    private class PooledThread extends Thread
+    {
+        public PooledThread()
+        {
+            super(SoundManager.this, "SoundManagerPool-" + (id++));
+        }
+
+        public void run()
+        {
+            threadStarted();
+
+            while (!isInterrupted()) {
+                Runnable task = null;
+                try {
+                    task = getTask();
+                }
+                catch (InterruptedException ex)
+                {
+                }
+
+                if (task == null)
+                    break;
+
+                try {
+                    task.run();
+                }
+                catch (Throwable t) {
+                    uncaughtException(this, t);
+                }
+            }
+            threadStopped();
+        }
+    }
+}