comparison game/SoundManager.java @ 61:ceaf645ed930

Add (non-working) audio code.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 24 Feb 2011 03:58:33 +0200
parents
children 1c7a97d80120
comparison
equal deleted inserted replaced
60:4a984e3b27d2 61:ceaf645ed930
1 /*
2 * Ristipolku Game Engine
3 * (C) Copyright 2011 Matti 'ccr' Hämäläinen <ccr@tnsp.org>
4 */
5 package game;
6
7 import java.util.*;
8 import java.io.*;
9 import game.*;
10 import javax.sound.sampled.*;
11
12
13 public class SoundManager extends ThreadGroup
14 {
15 private boolean alive;
16 private LinkedList queue;
17 private int id;
18 private static int poolID;
19
20 private AudioFormat playbackFormat;
21 private ThreadLocal localLine;
22 private ThreadLocal localBuffer;
23 private Object pausedLock;
24 private boolean paused;
25
26
27 public SoundManager(AudioFormat format)
28 {
29 this(format, getMaxSimultaneousSounds(format));
30 }
31
32
33 public SoundManager(AudioFormat format, int maxSounds)
34 {
35 super("SoundManagerPool-" + (poolID++));
36
37 int numThreads = Math.min(maxSounds, getMaxSimultaneousSounds(playbackFormat));
38
39 System.out.print("SMGR.SoundManager() initializing with " + numThreads +" max sounds\n");
40
41 setDaemon(true);
42 alive = true;
43
44 queue = new LinkedList();
45 for (int i = 0; i < numThreads; i++)
46 new PooledThread().start();
47
48 playbackFormat = format;
49 localLine = new ThreadLocal();
50 localBuffer = new ThreadLocal();
51 pausedLock = new Object();
52
53 synchronized (this)
54 {
55 notifyAll();
56 }
57 }
58
59
60 public static int getMaxSimultaneousSounds(AudioFormat playbackFormat)
61 {
62 DataLine.Info lineInfo = new DataLine.Info(SourceDataLine.class, playbackFormat);
63
64 Mixer.Info[] info = AudioSystem.getMixerInfo();
65 System.out.print("getMaxSimultaneousSounds() mixer information:\n");
66 Mixer.Info select = null;
67 for (Mixer.Info i : info)
68 {
69 System.out.print("#"+i.getName()+"\n");
70 if (i.getName().equals("Java Sound Audio Engine"))
71 select = i;
72 }
73
74 Mixer mixer = AudioSystem.getMixer(select);
75
76 Mixer.Info i = mixer.getMixerInfo();
77 System.out.print("selected: "+i.getName()+"\n");
78
79 int maxLines = mixer.getMaxLines(lineInfo);
80 if (maxLines == AudioSystem.NOT_SPECIFIED)
81 maxLines = 16;
82
83 System.out.print("SMGR.getMaxSimultaneousSounds() maxLines="+maxLines+"\n");
84
85 return maxLines;
86 }
87
88
89 protected void cleanUp()
90 {
91 System.out.print("SMGR.cleanUp()\n");
92 // signal to unpause
93 setPaused(false);
94
95 System.out.print("SMGR.cleanUp(): closing mixer\n");
96
97 // close the mixer (stops any running sounds)
98 Mixer mixer = AudioSystem.getMixer(null);
99 System.out.print("SMGR.cleanUp(): foo\n");
100 if (mixer.isOpen())
101 mixer.close();
102
103 System.out.print("SMGR.cleanUp(): leaving\n");
104 }
105
106
107 public void setPaused(boolean paused)
108 {
109 if (this.paused != paused)
110 {
111 synchronized (pausedLock)
112 {
113 this.paused = paused;
114 if (!paused)
115 {
116 // restart sounds
117 pausedLock.notifyAll();
118 }
119 }
120 }
121 }
122
123
124 public boolean isPaused()
125 {
126 return paused;
127 }
128
129
130 public Sound getSound(String filename)
131 {
132 return getSound(getAudioInputStream(filename));
133 }
134
135
136 public Sound getSound(InputStream is)
137 {
138 return getSound(getAudioInputStream(is));
139 }
140
141
142 public Sound getSound(AudioInputStream audioStream)
143 {
144 if (audioStream == null)
145 return null;
146
147 // get the number of bytes to read
148 int length = (int)(audioStream.getFrameLength() * audioStream.getFormat().getFrameSize());
149
150 // read the entire stream
151 byte[] samples = new byte[length];
152 DataInputStream is = new DataInputStream(audioStream);
153 try {
154 is.readFully(samples);
155 is.close();
156 }
157 catch (IOException ex)
158 {
159 ex.printStackTrace();
160 }
161
162 // return the samples
163 return new Sound(samples);
164 }
165
166
167 public AudioInputStream getAudioInputStream(String filename)
168 {
169 ResourceLoader res = new ResourceLoader(filename);
170 if (res == null || res.getStream() == null)
171 {
172 System.out.print("Could not load audio resource '"+ filename +"'.\n");
173 return null;
174 }
175 try {
176 return getAudioInputStream(res.getStream());
177 }
178 catch (Exception ex)
179 {
180 System.out.print("Could not get AudioInputStream for '"+ filename +"'\n");
181 return null;
182 }
183 }
184
185
186 public AudioInputStream getAudioInputStream(InputStream is)
187 {
188 try {
189 if (!is.markSupported())
190 is = new BufferedInputStream(is);
191
192 // open the source stream
193 AudioInputStream source = AudioSystem.getAudioInputStream(is);
194
195 // convert to playback format
196 return AudioSystem.getAudioInputStream(playbackFormat, source);
197 }
198
199 catch (UnsupportedAudioFileException ex) {
200 ex.printStackTrace();
201 }
202 catch (IOException ex) {
203 ex.printStackTrace();
204 }
205 catch (IllegalArgumentException ex) {
206 ex.printStackTrace();
207 }
208
209 return null;
210 }
211
212
213 public InputStream play(Sound sound)
214 {
215 InputStream is;
216 if (sound != null)
217 {
218 is = new ByteArrayInputStream(sound.getSamples());
219 return play(is);
220 }
221 return null;
222 }
223
224
225 public InputStream play(InputStream is)
226 {
227 System.out.print("SMGR.play()\n");
228 if (is != null)
229 runTask(new SoundPlayer(is));
230
231 return is;
232 }
233
234
235 protected void threadStarted()
236 {
237 synchronized (this)
238 {
239 try {
240 wait();
241 }
242 catch (InterruptedException ex) { }
243 }
244
245 System.out.print("SMGR.threadStarted()\n");
246
247 // use a short, 100ms (1/10th sec) buffer for filters that
248 // change in real-time
249 int bufferSize = playbackFormat.getFrameSize() *
250 Math.round(playbackFormat.getSampleRate() / 10);
251
252 // create, open, and start the line
253 SourceDataLine line;
254 DataLine.Info lineInfo = new DataLine.Info(SourceDataLine.class, playbackFormat);
255
256 try {
257 line = (SourceDataLine) AudioSystem.getLine(lineInfo);
258 line.open(playbackFormat, bufferSize);
259 }
260 catch (LineUnavailableException ex)
261 {
262 // the line is unavailable - signal to end this thread
263 Thread.currentThread().interrupt();
264 return;
265 }
266
267 /*
268 Control[] ctrls = line.getControls();
269 for (Control c : ctrls)
270 {
271 System.out.print("#" + c.toString() +"\n");
272 }
273 */
274 Control.Type ct = FloatControl.Type.MASTER_GAIN;
275 if (line.isControlSupported(ct))
276 {
277 FloatControl c = (FloatControl) line.getControl(ct);
278 c.setValue(-20f);
279 }
280
281 line.start();
282
283 // create the buffer
284 byte[] buffer = new byte[bufferSize];
285
286 // set this thread's locals
287 localLine.set(line);
288 localBuffer.set(buffer);
289 }
290
291
292 protected void threadStopped()
293 {
294 System.out.print("SMGR.threadStopped()\n");
295 SourceDataLine line = (SourceDataLine) localLine.get();
296 if (line != null)
297 {
298 line.drain();
299 line.close();
300 }
301 }
302
303
304 protected class SoundPlayer implements Runnable
305 {
306 private InputStream source;
307
308 public SoundPlayer(InputStream source)
309 {
310 this.source = source;
311 }
312
313 public void run()
314 {
315 // get line and buffer from ThreadLocals
316 SourceDataLine line = (SourceDataLine) localLine.get();
317 byte[] buffer = (byte[])localBuffer.get();
318
319 if (line == null || buffer == null)
320 return;
321
322 System.out.print("SMGR.SoundPlayer.run()\n");
323
324 // copy data to the line
325 try {
326 int numBytesRead = 0;
327
328 while (numBytesRead != -1) {
329 // if paused, wait until unpaused
330 synchronized (pausedLock)
331 {
332 if (paused) {
333 try {
334 pausedLock.wait();
335 }
336 catch (InterruptedException ex) {
337 return;
338 }
339 }
340 }
341
342 // copy data
343 numBytesRead = source.read(buffer, 0, buffer.length);
344 if (numBytesRead != -1)
345 line.write(buffer, 0, numBytesRead);
346 }
347 }
348
349 catch (IOException ex) {
350 ex.printStackTrace();
351 }
352
353 }
354 }
355
356 public synchronized void runTask(Runnable task)
357 {
358 if (!alive)
359 {
360 throw new IllegalStateException();
361 }
362 if (task != null)
363 {
364 queue.add(task);
365 notify();
366 }
367
368 }
369
370 protected synchronized Runnable getTask() throws InterruptedException
371 {
372 while (queue.size() == 0)
373 {
374 if (!alive)
375 return null;
376
377 wait();
378 }
379 return (Runnable) queue.removeFirst();
380 }
381
382
383 public synchronized void close()
384 {
385 System.out.print("SMGR.close()\n");
386
387 if (alive)
388 {
389 System.out.print("SMGR.close(): alive queue clear\n");
390 // Clear queue
391 alive = false;
392 queue.clear();
393 interrupt();
394 }
395
396 cleanUp();
397 System.out.print("SMGR.close(): leaving\n");
398 }
399
400
401 public void join()
402 {
403 System.out.print("SMGR.join()\n");
404 cleanUp();
405
406 synchronized (this)
407 {
408 alive = false;
409 notifyAll();
410 }
411
412 Thread[] threads = new Thread[activeCount()];
413 int count = enumerate(threads);
414 for (int i = 0; i < count; i++)
415 {
416 try {
417 threads[i].join();
418 }
419 catch (InterruptedException ex)
420 {
421 }
422 }
423 }
424
425
426 private class PooledThread extends Thread
427 {
428 public PooledThread()
429 {
430 super(SoundManager.this, "SoundManagerPool-" + (id++));
431 }
432
433 public void run()
434 {
435 threadStarted();
436
437 while (!isInterrupted()) {
438 Runnable task = null;
439 try {
440 task = getTask();
441 }
442 catch (InterruptedException ex)
443 {
444 }
445
446 if (task == null)
447 break;
448
449 try {
450 task.run();
451 }
452 catch (Throwable t) {
453 uncaughtException(this, t);
454 }
455 }
456 threadStopped();
457 }
458 }
459 }