Mercurial > hg > ristipolku
view game/SoundManager.java @ 103:8d5cb9e58301
Added tag dev-0_65 for changeset d5f51370617b
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 03 Mar 2011 18:36:16 +0200 |
parents | e1d657e6c25b |
children | dd896bc7352b |
line wrap: on
line source
/* * 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; 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(); } } }