Mercurial > hg > ristipolku
diff src/SoundManager.java @ 161:fb33d3796942
Rename source directory.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 21 Jun 2016 12:53:53 +0300 |
parents | game/SoundManager.java@d6d92845d6a2 |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/SoundManager.java Tue Jun 21 12:53:53 2016 +0300 @@ -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; + + playbackFormat = format; + localLine = new ThreadLocal(); + localBuffer = new ThreadLocal(); + pausedLock = new Object(); + + queue = new LinkedList(); + for (int i = 0; i < numThreads; i++) + new PooledThread().start(); + + 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("SMGR.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 = 8; + + System.out.print(" * 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); + 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(is="+is+")\n"); + if (is != null) + { + runTask(new SoundPlayer(is)); + } + return is; + } + + + protected void threadStarted() + { + synchronized (this) + { + try { + wait(); + } + catch (InterruptedException ex) { } + } + + + // 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); + + System.out.print("SMGR.threadStarted(): "+lineInfo.toString()+"\n"); + + try { + line = (SourceDataLine) AudioSystem.getLine(lineInfo); + line.open(playbackFormat, bufferSize); + } + catch (LineUnavailableException ex) + { + System.out.print("SMGR.threadStarted() line unavailable!\n"); + // the line is unavailable - signal to end this thread + Thread.currentThread().interrupt(); + return; + } + + // Change volume + 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; + + // copy data to the line + try { + boolean playing = true; + while (playing) { + // if paused, wait until unpaused + synchronized (pausedLock) + { + if (paused) { + try { + pausedLock.wait(); + } + catch (InterruptedException ex) { + return; + } + } + } + + // copy data + int bufPos = 0; + while (bufPos < buffer.length && playing) + { + int res = source.read(buffer, bufPos, buffer.length - bufPos); + if (res != -1) + bufPos += res; + else + playing = false; + } + if (playing) + line.write(buffer, 0, bufPos); + } + } + + 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(); + } + } +}