1 /* 2 * Copyright (c) 1999, 2017, 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.Mixer; 34 35 /** 36 * Abstract Mixer. Implements Mixer (with abstract methods) and specifies 37 * some other common methods for use by our implementation. 38 * 39 * @author Kara Kytle 40 */ 41 //$$fb 2002-07-26: let AbstractMixer be an AbstractLine and NOT an AbstractDataLine! 42 abstract class AbstractMixer extends AbstractLine implements Mixer { 43 44 // STATIC VARIABLES 45 protected static final int PCM = 0; 46 protected static final int ULAW = 1; 47 protected static final int ALAW = 2; 48 49 50 // IMMUTABLE PROPERTIES 51 52 /** 53 * Info object describing this mixer. 54 */ 55 private final Mixer.Info mixerInfo; 56 57 /** 58 * source lines provided by this mixer 59 */ 60 protected Line.Info[] sourceLineInfo; 61 62 /** 63 * target lines provided by this mixer 64 */ 65 protected Line.Info[] targetLineInfo; 66 67 /** 68 * if any line of this mixer is started 69 */ 70 private boolean started = false; 71 72 /** 73 * if this mixer had been opened manually with open() 74 * If it was, then it won't be closed automatically, 75 * only when close() is called manually. 76 */ 77 private boolean manuallyOpened = false; 78 79 // STATE VARIABLES 80 81 /** 82 * Source lines (ports) currently open. 83 */ 84 private final Vector<Line> sourceLines = new Vector<>(); 85 86 /** 87 * Target lines currently open. 88 */ 89 private final Vector<Line> targetLines = new Vector<>(); 90 91 /** 92 * Constructs a new AbstractMixer. 93 * @param mixerInfo the mixer with which this line is associated 94 * @param controls set of supported controls 95 */ 96 protected AbstractMixer(Mixer.Info mixerInfo, 97 Control[] controls, 98 Line.Info[] sourceLineInfo, 99 Line.Info[] targetLineInfo) { 100 101 // Line.Info, AbstractMixer, Control[] 102 super(new Line.Info(Mixer.class), null, controls); 103 104 // setup the line part 105 this.mixer = this; 106 if (controls == null) { 107 controls = new Control[0]; 108 } 109 110 // setup the mixer part 111 this.mixerInfo = mixerInfo; 112 this.sourceLineInfo = sourceLineInfo; 113 this.targetLineInfo = targetLineInfo; 114 } 115 116 // MIXER METHODS 117 118 @Override 119 public final Mixer.Info getMixerInfo() { 120 return mixerInfo; 121 } 122 123 @Override 124 public final Line.Info[] getSourceLineInfo() { 125 Line.Info[] localArray = new Line.Info[sourceLineInfo.length]; 126 System.arraycopy(sourceLineInfo, 0, localArray, 0, sourceLineInfo.length); 127 return localArray; 128 } 129 130 @Override 131 public final Line.Info[] getTargetLineInfo() { 132 Line.Info[] localArray = new Line.Info[targetLineInfo.length]; 133 System.arraycopy(targetLineInfo, 0, localArray, 0, targetLineInfo.length); 134 return localArray; 135 } 136 137 @Override 138 public final Line.Info[] getSourceLineInfo(Line.Info info) { 139 140 int i; 141 Vector<Line.Info> vec = new Vector<>(); 142 143 for (i = 0; i < sourceLineInfo.length; i++) { 144 145 if (info.matches(sourceLineInfo[i])) { 146 vec.addElement(sourceLineInfo[i]); 147 } 148 } 149 150 Line.Info[] returnedArray = new Line.Info[vec.size()]; 151 for (i = 0; i < returnedArray.length; i++) { 152 returnedArray[i] = vec.elementAt(i); 153 } 154 155 return returnedArray; 156 } 157 158 @Override 159 public final Line.Info[] getTargetLineInfo(Line.Info info) { 160 161 int i; 162 Vector<Line.Info> vec = new Vector<>(); 163 164 for (i = 0; i < targetLineInfo.length; i++) { 165 166 if (info.matches(targetLineInfo[i])) { 167 vec.addElement(targetLineInfo[i]); 168 } 169 } 170 171 Line.Info[] returnedArray = new Line.Info[vec.size()]; 172 for (i = 0; i < returnedArray.length; i++) { 173 returnedArray[i] = vec.elementAt(i); 174 } 175 176 return returnedArray; 177 } 178 179 @Override 180 public final boolean isLineSupported(Line.Info info) { 181 182 int i; 183 184 for (i = 0; i < sourceLineInfo.length; i++) { 185 186 if (info.matches(sourceLineInfo[i])) { 187 return true; 188 } 189 } 190 191 for (i = 0; i < targetLineInfo.length; i++) { 192 193 if (info.matches(targetLineInfo[i])) { 194 return true; 195 } 196 } 197 198 return false; 199 } 200 201 @Override 202 public abstract Line getLine(Line.Info info) throws LineUnavailableException; 203 204 @Override 205 public abstract int getMaxLines(Line.Info info); 206 207 protected abstract void implOpen() throws LineUnavailableException; 208 protected abstract void implStart(); 209 protected abstract void implStop(); 210 protected abstract void implClose(); 211 212 @Override 213 public final Line[] getSourceLines() { 214 215 Line[] localLines; 216 217 synchronized(sourceLines) { 218 219 localLines = new Line[sourceLines.size()]; 220 221 for (int i = 0; i < localLines.length; i++) { 222 localLines[i] = sourceLines.elementAt(i); 223 } 224 } 225 226 return localLines; 227 } 228 229 @Override 230 public final Line[] getTargetLines() { 231 232 Line[] localLines; 233 234 synchronized(targetLines) { 235 236 localLines = new Line[targetLines.size()]; 237 238 for (int i = 0; i < localLines.length; i++) { 239 localLines[i] = targetLines.elementAt(i); 240 } 241 } 242 243 return localLines; 244 } 245 246 /** 247 * Default implementation always throws an exception. 248 */ 249 @Override 250 public final void synchronize(Line[] lines, boolean maintainSync) { 251 throw new IllegalArgumentException("Synchronization not supported by this mixer."); 252 } 253 254 /** 255 * Default implementation always throws an exception. 256 */ 257 @Override 258 public final void unsynchronize(Line[] lines) { 259 throw new IllegalArgumentException("Synchronization not supported by this mixer."); 260 } 261 262 /** 263 * Default implementation always returns false. 264 */ 265 @Override 266 public final boolean isSynchronizationSupported(Line[] lines, 267 boolean maintainSync) { 268 return false; 269 } 270 271 // OVERRIDES OF ABSTRACT DATA LINE METHODS 272 273 /** 274 * This implementation tries to open the mixer with its current format and buffer size settings. 275 */ 276 @Override 277 public final synchronized void open() throws LineUnavailableException { 278 open(true); 279 } 280 281 /** 282 * This implementation tries to open the mixer with its current format and buffer size settings. 283 */ 284 final synchronized void open(boolean manual) throws LineUnavailableException { 285 if (Printer.trace) Printer.trace(">> AbstractMixer: open()"); 286 if (!isOpen()) { 287 implOpen(); 288 // if the mixer is not currently open, set open to true and send event 289 setOpen(true); 290 if (manual) { 291 manuallyOpened = true; 292 } 293 } 294 295 if (Printer.trace) Printer.trace("<< AbstractMixer: open() succeeded"); 296 } 297 298 // METHOD FOR INTERNAL IMPLEMENTATION USE 299 300 /** 301 * The default implementation of this method just determines whether 302 * this line is a source or target line, calls open(no-arg) on the 303 * mixer, and adds the line to the appropriate vector. 304 * The mixer may be opened at a format different than the line's 305 * format if it is a DataLine. 306 */ 307 final synchronized void open(Line line) throws LineUnavailableException { 308 309 if (Printer.trace) Printer.trace(">> AbstractMixer: open(line = " + line + ")"); 310 311 // $$kk: 06.11.99: ignore ourselves for now 312 if (this.equals(line)) { 313 if (Printer.trace) Printer.trace("<< AbstractMixer: open(" + line + ") nothing done"); 314 return; 315 } 316 317 // source line? 318 if (isSourceLine(line.getLineInfo())) { 319 if (! sourceLines.contains(line) ) { 320 // call the no-arg open method for the mixer; it should open at its 321 // default format if it is not open yet 322 open(false); 323 324 // we opened successfully! add the line to the list 325 sourceLines.addElement(line); 326 } 327 } else { 328 // target line? 329 if(isTargetLine(line.getLineInfo())) { 330 if (! targetLines.contains(line) ) { 331 // call the no-arg open method for the mixer; it should open at its 332 // default format if it is not open yet 333 open(false); 334 335 // we opened successfully! add the line to the list 336 targetLines.addElement(line); 337 } 338 } else { 339 if (Printer.err) Printer.err("Unknown line received for AbstractMixer.open(Line): " + line); 340 } 341 } 342 343 if (Printer.trace) Printer.trace("<< AbstractMixer: open(" + line + ") completed"); 344 } 345 346 /** 347 * Removes this line from the list of open source lines and 348 * open target lines, if it exists in either. 349 * If the list is now empty, closes the mixer. 350 */ 351 final synchronized void close(Line line) { 352 353 if (Printer.trace) Printer.trace(">> AbstractMixer: close(" + line + ")"); 354 355 // $$kk: 06.11.99: ignore ourselves for now 356 if (this.equals(line)) { 357 if (Printer.trace) Printer.trace("<< AbstractMixer: close(" + line + ") nothing done"); 358 return; 359 } 360 361 sourceLines.removeElement(line); 362 targetLines.removeElement(line); 363 364 if (Printer.debug) Printer.debug("AbstractMixer: close(line): sourceLines.size() now: " + sourceLines.size()); 365 if (Printer.debug) Printer.debug("AbstractMixer: close(line): targetLines.size() now: " + targetLines.size()); 366 367 368 if (sourceLines.isEmpty() && targetLines.isEmpty() && !manuallyOpened) { 369 if (Printer.trace) Printer.trace("AbstractMixer: close(" + line + "): need to close the mixer"); 370 close(); 371 } 372 373 if (Printer.trace) Printer.trace("<< AbstractMixer: close(" + line + ") succeeded"); 374 } 375 376 /** 377 * Close all lines and then close this mixer. 378 */ 379 @Override 380 public final synchronized void close() { 381 if (Printer.trace) Printer.trace(">> AbstractMixer: close()"); 382 if (isOpen()) { 383 // close all source lines 384 Line[] localLines = getSourceLines(); 385 for (int i = 0; i<localLines.length; i++) { 386 localLines[i].close(); 387 } 388 389 // close all target lines 390 localLines = getTargetLines(); 391 for (int i = 0; i<localLines.length; i++) { 392 localLines[i].close(); 393 } 394 395 implClose(); 396 397 // set the open state to false and send events 398 setOpen(false); 399 } 400 manuallyOpened = false; 401 if (Printer.trace) Printer.trace("<< AbstractMixer: close() succeeded"); 402 } 403 404 /** 405 * Starts the mixer. 406 */ 407 final synchronized void start(Line line) { 408 409 if (Printer.trace) Printer.trace(">> AbstractMixer: start(" + line + ")"); 410 411 // $$kk: 06.11.99: ignore ourselves for now 412 if (this.equals(line)) { 413 if (Printer.trace) Printer.trace("<< AbstractMixer: start(" + line + ") nothing done"); 414 return; 415 } 416 417 // we just start the mixer regardless of anything else here. 418 if (!started) { 419 if (Printer.debug) Printer.debug("AbstractMixer: start(line): starting the mixer"); 420 implStart(); 421 started = true; 422 } 423 424 if (Printer.trace) Printer.trace("<< AbstractMixer: start(" + line + ") succeeded"); 425 } 426 427 /** 428 * Stops the mixer if this was the last running line. 429 */ 430 final synchronized void stop(Line line) { 431 432 if (Printer.trace) Printer.trace(">> AbstractMixer: stop(" + line + ")"); 433 434 // $$kk: 06.11.99: ignore ourselves for now 435 if (this.equals(line)) { 436 if (Printer.trace) Printer.trace("<< AbstractMixer: stop(" + line + ") nothing done"); 437 return; 438 } 439 440 @SuppressWarnings("unchecked") 441 Vector<Line> localSourceLines = (Vector<Line>)sourceLines.clone(); 442 for (int i = 0; i < localSourceLines.size(); i++) { 443 444 // if any other open line is running, return 445 446 // this covers clips and source data lines 447 if (localSourceLines.elementAt(i) instanceof AbstractDataLine) { 448 AbstractDataLine sourceLine = (AbstractDataLine)localSourceLines.elementAt(i); 449 if ( sourceLine.isStartedRunning() && (!sourceLine.equals(line)) ) { 450 if (Printer.trace) Printer.trace("<< AbstractMixer: stop(" + line + ") found running sourceLine: " + sourceLine); 451 return; 452 } 453 } 454 } 455 456 @SuppressWarnings("unchecked") 457 Vector<Line> localTargetLines = (Vector<Line>)targetLines.clone(); 458 for (int i = 0; i < localTargetLines.size(); i++) { 459 460 // if any other open line is running, return 461 // this covers target data lines 462 if (localTargetLines.elementAt(i) instanceof AbstractDataLine) { 463 AbstractDataLine targetLine = (AbstractDataLine)localTargetLines.elementAt(i); 464 if ( targetLine.isStartedRunning() && (!targetLine.equals(line)) ) { 465 if (Printer.trace) Printer.trace("<< AbstractMixer: stop(" + line + ") found running targetLine: " + targetLine); 466 return; 467 } 468 } 469 } 470 471 // otherwise, stop 472 if (Printer.debug) Printer.debug("AbstractMixer: stop(line): stopping the mixer"); 473 started = false; 474 implStop(); 475 476 if (Printer.trace) Printer.trace("<< AbstractMixer: stop(" + line + ") succeeded"); 477 } 478 479 /** 480 * Determines whether this is a source line for this mixer. 481 * Right now this just checks whether it's supported, but should 482 * check whether it actually belongs to this mixer.... 483 */ 484 final boolean isSourceLine(Line.Info info) { 485 486 for (int i = 0; i < sourceLineInfo.length; i++) { 487 if (info.matches(sourceLineInfo[i])) { 488 return true; 489 } 490 } 491 492 return false; 493 } 494 495 /** 496 * Determines whether this is a target line for this mixer. 497 * Right now this just checks whether it's supported, but should 498 * check whether it actually belongs to this mixer.... 499 */ 500 final boolean isTargetLine(Line.Info info) { 501 502 for (int i = 0; i < targetLineInfo.length; i++) { 503 if (info.matches(targetLineInfo[i])) { 504 return true; 505 } 506 } 507 508 return false; 509 } 510 511 /** 512 * Returns the first complete Line.Info object it finds that 513 * matches the one specified, or null if no matching Line.Info 514 * object is found. 515 */ 516 final Line.Info getLineInfo(Line.Info info) { 517 if (info == null) { 518 return null; 519 } 520 // $$kk: 05.31.99: need to change this so that 521 // the format and buffer size get set in the 522 // returned info object for data lines?? 523 for (int i = 0; i < sourceLineInfo.length; i++) { 524 if (info.matches(sourceLineInfo[i])) { 525 return sourceLineInfo[i]; 526 } 527 } 528 529 for (int i = 0; i < targetLineInfo.length; i++) { 530 if (info.matches(targetLineInfo[i])) { 531 return targetLineInfo[i]; 532 } 533 } 534 return null; 535 } 536 }