1 /* 2 * Copyright (c) 1999, 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.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 */ AbstractMixer(Mixer.Info mixerInfo, Control[] controls, Line.Info[] sourceLineInfo, Line.Info[] targetLineInfo)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 getMixerInfo()119 public final Mixer.Info getMixerInfo() { 120 return mixerInfo; 121 } 122 123 @Override getSourceLineInfo()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 getTargetLineInfo()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 getSourceLineInfo(Line.Info info)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 getTargetLineInfo(Line.Info info)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 isLineSupported(Line.Info info)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 getLine(Line.Info info)202 public abstract Line getLine(Line.Info info) throws LineUnavailableException; 203 204 @Override getMaxLines(Line.Info info)205 public abstract int getMaxLines(Line.Info info); 206 implOpen()207 protected abstract void implOpen() throws LineUnavailableException; implStart()208 protected abstract void implStart(); implStop()209 protected abstract void implStop(); implClose()210 protected abstract void implClose(); 211 212 @Override getSourceLines()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 getTargetLines()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 synchronize(Line[] lines, boolean maintainSync)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 unsynchronize(Line[] lines)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 isSynchronizationSupported(Line[] lines, boolean maintainSync)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 open()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 */ open(boolean manual)284 final synchronized void open(boolean manual) throws LineUnavailableException { 285 if (!isOpen()) { 286 implOpen(); 287 // if the mixer is not currently open, set open to true and send event 288 setOpen(true); 289 if (manual) { 290 manuallyOpened = true; 291 } 292 } 293 } 294 295 // METHOD FOR INTERNAL IMPLEMENTATION USE 296 297 /** 298 * The default implementation of this method just determines whether 299 * this line is a source or target line, calls open(no-arg) on the 300 * mixer, and adds the line to the appropriate vector. 301 * The mixer may be opened at a format different than the line's 302 * format if it is a DataLine. 303 */ open(Line line)304 final synchronized void open(Line line) throws LineUnavailableException { 305 // $$kk: 06.11.99: ignore ourselves for now 306 if (this.equals(line)) { 307 return; 308 } 309 310 // source line? 311 if (isSourceLine(line.getLineInfo())) { 312 if (! sourceLines.contains(line) ) { 313 // call the no-arg open method for the mixer; it should open at its 314 // default format if it is not open yet 315 open(false); 316 317 // we opened successfully! add the line to the list 318 sourceLines.addElement(line); 319 } 320 } else { 321 // target line? 322 if(isTargetLine(line.getLineInfo())) { 323 if (! targetLines.contains(line) ) { 324 // call the no-arg open method for the mixer; it should open at its 325 // default format if it is not open yet 326 open(false); 327 328 // we opened successfully! add the line to the list 329 targetLines.addElement(line); 330 } 331 } else { 332 if (Printer.err) Printer.err("Unknown line received for AbstractMixer.open(Line): " + line); 333 } 334 } 335 } 336 337 /** 338 * Removes this line from the list of open source lines and 339 * open target lines, if it exists in either. 340 * If the list is now empty, closes the mixer. 341 */ close(Line line)342 final synchronized void close(Line line) { 343 // $$kk: 06.11.99: ignore ourselves for now 344 if (this.equals(line)) { 345 return; 346 } 347 348 sourceLines.removeElement(line); 349 targetLines.removeElement(line); 350 351 if (sourceLines.isEmpty() && targetLines.isEmpty() && !manuallyOpened) { 352 close(); 353 } 354 } 355 356 /** 357 * Close all lines and then close this mixer. 358 */ 359 @Override close()360 public final synchronized void close() { 361 if (isOpen()) { 362 // close all source lines 363 Line[] localLines = getSourceLines(); 364 for (int i = 0; i<localLines.length; i++) { 365 localLines[i].close(); 366 } 367 368 // close all target lines 369 localLines = getTargetLines(); 370 for (int i = 0; i<localLines.length; i++) { 371 localLines[i].close(); 372 } 373 374 implClose(); 375 376 // set the open state to false and send events 377 setOpen(false); 378 } 379 manuallyOpened = false; 380 } 381 382 /** 383 * Starts the mixer. 384 */ start(Line line)385 final synchronized void start(Line line) { 386 // $$kk: 06.11.99: ignore ourselves for now 387 if (this.equals(line)) { 388 return; 389 } 390 391 // we just start the mixer regardless of anything else here. 392 if (!started) { 393 implStart(); 394 started = true; 395 } 396 } 397 398 /** 399 * Stops the mixer if this was the last running line. 400 */ stop(Line line)401 final synchronized void stop(Line line) { 402 // $$kk: 06.11.99: ignore ourselves for now 403 if (this.equals(line)) { 404 return; 405 } 406 407 @SuppressWarnings("unchecked") 408 Vector<Line> localSourceLines = (Vector<Line>)sourceLines.clone(); 409 for (int i = 0; i < localSourceLines.size(); i++) { 410 411 // if any other open line is running, return 412 413 // this covers clips and source data lines 414 if (localSourceLines.elementAt(i) instanceof AbstractDataLine) { 415 AbstractDataLine sourceLine = (AbstractDataLine)localSourceLines.elementAt(i); 416 if ( sourceLine.isStartedRunning() && (!sourceLine.equals(line)) ) { 417 return; 418 } 419 } 420 } 421 422 @SuppressWarnings("unchecked") 423 Vector<Line> localTargetLines = (Vector<Line>)targetLines.clone(); 424 for (int i = 0; i < localTargetLines.size(); i++) { 425 426 // if any other open line is running, return 427 // this covers target data lines 428 if (localTargetLines.elementAt(i) instanceof AbstractDataLine) { 429 AbstractDataLine targetLine = (AbstractDataLine)localTargetLines.elementAt(i); 430 if ( targetLine.isStartedRunning() && (!targetLine.equals(line)) ) { 431 return; 432 } 433 } 434 } 435 436 // otherwise, stop 437 started = false; 438 implStop(); 439 } 440 441 /** 442 * Determines whether this is a source line for this mixer. 443 * Right now this just checks whether it's supported, but should 444 * check whether it actually belongs to this mixer.... 445 */ isSourceLine(Line.Info info)446 final boolean isSourceLine(Line.Info info) { 447 448 for (int i = 0; i < sourceLineInfo.length; i++) { 449 if (info.matches(sourceLineInfo[i])) { 450 return true; 451 } 452 } 453 454 return false; 455 } 456 457 /** 458 * Determines whether this is a target line for this mixer. 459 * Right now this just checks whether it's supported, but should 460 * check whether it actually belongs to this mixer.... 461 */ isTargetLine(Line.Info info)462 final boolean isTargetLine(Line.Info info) { 463 464 for (int i = 0; i < targetLineInfo.length; i++) { 465 if (info.matches(targetLineInfo[i])) { 466 return true; 467 } 468 } 469 470 return false; 471 } 472 473 /** 474 * Returns the first complete Line.Info object it finds that 475 * matches the one specified, or null if no matching Line.Info 476 * object is found. 477 */ getLineInfo(Line.Info info)478 final Line.Info getLineInfo(Line.Info info) { 479 if (info == null) { 480 return null; 481 } 482 // $$kk: 05.31.99: need to change this so that 483 // the format and buffer size get set in the 484 // returned info object for data lines?? 485 for (int i = 0; i < sourceLineInfo.length; i++) { 486 if (info.matches(sourceLineInfo[i])) { 487 return sourceLineInfo[i]; 488 } 489 } 490 491 for (int i = 0; i < targetLineInfo.length; i++) { 492 if (info.matches(targetLineInfo[i])) { 493 return targetLineInfo[i]; 494 } 495 } 496 return null; 497 } 498 } 499