1 /*
2 * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.media.sound;
27
28 import java.util.Vector;
29
30 import javax.sound.sampled.Control;
31 import javax.sound.sampled.Line;
32 import javax.sound.sampled.LineUnavailableException;
33 import javax.sound.sampled.Port;
34 import javax.sound.sampled.BooleanControl;
35 import javax.sound.sampled.CompoundControl;
36 import javax.sound.sampled.FloatControl;
37
38
39 /**
40 * A Mixer which only provides Ports.
41 *
42 * @author Florian Bomers
43 */
44 class PortMixer extends AbstractMixer {
45
46 // CONSTANTS
47 private static final int SRC_UNKNOWN = 0x01;
48 private static final int SRC_MICROPHONE = 0x02;
49 private static final int SRC_LINE_IN = 0x03;
50 private static final int SRC_COMPACT_DISC = 0x04;
51 private static final int SRC_MASK = 0xFF;
52
53 private static final int DST_UNKNOWN = 0x0100;
54 private static final int DST_SPEAKER = 0x0200;
55 private static final int DST_HEADPHONE = 0x0300;
56 private static final int DST_LINE_OUT = 0x0400;
57 private static final int DST_MASK = 0xFF00;
58
59 // INSTANCE VARIABLES
60 private Port.Info[] portInfos;
61 // cache of instantiated ports
62 private PortMixerPort[] ports;
63
64 // instance ID of the native implementation
65 private long id = 0;
66
67 // CONSTRUCTOR
68 PortMixer(PortMixerProvider.PortMixerInfo portMixerInfo) {
69 // pass in Line.Info, mixer, controls
70 super(portMixerInfo, // Mixer.Info
71 null, // Control[]
72 null, // Line.Info[] sourceLineInfo
73 null); // Line.Info[] targetLineInfo
74
75 if (Printer.trace) Printer.trace(">> PortMixer: constructor");
76
77 int count = 0;
78 int srcLineCount = 0;
79 int dstLineCount = 0;
80
81 try {
82 try {
83 id = nOpen(getMixerIndex());
84 if (id != 0) {
85 count = nGetPortCount(id);
86 if (count < 0) {
87 if (Printer.trace) Printer.trace("nGetPortCount() returned error code: " + count);
88 count = 0;
89 }
90 }
91 } catch (Exception e) {}
92
93 portInfos = new Port.Info[count];
94
95 for (int i = 0; i < count; i++) {
96 int type = nGetPortType(id, i);
97 srcLineCount += ((type & SRC_MASK) != 0)?1:0;
98 dstLineCount += ((type & DST_MASK) != 0)?1:0;
99 portInfos[i] = getPortInfo(i, type);
100 }
101 } finally {
102 if (id != 0) {
103 nClose(id);
104 }
105 id = 0;
106 }
107
108 // fill sourceLineInfo and targetLineInfos with copies of the ones in portInfos
109 sourceLineInfo = new Port.Info[srcLineCount];
110 targetLineInfo = new Port.Info[dstLineCount];
111
112 srcLineCount = 0; dstLineCount = 0;
113 for (int i = 0; i < count; i++) {
114 if (portInfos[i].isSource()) {
115 sourceLineInfo[srcLineCount++] = portInfos[i];
116 } else {
117 targetLineInfo[dstLineCount++] = portInfos[i];
118 }
119 }
120
121 if (Printer.trace) Printer.trace("<< PortMixer: constructor completed");
122 }
123
124
125 // ABSTRACT MIXER: ABSTRACT METHOD IMPLEMENTATIONS
126
127 public Line getLine(Line.Info info) throws LineUnavailableException {
128 Line.Info fullInfo = getLineInfo(info);
129
130 if ((fullInfo != null) && (fullInfo instanceof Port.Info)) {
131 for (int i = 0; i < portInfos.length; i++) {
132 if (fullInfo.equals(portInfos[i])) {
133 return getPort(i);
134 }
135 }
136 }
137 throw new IllegalArgumentException("Line unsupported: " + info);
138 }
139
140
141 public int getMaxLines(Line.Info info) {
142 Line.Info fullInfo = getLineInfo(info);
143
144 // if it's not supported at all, return 0.
145 if (fullInfo == null) {
146 return 0;
147 }
148
149 if (fullInfo instanceof Port.Info) {
150 //return AudioSystem.NOT_SPECIFIED; // if several instances of PortMixerPort
151 return 1;
152 }
153 return 0;
154 }
155
156
157 protected void implOpen() throws LineUnavailableException {
158 if (Printer.trace) Printer.trace(">> PortMixer: implOpen (id="+id+")");
159
160 // open the mixer device
161 id = nOpen(getMixerIndex());
162
163 if (Printer.trace) Printer.trace("<< PortMixer: implOpen succeeded.");
164 }
165
166 protected void implClose() {
167 if (Printer.trace) Printer.trace(">> PortMixer: implClose");
168
169 // close the mixer device
170 long thisID = id;
171 id = 0;
172 nClose(thisID);
173 if (ports != null) {
174 for (int i = 0; i < ports.length; i++) {
175 if (ports[i] != null) {
176 ports[i].disposeControls();
177 }
178 }
179 }
180
181 if (Printer.trace) Printer.trace("<< PortMixer: implClose succeeded");
182 }
183
184 protected void implStart() {}
185 protected void implStop() {}
186
187 // IMPLEMENTATION HELPERS
188
189 private Port.Info getPortInfo(int portIndex, int type) {
190 switch (type) {
191 case SRC_UNKNOWN: return new PortInfo(nGetPortName(getID(), portIndex), true);
192 case SRC_MICROPHONE: return Port.Info.MICROPHONE;
193 case SRC_LINE_IN: return Port.Info.LINE_IN;
194 case SRC_COMPACT_DISC: return Port.Info.COMPACT_DISC;
195
196 case DST_UNKNOWN: return new PortInfo(nGetPortName(getID(), portIndex), false);
197 case DST_SPEAKER: return Port.Info.SPEAKER;
198 case DST_HEADPHONE: return Port.Info.HEADPHONE;
199 case DST_LINE_OUT: return Port.Info.LINE_OUT;
200 }
201 // should never happen...
202 if (Printer.debug) Printer.debug("unknown port type: "+type);
203 return null;
204 }
205
206 int getMixerIndex() {
207 return ((PortMixerProvider.PortMixerInfo) getMixerInfo()).getIndex();
208 }
209
210 Port getPort(int index) {
211 if (ports == null) {
212 ports = new PortMixerPort[portInfos.length];
213 }
214 if (ports[index] == null) {
215 ports[index] = new PortMixerPort((Port.Info)portInfos[index], this, index);
216 return ports[index];
217 }
218 // $$fb TODO: return (Port) (ports[index].clone());
219 return ports[index];
220 }
221
222 long getID() {
223 return id;
224 }
225
226 // INNER CLASSES
227
228 /**
229 * Private inner class representing a Port for the PortMixer.
230 */
231 private static class PortMixerPort extends AbstractLine implements Port {
232 private int portIndex;
233 private long id;
234
235 // CONSTRUCTOR
236 private PortMixerPort(Port.Info info,
237 PortMixer mixer,
238 int portIndex) {
239 super(info, mixer, null);
240 if (Printer.trace) Printer.trace("PortMixerPort CONSTRUCTOR: info: " + info);
241 this.portIndex = portIndex;
242 }
243
244
245 // ABSTRACT METHOD IMPLEMENTATIONS
246
247 // ABSTRACT LINE
248
249 void implOpen() throws LineUnavailableException {
250 if (Printer.trace) Printer.trace(">> PortMixerPort: implOpen().");
251 long newID = ((PortMixer) mixer).getID();
252 if ((id == 0) || (newID != id) || (controls.length == 0)) {
253 id = newID;
254 Vector vector = new Vector();
255 synchronized (vector) {
256 nGetControls(id, portIndex, vector);
257 controls = new Control[vector.size()];
258 for (int i = 0; i < controls.length; i++) {
259 controls[i] = (Control) vector.elementAt(i);
260 }
261 }
262 } else {
263 enableControls(controls, true);
264 }
265 if (Printer.trace) Printer.trace("<< PortMixerPort: implOpen() succeeded");
266 }
267
268 private void enableControls(Control[] controls, boolean enable) {
269 for (int i = 0; i < controls.length; i++) {
270 if (controls[i] instanceof BoolCtrl) {
271 ((BoolCtrl) controls[i]).closed = !enable;
272 }
273 else if (controls[i] instanceof FloatCtrl) {
274 ((FloatCtrl) controls[i]).closed = !enable;
275 }
276 else if (controls[i] instanceof CompoundControl) {
277 enableControls(((CompoundControl) controls[i]).getMemberControls(), enable);
278 }
279 }
280 }
281
282 private void disposeControls() {
283 enableControls(controls, false);
284 controls = new Control[0];
285 }
286
287
288 void implClose() {
289 if (Printer.trace) Printer.trace(">> PortMixerPort: implClose()");
290 // get rid of controls
291 enableControls(controls, false);
292 if (Printer.trace) Printer.trace("<< PortMixerPort: implClose() succeeded");
293 }
294
295 // METHOD OVERRIDES
296
297 // this is very similar to open(AudioFormat, int) in AbstractDataLine...
298 public void open() throws LineUnavailableException {
299 synchronized (mixer) {
300 // if the line is not currently open, try to open it with this format and buffer size
301 if (!isOpen()) {
302 if (Printer.trace) Printer.trace("> PortMixerPort: open");
303 // reserve mixer resources for this line
304 mixer.open(this);
305 try {
306 // open the line. may throw LineUnavailableException.
307 implOpen();
308
309 // if we succeeded, set the open state to true and send events
310 setOpen(true);
311 } catch (LineUnavailableException e) {
312 // release mixer resources for this line and then throw the exception
313 mixer.close(this);
314 throw e;
315 }
316 if (Printer.trace) Printer.trace("< PortMixerPort: open succeeded");
317 }
318 }
319 }
320
321 // this is very similar to close() in AbstractDataLine...
322 public void close() {
323 synchronized (mixer) {
324 if (isOpen()) {
325 if (Printer.trace) Printer.trace("> PortMixerPort.close()");
326
327 // set the open state to false and send events
328 setOpen(false);
329
330 // close resources for this line
331 implClose();
332
333 // release mixer resources for this line
334 mixer.close(this);
335 if (Printer.trace) Printer.trace("< PortMixerPort.close() succeeded");
336 }
337 }
338 }
339
340 } // class PortMixerPort
341
342 /**
343 * Private inner class representing a BooleanControl for PortMixerPort
344 */
345 private static class BoolCtrl extends BooleanControl {
346 // the handle to the native control function
347 private long controlID;
348 private boolean closed = false;
349
350 private static BooleanControl.Type createType(String name) {
351 if (name.equals("Mute")) {
352 return BooleanControl.Type.MUTE;
353 }
354 else if (name.equals("Select")) {
355 // $$fb add as new static type?
356 //return BooleanControl.Type.SELECT;
357 }
358 return new BCT(name);
359 }
360
361
362 private BoolCtrl(long controlID, String name) {
363 this(controlID, createType(name));
364 }
365
366 private BoolCtrl(long controlID, BooleanControl.Type typ) {
367 super(typ, false);
368 this.controlID = controlID;
369 }
370
371 public void setValue(boolean value) {
372 if (!closed) {
373 nControlSetIntValue(controlID, value?1:0);
374 }
375 }
376
377 public boolean getValue() {
378 if (!closed) {
379 // never use any cached values
380 return (nControlGetIntValue(controlID)!=0)?true:false;
381 }
382 // ??
383 return false;
384 }
385
386 /**
387 * inner class for custom types
388 */
389 private static class BCT extends BooleanControl.Type {
390 private BCT(String name) {
391 super(name);
392 }
393 }
394 }
395
396 /**
397 * Private inner class representing a CompoundControl for PortMixerPort
398 */
399 private static class CompCtrl extends CompoundControl {
400 private CompCtrl(String name, Control[] controls) {
401 super(new CCT(name), controls);
402 }
403
404 /**
405 * inner class for custom compound control types
406 */
407 private static class CCT extends CompoundControl.Type {
408 private CCT(String name) {
409 super(name);
410 }
411 }
412 }
413
414 /**
415 * Private inner class representing a BooleanControl for PortMixerPort
416 */
417 private static class FloatCtrl extends FloatControl {
418 // the handle to the native control function
419 private long controlID;
420 private boolean closed = false;
421
422 // predefined float control types. See also Ports.h
423 private final static FloatControl.Type[] FLOAT_CONTROL_TYPES = {
424 null,
425 FloatControl.Type.BALANCE,
426 FloatControl.Type.MASTER_GAIN,
427 FloatControl.Type.PAN,
428 FloatControl.Type.VOLUME
429 };
430
431 private FloatCtrl(long controlID, String name,
432 float min, float max, float precision, String units) {
433 this(controlID, new FCT(name), min, max, precision, units);
434 }
435
436 private FloatCtrl(long controlID, int type,
437 float min, float max, float precision, String units) {
438 this(controlID, FLOAT_CONTROL_TYPES[type], min, max, precision, units);
439 }
440
441 private FloatCtrl(long controlID, FloatControl.Type typ,
442 float min, float max, float precision, String units) {
443 super(typ, min, max, precision, 1000, min, units);
444 this.controlID = controlID;
445 }
446
447 public void setValue(float value) {
448 if (!closed) {
449 nControlSetFloatValue(controlID, value);
450 }
451 }
452
453 public float getValue() {
454 if (!closed) {
455 // never use any cached values
456 return nControlGetFloatValue(controlID);
457 }
458 // ??
459 return getMinimum();
460 }
461
462 /**
463 * inner class for custom types
464 */
465 private static class FCT extends FloatControl.Type {
466 private FCT(String name) {
467 super(name);
468 }
469 }
470 }
471
472 /**
473 * Private inner class representing a port info
474 */
475 private static class PortInfo extends Port.Info {
476 private PortInfo(String name, boolean isSource) {
477 super(Port.class, name, isSource);
478 }
479 }
480
481 // open the mixer with the given index. Returns a handle ID
482 private static native long nOpen(int mixerIndex) throws LineUnavailableException;
483 private static native void nClose(long id);
484
485 // gets the number of ports for this mixer
486 private static native int nGetPortCount(long id);
487
488 // gets the type of the port with this index
489 private static native int nGetPortType(long id, int portIndex);
490
491 // gets the name of the port with this index
492 private static native String nGetPortName(long id, int portIndex);
493
494 // fills the vector with the controls for this port
495 private static native void nGetControls(long id, int portIndex, Vector vector);
496
497 // getters/setters for controls
498 private static native void nControlSetIntValue(long controlID, int value);
499 private static native int nControlGetIntValue(long controlID);
500 private static native void nControlSetFloatValue(long controlID, float value);
501 private static native float nControlGetFloatValue(long controlID);
502
503 }