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