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