1 /* 2 * Copyright (c) 2002, 2014, 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 final 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(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 final class PortMixerPort extends AbstractLine 232 implements Port { 233 234 private final int portIndex; 235 private long id; 236 237 // CONSTRUCTOR 238 private PortMixerPort(Port.Info info, 239 PortMixer mixer, 240 int portIndex) { 241 super(info, mixer, null); 242 if (Printer.trace) Printer.trace("PortMixerPort CONSTRUCTOR: info: " + info); 243 this.portIndex = portIndex; 244 } 245 246 247 // ABSTRACT METHOD IMPLEMENTATIONS 248 249 // ABSTRACT LINE 250 251 void implOpen() throws LineUnavailableException { 252 if (Printer.trace) Printer.trace(">> PortMixerPort: implOpen()."); 253 long newID = ((PortMixer) mixer).getID(); 254 if ((id == 0) || (newID != id) || (controls.length == 0)) { 255 id = newID; 256 Vector<Control> vector = new Vector<>(); 257 synchronized (vector) { 258 nGetControls(id, portIndex, vector); 259 controls = new Control[vector.size()]; 260 for (int i = 0; i < controls.length; i++) { 261 controls[i] = vector.elementAt(i); 262 } 263 } 264 } else { 265 enableControls(controls, true); 266 } 267 if (Printer.trace) Printer.trace("<< PortMixerPort: implOpen() succeeded"); 268 } 269 270 private void enableControls(Control[] controls, boolean enable) { 271 for (int i = 0; i < controls.length; i++) { 272 if (controls[i] instanceof BoolCtrl) { 273 ((BoolCtrl) controls[i]).closed = !enable; 274 } 275 else if (controls[i] instanceof FloatCtrl) { 276 ((FloatCtrl) controls[i]).closed = !enable; 277 } 278 else if (controls[i] instanceof CompoundControl) { 279 enableControls(((CompoundControl) controls[i]).getMemberControls(), enable); 280 } 281 } 282 } 283 284 private void disposeControls() { 285 enableControls(controls, false); 286 controls = new Control[0]; 287 } 288 289 290 void implClose() { 291 if (Printer.trace) Printer.trace(">> PortMixerPort: implClose()"); 292 // get rid of controls 293 enableControls(controls, false); 294 if (Printer.trace) Printer.trace("<< PortMixerPort: implClose() succeeded"); 295 } 296 297 // METHOD OVERRIDES 298 299 // this is very similar to open(AudioFormat, int) in AbstractDataLine... 300 public void open() throws LineUnavailableException { 301 synchronized (mixer) { 302 // if the line is not currently open, try to open it with this format and buffer size 303 if (!isOpen()) { 304 if (Printer.trace) Printer.trace("> PortMixerPort: open"); 305 // reserve mixer resources for this line 306 mixer.open(this); 307 try { 308 // open the line. may throw LineUnavailableException. 309 implOpen(); 310 311 // if we succeeded, set the open state to true and send events 312 setOpen(true); 313 } catch (LineUnavailableException e) { 314 // release mixer resources for this line and then throw the exception 315 mixer.close(this); 316 throw e; 317 } 318 if (Printer.trace) Printer.trace("< PortMixerPort: open succeeded"); 319 } 320 } 321 } 322 323 // this is very similar to close() in AbstractDataLine... 324 public void close() { 325 synchronized (mixer) { 326 if (isOpen()) { 327 if (Printer.trace) Printer.trace("> PortMixerPort.close()"); 328 329 // set the open state to false and send events 330 setOpen(false); 331 332 // close resources for this line 333 implClose(); 334 335 // release mixer resources for this line 336 mixer.close(this); 337 if (Printer.trace) Printer.trace("< PortMixerPort.close() succeeded"); 338 } 339 } 340 } 341 342 } // class PortMixerPort 343 344 /** 345 * Private inner class representing a BooleanControl for PortMixerPort 346 */ 347 private static final class BoolCtrl extends BooleanControl { 348 // the handle to the native control function 349 private final long controlID; 350 private boolean closed = false; 351 352 private static BooleanControl.Type createType(String name) { 353 if (name.equals("Mute")) { 354 return BooleanControl.Type.MUTE; 355 } 356 else if (name.equals("Select")) { 357 // $$fb add as new static type? 358 //return BooleanControl.Type.SELECT; 359 } 360 return new BCT(name); 361 } 362 363 364 private BoolCtrl(long controlID, String name) { 365 this(controlID, createType(name)); 366 } 367 368 private BoolCtrl(long controlID, BooleanControl.Type typ) { 369 super(typ, false); 370 this.controlID = controlID; 371 } 372 373 public void setValue(boolean value) { 374 if (!closed) { 375 nControlSetIntValue(controlID, value?1:0); 376 } 377 } 378 379 public boolean getValue() { 380 if (!closed) { 381 // never use any cached values 382 return (nControlGetIntValue(controlID)!=0)?true:false; 383 } 384 // ?? 385 return false; 386 } 387 388 /** 389 * inner class for custom types 390 */ 391 private static final class BCT extends BooleanControl.Type { 392 private BCT(String name) { 393 super(name); 394 } 395 } 396 } 397 398 /** 399 * Private inner class representing a CompoundControl for PortMixerPort 400 */ 401 private static final class CompCtrl extends CompoundControl { 402 private CompCtrl(String name, Control[] controls) { 403 super(new CCT(name), controls); 404 } 405 406 /** 407 * inner class for custom compound control types 408 */ 409 private static final class CCT extends CompoundControl.Type { 410 private CCT(String name) { 411 super(name); 412 } 413 } 414 } 415 416 /** 417 * Private inner class representing a BooleanControl for PortMixerPort 418 */ 419 private static final class FloatCtrl extends FloatControl { 420 // the handle to the native control function 421 private final long controlID; 422 private boolean closed = false; 423 424 // predefined float control types. See also Ports.h 425 private final static FloatControl.Type[] FLOAT_CONTROL_TYPES = { 426 null, 427 FloatControl.Type.BALANCE, 428 FloatControl.Type.MASTER_GAIN, 429 FloatControl.Type.PAN, 430 FloatControl.Type.VOLUME 431 }; 432 433 private FloatCtrl(long controlID, String name, 434 float min, float max, float precision, String units) { 435 this(controlID, new FCT(name), min, max, precision, units); 436 } 437 438 private FloatCtrl(long controlID, int type, 439 float min, float max, float precision, String units) { 440 this(controlID, FLOAT_CONTROL_TYPES[type], min, max, precision, units); 441 } 442 443 private FloatCtrl(long controlID, FloatControl.Type typ, 444 float min, float max, float precision, String units) { 445 super(typ, min, max, precision, 1000, min, units); 446 this.controlID = controlID; 447 } 448 449 public void setValue(float value) { 450 if (!closed) { 451 nControlSetFloatValue(controlID, value); 452 } 453 } 454 455 public float getValue() { 456 if (!closed) { 457 // never use any cached values 458 return nControlGetFloatValue(controlID); 459 } 460 // ?? 461 return getMinimum(); 462 } 463 464 /** 465 * inner class for custom types 466 */ 467 private static final class FCT extends FloatControl.Type { 468 private FCT(String name) { 469 super(name); 470 } 471 } 472 } 473 474 /** 475 * Private inner class representing a port info 476 */ 477 private static final class PortInfo extends Port.Info { 478 private PortInfo(String name, boolean isSource) { 479 super(Port.class, name, isSource); 480 } 481 } 482 483 // open the mixer with the given index. Returns a handle ID 484 private static native long nOpen(int mixerIndex) throws LineUnavailableException; 485 private static native void nClose(long id); 486 487 // gets the number of ports for this mixer 488 private static native int nGetPortCount(long id); 489 490 // gets the type of the port with this index 491 private static native int nGetPortType(long id, int portIndex); 492 493 // gets the name of the port with this index 494 private static native String nGetPortName(long id, int portIndex); 495 496 // fills the vector with the controls for this port 497 @SuppressWarnings("rawtypes") 498 private static native void nGetControls(long id, int portIndex, Vector vector); 499 500 // getters/setters for controls 501 private static native void nControlSetIntValue(long controlID, int value); 502 private static native int nControlGetIntValue(long controlID); 503 private static native void nControlSetFloatValue(long controlID, float value); 504 private static native float nControlGetFloatValue(long controlID); 505 506 }