001: /*
002: * @(#)AudioDevice.java 1.20 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.audio;
029:
030: import java.util.Vector;
031: import java.util.Enumeration;
032: import java.io.IOException;
033: import java.io.InputStream;
034: import java.io.OutputStream;
035:
036: /**
037: * This class provides an interface to a Sun audio device.
038: *
039: * This class emulates systems with multiple audio channels, mixing
040: * multiple streams for the workstation's single-channel device.
041: *
042: * @see AudioData
043: * @see AudioDataStream
044: * @see AudioStream
045: * @see AudioStreamSequence
046: * @see ContinuousAudioDataStream
047: * @author Arthur van Hoff, Thomas Ball
048: * @version 1.16, 08/19/02
049: */
050:
051: /* For LINUX - refer to sun.audio.audioDevice.c
052: * audioDevice.c includes code for converting from standard mono 8-bit
053: * ulaw encoding format (Sun audio device) to stereo 16-bit linear encoding (Linux).
054: * There seems to be no standard encoding format for Linux audio device
055: * drivers so other conversion functions may be needed to support other
056: * audio device drivers.
057: */
058:
059: public class AudioDevice {
060: private Vector streams;
061: private byte ulaw[];
062: private int linear[];
063: private int dev;
064: /*
065: * ulaw stuff
066: */
067: private static final int MSCLICK = 50;
068: private static final int MSMARGIN = MSCLICK / 3;
069: private static final int BYTES_PER_SAMPLE = 1;
070: private static final int SAMPLE_RATE = 8000;
071: /* define the add-in bias for 16 bit samples */
072: private final static int ULAW_BIAS = 0x84;
073: private final static int ULAW_CLIP = 32635;
074: private final static int ULAW_TAB[] = { -32124, -31100, -30076,
075: -29052, -28028, -27004, -25980, -24956, -23932, -22908,
076: -21884, -20860, -19836, -18812, -17788, -16764, -15996,
077: -15484, -14972, -14460, -13948, -13436, -12924, -12412,
078: -11900, -11388, -10876, -10364, -9852, -9340, -8828, -8316,
079: -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
080: -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
081: -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
082: -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
083: -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
084: -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, -876,
085: -844, -812, -780, -748, -716, -684, -652, -620, -588, -556,
086: -524, -492, -460, -428, -396, -372, -356, -340, -324, -308,
087: -292, -276, -260, -244, -228, -212, -196, -180, -164, -148,
088: -132, -120, -112, -104, -96, -88, -80, -72, -64, -56, -48,
089: -40, -32, -24, -16, -8, 0, 32124, 31100, 30076, 29052,
090: 28028, 27004, 25980, 24956, 23932, 22908, 21884, 20860,
091: 19836, 18812, 17788, 16764, 15996, 15484, 14972, 14460,
092: 13948, 13436, 12924, 12412, 11900, 11388, 10876, 10364,
093: 9852, 9340, 8828, 8316, 7932, 7676, 7420, 7164, 6908, 6652,
094: 6396, 6140, 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
095: 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, 2876, 2748,
096: 2620, 2492, 2364, 2236, 2108, 1980, 1884, 1820, 1756, 1692,
097: 1628, 1564, 1500, 1436, 1372, 1308, 1244, 1180, 1116, 1052,
098: 988, 924, 876, 844, 812, 780, 748, 716, 684, 652, 620, 588,
099: 556, 524, 492, 460, 428, 396, 372, 356, 340, 324, 308, 292,
100: 276, 260, 244, 228, 212, 196, 180, 164, 148, 132, 120, 112,
101: 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0 };
102: private final static int ULAW_LUT[] = { 0, 0, 1, 1, 2, 2, 2, 2, 3,
103: 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
104: 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
105: 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6,
106: 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
107: 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
108: 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
109: 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
110: 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
111: 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
112: 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
113: 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
114: 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
115: 7, 7, 7, 7, 7, 7, 7 };
116:
117: private native int audioOpen();
118:
119: private native void audioClose();
120:
121: private synchronized native void audioWrite(byte buf[], int len);
122:
123: private static native void initIDs();
124:
125: static {
126: initIDs();
127: }
128: /**
129: * The default audio player. This audio player is initialized
130: * automatically.
131: */
132: public static final AudioDevice device = new AudioDevice();
133:
134: /**
135: * Create an AudioDevice instance.
136: */
137: private AudioDevice() {
138: /* in pjava, this mmedia code is statically linked in */
139: // try {
140: // System.loadLibrary("mmedia");
141: // } catch (UnsatisfiedLinkError e) {
142: // System.out.println("could not find/load the mmedia library");
143: // }
144: streams = new Vector();
145: //int bufferSize = ((SAMPLE_RATE * MSCLICK) / 1000) * BYTES_PER_SAMPLE;
146: /* Increasing bufferSize to send to device. */
147: int bufferSize = 512;
148: ulaw = new byte[bufferSize];
149: linear = new int[bufferSize];
150: }
151:
152: /**
153: * Open an audio channel.
154: */
155: public synchronized void openChannel(InputStream in) {
156: streams.insertElementAt(in, 0);
157: notify();
158: }
159:
160: /**
161: * Close an audio channel.
162: */
163: public synchronized void closeChannel(InputStream in) {
164: if (streams.removeElement(in)) {
165: try {
166: in.close();
167: } catch (IOException e) {
168: }
169: }
170: }
171:
172: /**
173: * Open the device (done automatically)
174: */
175: public synchronized void open() {
176: int ntries = 1;
177: int maxtries = 10;
178: while (dev == 0) {
179: dev = audioOpen();
180: if (dev < 0) {
181: System.out.println("no audio device");
182: return;
183: }
184: if (dev == 0) {
185: System.out.println("audio device busy (attempt "
186: + ntries + " out of " + maxtries + ")");
187: /* streams.size() can be zero when the device is being opened.
188: * Removed check on streams.size().
189: */
190: //if ((streams.size() == 0) && (++ntries > maxtries)) {
191: if (++ntries > maxtries) {
192: // failed to open the device
193: // close all open streams, wait a while and return
194: closeStreams();
195: return;
196: }
197: // use wait instead of sleep because this unlocks the
198: // current object during the wait.
199: try {
200: wait(3000);
201: } catch (InterruptedException e) {
202: closeStreams();
203: return;
204: }
205: }
206: }
207: }
208:
209: /**
210: * Close the device (done automatically)
211: */
212: public synchronized void close() {
213: if (dev != 0) {
214: audioClose();
215: dev = 0;
216: }
217: closeStreams();
218: }
219:
220: /**
221: * Play one mixed click of data
222: */
223: private synchronized void mix() {
224: int len = ulaw.length;
225: byte ubuf[] = ulaw;
226: switch (streams.size()) {
227: case 0: {
228: // fill the buffer with silence
229: for (int n = len; n-- > 0;) {
230: ubuf[n] = 127;
231: }
232: break;
233: }
234:
235: case 1: {
236: // read from the input stream
237: InputStream in = (InputStream) streams.elementAt(0);
238: int n = 0;
239: try {
240: n = in.read(ubuf, 0, len);
241: } catch (IOException e) {
242: n = -1;
243: }
244: // Close the stream if needed
245: if (n <= 0) {
246: streams.removeElementAt(0);
247: n = 0;
248: try {
249: in.close();
250: } catch (IOException e) {
251: }
252: }
253: // fill the rest of the buffer with silence
254: for (; n < len; n++) {
255: ubuf[n] = 127;
256: }
257: break;
258: }
259:
260: default: {
261: int tab[] = ULAW_TAB;
262: int lbuf[] = linear;
263: int i = streams.size() - 1;
264: // fill linear buffer with the first stream
265: InputStream in = (InputStream) streams.elementAt(i);
266: int n = 0;
267: try {
268: n = in.read(ubuf, 0, len);
269: } catch (IOException e) {
270: n = -1;
271: }
272: // Close the stream if needed
273: if (n <= 0) {
274: streams.removeElementAt(i);
275: n = 0;
276: try {
277: in.close();
278: } catch (IOException e) {
279: }
280: }
281: // copy the data into the linear buffer
282: for (int j = 0; j < n; j++) {
283: lbuf[j] = tab[ubuf[j] & 0xFF];
284: }
285: // zero the rest of the buffer.
286: for (; n < len; n++) {
287: lbuf[n] = 0;
288: }
289: // mix the rest of the streams into the linear buffer
290: while (i-- > 0) {
291: in = (InputStream) streams.elementAt(i);
292: try {
293: n = in.read(ubuf, 0, len);
294: } catch (IOException e) {
295: n = -1;
296: }
297: if (n <= 0) {
298: streams.removeElementAt(i);
299: n = 0;
300: try {
301: in.close();
302: } catch (IOException e) {
303: }
304: }
305: while (n-- > 0) {
306: lbuf[n] += tab[ubuf[n] & 0xFF];
307: }
308: }
309: // convert the linear buffer back to ulaw
310: int lut[] = ULAW_LUT;
311: for (n = len; n-- > 0;) {
312: int sample = lbuf[n];
313: /* Get the sample into sign-magnitude. */
314: if (sample >= 0) {
315: if (sample > ULAW_CLIP) {
316: sample = ULAW_CLIP; /* clip the magnitude */
317: }
318: /* Convert from 16 bit linear to ulaw. */
319: sample += ULAW_BIAS;
320: int exponent = lut[sample >> 7];
321: int mantissa = (sample >> (exponent + 3)) & 0x0F;
322: sample = ((exponent << 4) | mantissa) ^ 0xFF;
323: } else {
324: sample = -sample;
325: if (sample > ULAW_CLIP) {
326: sample = ULAW_CLIP; /* clip the magnitude */
327: }
328: /* Convert from 16 bit linear to ulaw. */
329: sample += ULAW_BIAS;
330: int exponent = lut[sample >> 7];
331: int mantissa = (sample >> (exponent + 3)) & 0x0F;
332: sample = ((exponent << 4) | mantissa) ^ 0x7F;
333: }
334: ubuf[n] = (byte) sample;
335: }
336: }
337: }
338: }
339:
340: /**
341: * Wait for data
342: */
343: private synchronized boolean waitForData()
344: throws InterruptedException {
345: if (streams.size() == 0) {
346: close();
347: wait();
348: open();
349: return true;
350: }
351: return false;
352: }
353:
354: /**
355: * Play open audio stream(s)
356: */
357: public void play() {
358: try {
359: long tm = System.currentTimeMillis() - MSMARGIN;
360: while (dev > 0) {
361: // wait for data
362: if (waitForData()) {
363: tm = System.currentTimeMillis() - MSMARGIN;
364: }
365: // mix the next bit
366: mix();
367: // write the next buffer
368: audioWrite(ulaw, ulaw.length);
369: /* Removing wait to allow Linux play sound fully.
370: * Breaks sound on Solaris - cannot stop sound.
371: */
372: // wait for the time out
373: tm += MSCLICK;
374: long delay = tm - System.currentTimeMillis();
375: if (delay > 0) {
376: Thread.currentThread().sleep(delay);
377: } else {
378: // We've lost it, reset the time..
379: //System.out.println("delay2=" + delay);
380: tm = System.currentTimeMillis() - MSMARGIN;
381: }
382: }
383: } catch (InterruptedException e) {// the thread got interrupted, exit
384: }
385: }
386:
387: /**
388: * Close streams
389: */
390: public synchronized void closeStreams() {
391: // close the streams be garbage collected
392: for (Enumeration e = streams.elements(); e.hasMoreElements();) {
393: try {
394: ((InputStream) e.nextElement()).close();
395: } catch (IOException ee) {
396: }
397: }
398: streams = new Vector();
399: }
400:
401: /**
402: * Number of channels currently open.
403: */
404: public int openChannels() {
405: return streams.size();
406: }
407: }
|