1 /*
2  * Copyright (c) 2002, 2019, 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.BooleanControl;
31 import javax.sound.sampled.CompoundControl;
32 import javax.sound.sampled.Control;
33 import javax.sound.sampled.FloatControl;
34 import javax.sound.sampled.Line;
35 import javax.sound.sampled.LineUnavailableException;
36 import javax.sound.sampled.Port;
37 
38 /**
39  * A Mixer which only provides Ports.
40  *
41  * @author Florian Bomers
42  */
43 final class PortMixer extends AbstractMixer {
44 
45     private static final int SRC_UNKNOWN      = 0x01;
46     private static final int SRC_MICROPHONE   = 0x02;
47     private static final int SRC_LINE_IN      = 0x03;
48     private static final int SRC_COMPACT_DISC = 0x04;
49     private static final int SRC_MASK         = 0xFF;
50 
51     private static final int DST_UNKNOWN      = 0x0100;
52     private static final int DST_SPEAKER      = 0x0200;
53     private static final int DST_HEADPHONE    = 0x0300;
54     private static final int DST_LINE_OUT     = 0x0400;
55     private static final int DST_MASK         = 0xFF00;
56 
57     private final Port.Info[] portInfos;
58     // cache of instantiated ports
59     private PortMixerPort[] ports;
60 
61     // instance ID of the native implementation
62     private long id = 0;
63 
PortMixer(PortMixerProvider.PortMixerInfo portMixerInfo)64     PortMixer(PortMixerProvider.PortMixerInfo portMixerInfo) {
65         // pass in Line.Info, mixer, controls
66         super(portMixerInfo,              // Mixer.Info
67               null,                       // Control[]
68               null,                       // Line.Info[] sourceLineInfo
69               null);                      // Line.Info[] targetLineInfo
70         int count = 0;
71         int srcLineCount = 0;
72         int dstLineCount = 0;
73 
74         try {
75             try {
76                 id = nOpen(getMixerIndex());
77                 if (id != 0) {
78                     count = nGetPortCount(id);
79                     if (count < 0) {
80                         count = 0;
81                     }
82                 }
83             } catch (Exception e) {}
84 
85             portInfos = new Port.Info[count];
86 
87             for (int i = 0; i < count; i++) {
88                 int type = nGetPortType(id, i);
89                 srcLineCount += ((type & SRC_MASK) != 0)?1:0;
90                 dstLineCount += ((type & DST_MASK) != 0)?1:0;
91                 portInfos[i] = getPortInfo(i, type);
92             }
93         } finally {
94             if (id != 0) {
95                 nClose(id);
96             }
97             id = 0;
98         }
99 
100         // fill sourceLineInfo and targetLineInfos with copies of the ones in portInfos
101         sourceLineInfo = new Port.Info[srcLineCount];
102         targetLineInfo = new Port.Info[dstLineCount];
103 
104         srcLineCount = 0; dstLineCount = 0;
105         for (int i = 0; i < count; i++) {
106             if (portInfos[i].isSource()) {
107                 sourceLineInfo[srcLineCount++] = portInfos[i];
108             } else {
109                 targetLineInfo[dstLineCount++] = portInfos[i];
110             }
111         }
112     }
113 
114     @Override
getLine(Line.Info info)115     public Line getLine(Line.Info info) throws LineUnavailableException {
116         Line.Info fullInfo = getLineInfo(info);
117 
118         if ((fullInfo != null) && (fullInfo instanceof Port.Info)) {
119             for (int i = 0; i < portInfos.length; i++) {
120                 if (fullInfo.equals(portInfos[i])) {
121                     return getPort(i);
122                 }
123             }
124         }
125         throw new IllegalArgumentException("Line unsupported: " + info);
126     }
127 
128     @Override
getMaxLines(Line.Info info)129     public int getMaxLines(Line.Info info) {
130         Line.Info fullInfo = getLineInfo(info);
131 
132         // if it's not supported at all, return 0.
133         if (fullInfo == null) {
134             return 0;
135         }
136 
137         if (fullInfo instanceof Port.Info) {
138             //return AudioSystem.NOT_SPECIFIED; // if several instances of PortMixerPort
139             return 1;
140         }
141         return 0;
142     }
143 
144     @Override
implOpen()145     protected void implOpen() throws LineUnavailableException {
146         // open the mixer device
147         id = nOpen(getMixerIndex());
148     }
149 
150     @Override
implClose()151     protected void implClose() {
152         // close the mixer device
153         long thisID = id;
154         id = 0;
155         nClose(thisID);
156         if (ports != null) {
157             for (int i = 0; i < ports.length; i++) {
158                 if (ports[i] != null) {
159                     ports[i].disposeControls();
160                 }
161             }
162         }
163     }
164 
165     @Override
implStart()166     protected void implStart() {}
167     @Override
implStop()168     protected void implStop() {}
169 
getPortInfo(int portIndex, int type)170     private Port.Info getPortInfo(int portIndex, int type) {
171         switch (type) {
172         case SRC_UNKNOWN:      return new PortInfo(nGetPortName(getID(), portIndex), true);
173         case SRC_MICROPHONE:   return Port.Info.MICROPHONE;
174         case SRC_LINE_IN:      return Port.Info.LINE_IN;
175         case SRC_COMPACT_DISC: return Port.Info.COMPACT_DISC;
176 
177         case DST_UNKNOWN:      return new PortInfo(nGetPortName(getID(), portIndex), false);
178         case DST_SPEAKER:      return Port.Info.SPEAKER;
179         case DST_HEADPHONE:    return Port.Info.HEADPHONE;
180         case DST_LINE_OUT:     return Port.Info.LINE_OUT;
181         }
182         // should never happen...
183         if (Printer.err) Printer.err("unknown port type: "+type);
184         return null;
185     }
186 
getMixerIndex()187     int getMixerIndex() {
188         return ((PortMixerProvider.PortMixerInfo) getMixerInfo()).getIndex();
189     }
190 
getPort(int index)191     Port getPort(int index) {
192         if (ports == null) {
193             ports = new PortMixerPort[portInfos.length];
194         }
195         if (ports[index] == null) {
196             ports[index] = new PortMixerPort(portInfos[index], this, index);
197             return ports[index];
198         }
199         // $$fb TODO: return (Port) (ports[index].clone());
200         return ports[index];
201     }
202 
getID()203     long getID() {
204         return id;
205     }
206 
207     /**
208      * Private inner class representing a Port for the PortMixer.
209      */
210     private static final class PortMixerPort extends AbstractLine
211             implements Port {
212 
213         private final int portIndex;
214         private long id;
215 
PortMixerPort(Port.Info info, PortMixer mixer, int portIndex)216         private PortMixerPort(Port.Info info,
217                               PortMixer mixer,
218                               int portIndex) {
219             super(info, mixer, null);
220             this.portIndex = portIndex;
221         }
222 
implOpen()223         void implOpen() throws LineUnavailableException {
224             long newID = ((PortMixer) mixer).getID();
225             if ((id == 0) || (newID != id) || (controls.length == 0)) {
226                 id = newID;
227                 Vector<Control> vector = new Vector<>();
228                 synchronized (vector) {
229                     nGetControls(id, portIndex, vector);
230                     controls = new Control[vector.size()];
231                     for (int i = 0; i < controls.length; i++) {
232                         controls[i] = vector.elementAt(i);
233                     }
234                 }
235             } else {
236                 enableControls(controls, true);
237             }
238         }
239 
enableControls(Control[] controls, boolean enable)240         private void enableControls(Control[] controls, boolean enable) {
241             for (int i = 0; i < controls.length; i++) {
242                 if (controls[i] instanceof BoolCtrl) {
243                     ((BoolCtrl) controls[i]).closed = !enable;
244                 }
245                 else if (controls[i] instanceof FloatCtrl) {
246                     ((FloatCtrl) controls[i]).closed = !enable;
247                 }
248                 else if (controls[i] instanceof CompoundControl) {
249                     enableControls(((CompoundControl) controls[i]).getMemberControls(), enable);
250                 }
251             }
252         }
253 
disposeControls()254         private void disposeControls() {
255             enableControls(controls, false);
256             controls = new Control[0];
257         }
258 
implClose()259         void implClose() {
260             // get rid of controls
261             enableControls(controls, false);
262         }
263 
264         // this is very similar to open(AudioFormat, int) in AbstractDataLine...
265         @Override
open()266         public void open() throws LineUnavailableException {
267             synchronized (mixer) {
268                 // if the line is not currently open, try to open it with this format and buffer size
269                 if (!isOpen()) {
270                     // reserve mixer resources for this line
271                     mixer.open(this);
272                     try {
273                         // open the line.  may throw LineUnavailableException.
274                         implOpen();
275 
276                         // if we succeeded, set the open state to true and send events
277                         setOpen(true);
278                     } catch (LineUnavailableException e) {
279                         // release mixer resources for this line and then throw the exception
280                         mixer.close(this);
281                         throw e;
282                     }
283                 }
284             }
285         }
286 
287         // this is very similar to close() in AbstractDataLine...
288         @Override
close()289         public void close() {
290             synchronized (mixer) {
291                 if (isOpen()) {
292                     // set the open state to false and send events
293                     setOpen(false);
294 
295                     // close resources for this line
296                     implClose();
297 
298                     // release mixer resources for this line
299                     mixer.close(this);
300                 }
301             }
302         }
303 
304     } // class PortMixerPort
305 
306     /**
307      * Private inner class representing a BooleanControl for PortMixerPort.
308      */
309     private static final class BoolCtrl extends BooleanControl {
310         // the handle to the native control function
311         private final long controlID;
312         private boolean closed = false;
313 
createType(String name)314         private static BooleanControl.Type createType(String name) {
315             if (name.equals("Mute")) {
316                 return BooleanControl.Type.MUTE;
317             }
318             else if (name.equals("Select")) {
319                 // $$fb add as new static type?
320                 //return BooleanControl.Type.SELECT;
321             }
322             return new BCT(name);
323         }
324 
BoolCtrl(long controlID, String name)325         private BoolCtrl(long controlID, String name) {
326             this(controlID, createType(name));
327         }
328 
BoolCtrl(long controlID, BooleanControl.Type typ)329         private BoolCtrl(long controlID, BooleanControl.Type typ) {
330             super(typ, false);
331             this.controlID = controlID;
332         }
333 
334         @Override
setValue(boolean value)335         public void setValue(boolean value) {
336             if (!closed) {
337                 nControlSetIntValue(controlID, value?1:0);
338             }
339         }
340 
341         @Override
getValue()342         public boolean getValue() {
343             if (!closed) {
344                 // never use any cached values
345                 return (nControlGetIntValue(controlID)!=0)?true:false;
346             }
347             // ??
348             return false;
349         }
350 
351         /**
352          * inner class for custom types.
353          */
354         private static final class BCT extends BooleanControl.Type {
BCT(String name)355             private BCT(String name) {
356                 super(name);
357             }
358         }
359     }
360 
361     /**
362      * Private inner class representing a CompoundControl for PortMixerPort.
363      */
364     private static final class CompCtrl extends CompoundControl {
CompCtrl(String name, Control[] controls)365         private CompCtrl(String name, Control[] controls) {
366             super(new CCT(name), controls);
367         }
368 
369         /**
370          * inner class for custom compound control types.
371          */
372         private static final class CCT extends CompoundControl.Type {
CCT(String name)373             private CCT(String name) {
374                 super(name);
375             }
376         }
377     }
378 
379     /**
380      * Private inner class representing a BooleanControl for PortMixerPort.
381      */
382     private static final class FloatCtrl extends FloatControl {
383         // the handle to the native control function
384         private final long controlID;
385         private boolean closed = false;
386 
387         // predefined float control types. See also Ports.h
388         private static final FloatControl.Type[] FLOAT_CONTROL_TYPES = {
389             null,
390             FloatControl.Type.BALANCE,
391             FloatControl.Type.MASTER_GAIN,
392             FloatControl.Type.PAN,
393             FloatControl.Type.VOLUME
394         };
395 
FloatCtrl(long controlID, String name, float min, float max, float precision, String units)396         private FloatCtrl(long controlID, String name,
397                           float min, float max, float precision, String units) {
398             this(controlID, new FCT(name), min, max, precision, units);
399         }
400 
FloatCtrl(long controlID, int type, float min, float max, float precision, String units)401         private FloatCtrl(long controlID, int type,
402                           float min, float max, float precision, String units) {
403             this(controlID, FLOAT_CONTROL_TYPES[type], min, max, precision, units);
404         }
405 
FloatCtrl(long controlID, FloatControl.Type typ, float min, float max, float precision, String units)406         private FloatCtrl(long controlID, FloatControl.Type typ,
407                          float min, float max, float precision, String units) {
408             super(typ, min, max, precision, 1000, min, units);
409             this.controlID = controlID;
410         }
411 
412         @Override
setValue(float value)413         public void setValue(float value) {
414             if (!closed) {
415                 nControlSetFloatValue(controlID, value);
416             }
417         }
418 
419         @Override
getValue()420         public float getValue() {
421             if (!closed) {
422                 // never use any cached values
423                 return nControlGetFloatValue(controlID);
424             }
425             // ??
426             return getMinimum();
427         }
428 
429         /**
430          * inner class for custom types.
431          */
432         private static final class FCT extends FloatControl.Type {
FCT(String name)433             private FCT(String name) {
434                 super(name);
435             }
436         }
437     }
438 
439     /**
440      * Private inner class representing a port info.
441      */
442     private static final class PortInfo extends Port.Info {
PortInfo(String name, boolean isSource)443         private PortInfo(String name, boolean isSource) {
444             super(Port.class, name, isSource);
445         }
446     }
447 
448     // open the mixer with the given index. Returns a handle ID
nOpen(int mixerIndex)449     private static native long nOpen(int mixerIndex) throws LineUnavailableException;
nClose(long id)450     private static native void nClose(long id);
451 
452     // gets the number of ports for this mixer
nGetPortCount(long id)453     private static native int nGetPortCount(long id);
454 
455     // gets the type of the port with this index
nGetPortType(long id, int portIndex)456     private static native int nGetPortType(long id, int portIndex);
457 
458     // gets the name of the port with this index
nGetPortName(long id, int portIndex)459     private static native String nGetPortName(long id, int portIndex);
460 
461     // fills the vector with the controls for this port
462     @SuppressWarnings("rawtypes")
nGetControls(long id, int portIndex, Vector vector)463     private static native void nGetControls(long id, int portIndex, Vector vector);
464 
465     // getters/setters for controls
nControlSetIntValue(long controlID, int value)466     private static native void nControlSetIntValue(long controlID, int value);
nControlGetIntValue(long controlID)467     private static native int nControlGetIntValue(long controlID);
nControlSetFloatValue(long controlID, float value)468     private static native void nControlSetFloatValue(long controlID, float value);
nControlGetFloatValue(long controlID)469     private static native float nControlGetFloatValue(long controlID);
470 
471 }
472