1 /* 2 * Copyright (c) 1999, 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 javax.sound.sampled; 27 28 import java.io.File; 29 import java.io.InputStream; 30 import java.io.IOException; 31 import java.io.OutputStream; 32 import java.net.URL; 33 34 import java.util.HashSet; 35 import java.util.List; 36 import java.util.Set; 37 import java.util.Vector; 38 import java.util.ArrayList; 39 40 import javax.sound.sampled.spi.AudioFileWriter; 41 import javax.sound.sampled.spi.AudioFileReader; 42 import javax.sound.sampled.spi.FormatConversionProvider; 43 import javax.sound.sampled.spi.MixerProvider; 44 45 import com.sun.media.sound.JDK13Services; 46 47 /* $fb TODO: 48 * - consistent usage of (typed) collections 49 */ 50 51 52 /** 53 * The {@code AudioSystem} class acts as the entry point to the sampled-audio 54 * system resources. This class lets you query and access the mixers that are 55 * installed on the system. {@code AudioSystem} includes a number of methods for 56 * converting audio data between different formats, and for translating between 57 * audio files and streams. It also provides a method for obtaining a 58 * {@link Line} directly from the {@code AudioSystem} without dealing explicitly 59 * with mixers. 60 * <p> 61 * Properties can be used to specify the default mixer for specific line types. 62 * Both system properties and a properties file are considered. The 63 * {@code sound.properties} properties file is read from an 64 * implementation-specific location (typically it is the {@code lib} directory 65 * in the Java installation directory). If a property exists both as a system 66 * property and in the properties file, the system property takes precedence. 67 * If none is specified, a suitable default is chosen among the available 68 * devices. The syntax of the properties file is specified in 69 * {@link java.util.Properties#load(InputStream) Properties.load}. The following 70 * table lists the available property keys and which methods consider them: 71 * 72 * <table border=0> 73 * <caption>Audio System Property Keys</caption> 74 * <tr> 75 * <th>Property Key</th> 76 * <th>Interface</th> 77 * <th>Affected Method(s)</th> 78 * </tr> 79 * <tr> 80 * <td>{@code javax.sound.sampled.Clip}</td> 81 * <td>{@link Clip}</td> 82 * <td>{@link #getLine}, {@link #getClip}</td> 83 * </tr> 84 * <tr> 85 * <td>{@code javax.sound.sampled.Port}</td> 86 * <td>{@link Port}</td> 87 * <td>{@link #getLine}</td> 88 * </tr> 89 * <tr> 90 * <td>{@code javax.sound.sampled.SourceDataLine}</td> 91 * <td>{@link SourceDataLine}</td> 92 * <td>{@link #getLine}, {@link #getSourceDataLine}</td> 93 * </tr> 94 * <tr> 95 * <td>{@code javax.sound.sampled.TargetDataLine}</td> 96 * <td>{@link TargetDataLine}</td> 97 * <td>{@link #getLine}, {@link #getTargetDataLine}</td> 98 * </tr> 99 * </table> 100 * 101 * The property value consists of the provider class name and the mixer name, 102 * separated by the hash mark ("#"). The provider class name is the 103 * fully-qualified name of a concrete 104 * {@link javax.sound.sampled.spi.MixerProvider mixer provider} class. The mixer 105 * name is matched against the {@code String} returned by the {@code getName} 106 * method of {@code Mixer.Info}. Either the class name, or the mixer name may be 107 * omitted. If only the class name is specified, the trailing hash mark is 108 * optional. 109 * <p> 110 * If the provider class is specified, and it can be successfully retrieved from 111 * the installed providers, the list of {@code Mixer.Info} objects is retrieved 112 * from the provider. Otherwise, or when these mixers do not provide a 113 * subsequent match, the list is retrieved from {@link #getMixerInfo} to contain 114 * all available {@code Mixer.Info} objects. 115 * <p> 116 * If a mixer name is specified, the resulting list of {@code Mixer.Info} 117 * objects is searched: the first one with a matching name, and whose 118 * {@code Mixer} provides the respective line interface, will be returned. If no 119 * matching {@code Mixer.Info} object is found, or the mixer name is not 120 * specified, the first mixer from the resulting list, which provides the 121 * respective line interface, will be returned. 122 * 123 * For example, the property {@code javax.sound.sampled.Clip} with a value 124 * {@code "com.sun.media.sound.MixerProvider#SunClip"} 125 * will have the following consequences when {@code getLine} is called 126 * requesting a {@code Clip} instance: if the class 127 * {@code com.sun.media.sound.MixerProvider} exists in the list of installed 128 * mixer providers, the first {@code Clip} from the first mixer with name 129 * {@code "SunClip"} will be returned. If it cannot be found, the 130 * first {@code Clip} from the first mixer of the specified provider will be 131 * returned, regardless of name. If there is none, the first {@code Clip} from 132 * the first {@code Mixer} with name {@code "SunClip"} in the list of 133 * all mixers (as returned by {@code getMixerInfo}) will be returned, or, if not 134 * found, the first {@code Clip} of the first {@code Mixer} that can be found in 135 * the list of all mixers is returned. If that fails, too, an 136 * {@code IllegalArgumentException} is thrown. 137 * 138 * @author Kara Kytle 139 * @author Florian Bomers 140 * @author Matthias Pfisterer 141 * @author Kevin P. Smith 142 * @see AudioFormat 143 * @see AudioInputStream 144 * @see Mixer 145 * @see Line 146 * @see Line.Info 147 * @since 1.3 148 */ 149 public class AudioSystem { 150 151 /** 152 * An integer that stands for an unknown numeric value. This value is 153 * appropriate only for signed quantities that do not normally take negative 154 * values. Examples include file sizes, frame sizes, buffer sizes, and 155 * sample rates. A number of Java Sound constructors accept a value of 156 * {@code NOT_SPECIFIED} for such parameters. Other methods may also accept 157 * or return this value, as documented. 158 */ 159 public static final int NOT_SPECIFIED = -1; 160 161 /** 162 * Private no-args constructor for ensuring against instantiation. 163 */ 164 private AudioSystem() { 165 } 166 167 /** 168 * Obtains an array of mixer info objects that represents the set of audio 169 * mixers that are currently installed on the system. 170 * 171 * @return an array of info objects for the currently installed mixers. If 172 * no mixers are available on the system, an array of length 0 is 173 * returned. 174 * @see #getMixer 175 */ 176 public static Mixer.Info[] getMixerInfo() { 177 178 List<Mixer.Info> infos = getMixerInfoList(); 179 Mixer.Info[] allInfos = infos.toArray(new Mixer.Info[infos.size()]); 180 return allInfos; 181 } 182 183 /** 184 * Obtains the requested audio mixer. 185 * 186 * @param info a {@code Mixer.Info} object representing the desired mixer, 187 * or {@code null} for the system default mixer 188 * @return the requested mixer 189 * @throws SecurityException if the requested mixer is unavailable because 190 * of security restrictions 191 * @throws IllegalArgumentException if the info object does not represent a 192 * mixer installed on the system 193 * @see #getMixerInfo 194 */ 195 public static Mixer getMixer(Mixer.Info info) { 196 197 Mixer mixer = null; 198 List<MixerProvider> providers = getMixerProviders(); 199 200 for(int i = 0; i < providers.size(); i++ ) { 201 202 try { 203 return providers.get(i).getMixer(info); 204 205 } catch (IllegalArgumentException e) { 206 } catch (NullPointerException e) { 207 // $$jb 08.20.99: If the strings in the info object aren't 208 // set, then Netscape (using jdk1.1.5) tends to throw 209 // NPE's when doing some string manipulation. This is 210 // probably not the best fix, but is solves the problem 211 // of the NPE in Netscape using local classes 212 // $$jb 11.01.99: Replacing this patch. 213 } 214 } 215 216 //$$fb if looking for default mixer, and not found yet, add a round of looking 217 if (info == null) { 218 for(int i = 0; i < providers.size(); i++ ) { 219 try { 220 MixerProvider provider = providers.get(i); 221 Mixer.Info[] infos = provider.getMixerInfo(); 222 // start from 0 to last device (do not reverse this order) 223 for (int ii = 0; ii < infos.length; ii++) { 224 try { 225 return provider.getMixer(infos[ii]); 226 } catch (IllegalArgumentException e) { 227 // this is not a good default device :) 228 } 229 } 230 } catch (IllegalArgumentException e) { 231 } catch (NullPointerException e) { 232 } 233 } 234 } 235 236 237 throw new IllegalArgumentException("Mixer not supported: " 238 + (info!=null?info.toString():"null")); 239 } 240 241 //$$fb 2002-11-26: fix for 4757930: DOC: AudioSystem.getTarget/SourceLineInfo() is ambiguous 242 243 /** 244 * Obtains information about all source lines of a particular type that are 245 * supported by the installed mixers. 246 * 247 * @param info a {@code Line.Info} object that specifies the kind of lines 248 * about which information is requested 249 * @return an array of {@code Line.Info} objects describing source lines 250 * matching the type requested. If no matching source lines are 251 * supported, an array of length 0 is returned. 252 * @see Mixer#getSourceLineInfo(Line.Info) 253 */ 254 public static Line.Info[] getSourceLineInfo(Line.Info info) { 255 256 Vector<Line.Info> vector = new Vector<>(); 257 Line.Info[] currentInfoArray; 258 259 Mixer mixer; 260 Line.Info fullInfo = null; 261 Mixer.Info[] infoArray = getMixerInfo(); 262 263 for (int i = 0; i < infoArray.length; i++) { 264 265 mixer = getMixer(infoArray[i]); 266 267 currentInfoArray = mixer.getSourceLineInfo(info); 268 for (int j = 0; j < currentInfoArray.length; j++) { 269 vector.addElement(currentInfoArray[j]); 270 } 271 } 272 273 Line.Info[] returnedArray = new Line.Info[vector.size()]; 274 275 for (int i = 0; i < returnedArray.length; i++) { 276 returnedArray[i] = vector.get(i); 277 } 278 279 return returnedArray; 280 } 281 282 /** 283 * Obtains information about all target lines of a particular type that are 284 * supported by the installed mixers. 285 * 286 * @param info a {@code Line.Info} object that specifies the kind of lines 287 * about which information is requested 288 * @return an array of {@code Line.Info} objects describing target lines 289 * matching the type requested. If no matching target lines are 290 * supported, an array of length 0 is returned. 291 * @see Mixer#getTargetLineInfo(Line.Info) 292 */ 293 public static Line.Info[] getTargetLineInfo(Line.Info info) { 294 295 Vector<Line.Info> vector = new Vector<>(); 296 Line.Info[] currentInfoArray; 297 298 Mixer mixer; 299 Line.Info fullInfo = null; 300 Mixer.Info[] infoArray = getMixerInfo(); 301 302 for (int i = 0; i < infoArray.length; i++) { 303 304 mixer = getMixer(infoArray[i]); 305 306 currentInfoArray = mixer.getTargetLineInfo(info); 307 for (int j = 0; j < currentInfoArray.length; j++) { 308 vector.addElement(currentInfoArray[j]); 309 } 310 } 311 312 Line.Info[] returnedArray = new Line.Info[vector.size()]; 313 314 for (int i = 0; i < returnedArray.length; i++) { 315 returnedArray[i] = vector.get(i); 316 } 317 318 return returnedArray; 319 } 320 321 /** 322 * Indicates whether the system supports any lines that match the specified 323 * {@code Line.Info} object. A line is supported if any installed mixer 324 * supports it. 325 * 326 * @param info a {@code Line.Info} object describing the line for which 327 * support is queried 328 * @return {@code true} if at least one matching line is supported, 329 * otherwise {@code false} 330 * @see Mixer#isLineSupported(Line.Info) 331 */ 332 public static boolean isLineSupported(Line.Info info) { 333 334 Mixer mixer; 335 Mixer.Info[] infoArray = getMixerInfo(); 336 337 for (int i = 0; i < infoArray.length; i++) { 338 339 if( infoArray[i] != null ) { 340 mixer = getMixer(infoArray[i]); 341 if (mixer.isLineSupported(info)) { 342 return true; 343 } 344 } 345 } 346 347 return false; 348 } 349 350 /** 351 * Obtains a line that matches the description in the specified 352 * {@code Line.Info} object. 353 * <p> 354 * If a {@code DataLine} is requested, and {@code info} is an instance of 355 * {@code DataLine.Info} specifying at least one fully qualified audio 356 * format, the last one will be used as the default format of the returned 357 * {@code DataLine}. 358 * <p> 359 * If system properties 360 * {@code javax.sound.sampled.Clip}, 361 * {@code javax.sound.sampled.Port}, 362 * {@code javax.sound.sampled.SourceDataLine} and 363 * {@code javax.sound.sampled.TargetDataLine} are defined or they are 364 * defined in the file "sound.properties", they are used to retrieve default 365 * lines. For details, refer to the {@link AudioSystem class description}. 366 * 367 * If the respective property is not set, or the mixer requested in the 368 * property is not installed or does not provide the requested line, all 369 * installed mixers are queried for the requested line type. A Line will be 370 * returned from the first mixer providing the requested line type. 371 * 372 * @param info a {@code Line.Info} object describing the desired kind of 373 * line 374 * @return a line of the requested kind 375 * @throws LineUnavailableException if a matching line is not available due 376 * to resource restrictions 377 * @throws SecurityException if a matching line is not available due to 378 * security restrictions 379 * @throws IllegalArgumentException if the system does not support at least 380 * one line matching the specified {@code Line.Info} object through 381 * any installed mixer 382 */ 383 public static Line getLine(Line.Info info) throws LineUnavailableException { 384 LineUnavailableException lue = null; 385 List<MixerProvider> providers = getMixerProviders(); 386 387 388 // 1: try from default mixer for this line class 389 try { 390 Mixer mixer = getDefaultMixer(providers, info); 391 if (mixer != null && mixer.isLineSupported(info)) { 392 return mixer.getLine(info); 393 } 394 } catch (LineUnavailableException e) { 395 lue = e; 396 } catch (IllegalArgumentException iae) { 397 // must not happen... but better to catch it here, 398 // if plug-ins are badly written 399 } 400 401 402 // 2: if that doesn't work, try to find any mixing mixer 403 for(int i = 0; i < providers.size(); i++) { 404 MixerProvider provider = providers.get(i); 405 Mixer.Info[] infos = provider.getMixerInfo(); 406 407 for (int j = 0; j < infos.length; j++) { 408 try { 409 Mixer mixer = provider.getMixer(infos[j]); 410 // see if this is an appropriate mixer which can mix 411 if (isAppropriateMixer(mixer, info, true)) { 412 return mixer.getLine(info); 413 } 414 } catch (LineUnavailableException e) { 415 lue = e; 416 } catch (IllegalArgumentException iae) { 417 // must not happen... but better to catch it here, 418 // if plug-ins are badly written 419 } 420 } 421 } 422 423 424 // 3: if that didn't work, try to find any non-mixing mixer 425 for(int i = 0; i < providers.size(); i++) { 426 MixerProvider provider = providers.get(i); 427 Mixer.Info[] infos = provider.getMixerInfo(); 428 for (int j = 0; j < infos.length; j++) { 429 try { 430 Mixer mixer = provider.getMixer(infos[j]); 431 // see if this is an appropriate mixer which can mix 432 if (isAppropriateMixer(mixer, info, false)) { 433 return mixer.getLine(info); 434 } 435 } catch (LineUnavailableException e) { 436 lue = e; 437 } catch (IllegalArgumentException iae) { 438 // must not happen... but better to catch it here, 439 // if plug-ins are badly written 440 } 441 } 442 } 443 444 // if this line was supported but was not available, throw the last 445 // LineUnavailableException we got (??). 446 if (lue != null) { 447 throw lue; 448 } 449 450 // otherwise, the requested line was not supported, so throw 451 // an Illegal argument exception 452 throw new IllegalArgumentException("No line matching " + 453 info.toString() + " is supported."); 454 } 455 456 /** 457 * Obtains a clip that can be used for playing back an audio file or an 458 * audio stream. The returned clip will be provided by the default system 459 * mixer, or, if not possible, by any other mixer installed in the system 460 * that supports a {@code Clip} object. 461 * <p> 462 * The returned clip must be opened with the {@code open(AudioFormat)} or 463 * {@code open(AudioInputStream)} method. 464 * <p> 465 * This is a high-level method that uses {@code getMixer} and 466 * {@code getLine} internally. 467 * <p> 468 * If the system property {@code javax.sound.sampled.Clip} is defined or it 469 * is defined in the file "sound.properties", it is used to retrieve the 470 * default clip. For details, refer to the 471 * {@link AudioSystem class description}. 472 * 473 * @return the desired clip object 474 * @throws LineUnavailableException if a clip object is not available due to 475 * resource restrictions 476 * @throws SecurityException if a clip object is not available due to 477 * security restrictions 478 * @throws IllegalArgumentException if the system does not support at least 479 * one clip instance through any installed mixer 480 * @see #getClip(Mixer.Info) 481 * @since 1.5 482 */ 483 public static Clip getClip() throws LineUnavailableException{ 484 AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 485 AudioSystem.NOT_SPECIFIED, 486 16, 2, 4, 487 AudioSystem.NOT_SPECIFIED, true); 488 DataLine.Info info = new DataLine.Info(Clip.class, format); 489 return (Clip) AudioSystem.getLine(info); 490 } 491 492 /** 493 * Obtains a clip from the specified mixer that can be used for playing back 494 * an audio file or an audio stream. 495 * <p> 496 * The returned clip must be opened with the {@code open(AudioFormat)} or 497 * {@code open(AudioInputStream)} method. 498 * <p> 499 * This is a high-level method that uses {@code getMixer} and 500 * {@code getLine} internally. 501 * 502 * @param mixerInfo a {@code Mixer.Info} object representing the desired 503 * mixer, or {@code null} for the system default mixer 504 * @return a clip object from the specified mixer 505 * 506 * @throws LineUnavailableException if a clip is not available from this 507 * mixer due to resource restrictions 508 * @throws SecurityException if a clip is not available from this mixer due 509 * to security restrictions 510 * @throws IllegalArgumentException if the system does not support at least 511 * one clip through the specified mixer 512 * @see #getClip() 513 * @since 1.5 514 */ 515 public static Clip getClip(Mixer.Info mixerInfo) throws LineUnavailableException{ 516 AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 517 AudioSystem.NOT_SPECIFIED, 518 16, 2, 4, 519 AudioSystem.NOT_SPECIFIED, true); 520 DataLine.Info info = new DataLine.Info(Clip.class, format); 521 Mixer mixer = AudioSystem.getMixer(mixerInfo); 522 return (Clip) mixer.getLine(info); 523 } 524 525 /** 526 * Obtains a source data line that can be used for playing back audio data 527 * in the format specified by the {@code AudioFormat} object. The returned 528 * line will be provided by the default system mixer, or, if not possible, 529 * by any other mixer installed in the system that supports a matching 530 * {@code SourceDataLine} object. 531 * <p> 532 * The returned line should be opened with the {@code open(AudioFormat)} or 533 * {@code open(AudioFormat, int)} method. 534 * <p> 535 * This is a high-level method that uses {@code getMixer} and 536 * {@code getLine} internally. 537 * <p> 538 * The returned {@code SourceDataLine}'s default audio format will be 539 * initialized with {@code format}. 540 * <p> 541 * If the system property {@code javax.sound.sampled.SourceDataLine} is 542 * defined or it is defined in the file "sound.properties", it is used to 543 * retrieve the default source data line. For details, refer to the 544 * {@link AudioSystem class description}. 545 * 546 * @param format an {@code AudioFormat} object specifying the supported 547 * audio format of the returned line, or {@code null} for any audio 548 * format 549 * @return the desired {@code SourceDataLine} object 550 * @throws LineUnavailableException if a matching source data line is not 551 * available due to resource restrictions 552 * @throws SecurityException if a matching source data line is not available 553 * due to security restrictions 554 * @throws IllegalArgumentException if the system does not support at least 555 * one source data line supporting the specified audio format 556 * through any installed mixer 557 * @see #getSourceDataLine(AudioFormat, Mixer.Info) 558 * @since 1.5 559 */ 560 public static SourceDataLine getSourceDataLine(AudioFormat format) 561 throws LineUnavailableException{ 562 DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); 563 return (SourceDataLine) AudioSystem.getLine(info); 564 } 565 566 /** 567 * Obtains a source data line that can be used for playing back audio data 568 * in the format specified by the {@code AudioFormat} object, provided by 569 * the mixer specified by the {@code Mixer.Info} object. 570 * <p> 571 * The returned line should be opened with the {@code open(AudioFormat)} or 572 * {@code open(AudioFormat, int)} method. 573 * <p> 574 * This is a high-level method that uses {@code getMixer} and 575 * {@code getLine} internally. 576 * <p> 577 * The returned {@code SourceDataLine}'s default audio format will be 578 * initialized with {@code format}. 579 * 580 * @param format an {@code AudioFormat} object specifying the supported 581 * audio format of the returned line, or {@code null} for any audio 582 * format 583 * @param mixerinfo a {@code Mixer.Info} object representing the desired 584 * mixer, or {@code null} for the system default mixer 585 * @return the desired {@code SourceDataLine} object 586 * @throws LineUnavailableException if a matching source data line is not 587 * available from the specified mixer due to resource restrictions 588 * @throws SecurityException if a matching source data line is not available 589 * from the specified mixer due to security restrictions 590 * @throws IllegalArgumentException if the specified mixer does not support 591 * at least one source data line supporting the specified audio 592 * format 593 * @see #getSourceDataLine(AudioFormat) 594 * @since 1.5 595 */ 596 public static SourceDataLine getSourceDataLine(AudioFormat format, 597 Mixer.Info mixerinfo) 598 throws LineUnavailableException{ 599 DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); 600 Mixer mixer = AudioSystem.getMixer(mixerinfo); 601 return (SourceDataLine) mixer.getLine(info); 602 } 603 604 /** 605 * Obtains a target data line that can be used for recording audio data in 606 * the format specified by the {@code AudioFormat} object. The returned line 607 * will be provided by the default system mixer, or, if not possible, by any 608 * other mixer installed in the system that supports a matching 609 * {@code TargetDataLine} object. 610 * <p> 611 * The returned line should be opened with the {@code open(AudioFormat)} or 612 * {@code open(AudioFormat, int)} method. 613 * <p> 614 * This is a high-level method that uses {@code getMixer} and 615 * {@code getLine} internally. 616 * <p> 617 * The returned {@code TargetDataLine}'s default audio format will be 618 * initialized with {@code format}. 619 * <p> 620 * If the system property {@code javax.sound.sampled.TargetDataLine} is 621 * defined or it is defined in the file "sound.properties", it is used to 622 * retrieve the default target data line. For details, refer to the 623 * {@link AudioSystem class description}. 624 * 625 * @param format an {@code AudioFormat} object specifying the supported 626 * audio format of the returned line, or {@code null} for any audio 627 * format 628 * @return the desired {@code TargetDataLine} object 629 * @throws LineUnavailableException if a matching target data line is not 630 * available due to resource restrictions 631 * @throws SecurityException if a matching target data line is not available 632 * due to security restrictions 633 * @throws IllegalArgumentException if the system does not support at least 634 * one target data line supporting the specified audio format 635 * through any installed mixer 636 * @see #getTargetDataLine(AudioFormat, Mixer.Info) 637 * @see AudioPermission 638 * @since 1.5 639 */ 640 public static TargetDataLine getTargetDataLine(AudioFormat format) 641 throws LineUnavailableException{ 642 643 DataLine.Info info = new DataLine.Info(TargetDataLine.class, format); 644 return (TargetDataLine) AudioSystem.getLine(info); 645 } 646 647 /** 648 * Obtains a target data line that can be used for recording audio data in 649 * the format specified by the {@code AudioFormat} object, provided by the 650 * mixer specified by the {@code Mixer.Info} object. 651 * <p> 652 * The returned line should be opened with the {@code open(AudioFormat)} or 653 * {@code open(AudioFormat, int)} method. 654 * <p> 655 * This is a high-level method that uses {@code getMixer} and 656 * {@code getLine} internally. 657 * <p> 658 * The returned {@code TargetDataLine}'s default audio format will be 659 * initialized with {@code format}. 660 * 661 * @param format an {@code AudioFormat} object specifying the supported 662 * audio format of the returned line, or {@code null} for any audio 663 * format 664 * @param mixerinfo a {@code Mixer.Info} object representing the desired 665 * mixer, or {@code null} for the system default mixer 666 * @return the desired {@code TargetDataLine} object 667 * @throws LineUnavailableException if a matching target data line is not 668 * available from the specified mixer due to resource restrictions 669 * @throws SecurityException if a matching target data line is not available 670 * from the specified mixer due to security restrictions 671 * @throws IllegalArgumentException if the specified mixer does not support 672 * at least one target data line supporting the specified audio 673 * format 674 * @see #getTargetDataLine(AudioFormat) 675 * @see AudioPermission 676 * @since 1.5 677 */ 678 public static TargetDataLine getTargetDataLine(AudioFormat format, 679 Mixer.Info mixerinfo) 680 throws LineUnavailableException { 681 682 DataLine.Info info = new DataLine.Info(TargetDataLine.class, format); 683 Mixer mixer = AudioSystem.getMixer(mixerinfo); 684 return (TargetDataLine) mixer.getLine(info); 685 } 686 687 // $$fb 2002-04-12: fix for 4662082: behavior of AudioSystem.getTargetEncodings() methods doesn't match the spec 688 689 /** 690 * Obtains the encodings that the system can obtain from an audio input 691 * stream with the specified encoding using the set of installed format 692 * converters. 693 * 694 * @param sourceEncoding the encoding for which conversion support is 695 * queried 696 * @return array of encodings. If {@code sourceEncoding}is not supported, an 697 * array of length 0 is returned. Otherwise, the array will have a 698 * length of at least 1, representing {@code sourceEncoding} 699 * (no conversion). 700 */ 701 public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat.Encoding sourceEncoding) { 702 703 List<FormatConversionProvider> codecs = getFormatConversionProviders(); 704 Vector<AudioFormat.Encoding> encodings = new Vector<>(); 705 706 AudioFormat.Encoding encs[] = null; 707 708 // gather from all the codecs 709 for(int i=0; i<codecs.size(); i++ ) { 710 FormatConversionProvider codec = codecs.get(i); 711 if( codec.isSourceEncodingSupported( sourceEncoding ) ) { 712 encs = codec.getTargetEncodings(); 713 for (int j = 0; j < encs.length; j++) { 714 encodings.addElement( encs[j] ); 715 } 716 } 717 } 718 AudioFormat.Encoding encs2[] = encodings.toArray(new AudioFormat.Encoding[0]); 719 return encs2; 720 } 721 722 // $$fb 2002-04-12: fix for 4662082: behavior of AudioSystem.getTargetEncodings() methods doesn't match the spec 723 724 /** 725 * Obtains the encodings that the system can obtain from an audio input 726 * stream with the specified format using the set of installed format 727 * converters. 728 * 729 * @param sourceFormat the audio format for which conversion is queried 730 * @return array of encodings. If {@code sourceFormat}is not supported, an 731 * array of length 0 is returned. Otherwise, the array will have a 732 * length of at least 1, representing the encoding of 733 * {@code sourceFormat} (no conversion). 734 */ 735 public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat) { 736 737 738 List<FormatConversionProvider> codecs = getFormatConversionProviders(); 739 Vector<AudioFormat.Encoding[]> encodings = new Vector<>(); 740 741 int size = 0; 742 int index = 0; 743 AudioFormat.Encoding encs[] = null; 744 745 // gather from all the codecs 746 747 for(int i=0; i<codecs.size(); i++ ) { 748 encs = codecs.get(i).getTargetEncodings(sourceFormat); 749 size += encs.length; 750 encodings.addElement( encs ); 751 } 752 753 // now build a new array 754 755 AudioFormat.Encoding encs2[] = new AudioFormat.Encoding[size]; 756 for(int i=0; i<encodings.size(); i++ ) { 757 encs = encodings.get(i); 758 for(int j=0; j<encs.length; j++ ) { 759 encs2[index++] = encs[j]; 760 } 761 } 762 return encs2; 763 } 764 765 /** 766 * Indicates whether an audio input stream of the specified encoding can be 767 * obtained from an audio input stream that has the specified format. 768 * 769 * @param targetEncoding the desired encoding after conversion 770 * @param sourceFormat the audio format before conversion 771 * @return {@code true} if the conversion is supported, otherwise 772 * {@code false} 773 */ 774 public static boolean isConversionSupported(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) { 775 776 777 List<FormatConversionProvider> codecs = getFormatConversionProviders(); 778 779 for(int i=0; i<codecs.size(); i++ ) { 780 FormatConversionProvider codec = codecs.get(i); 781 if(codec.isConversionSupported(targetEncoding,sourceFormat) ) { 782 return true; 783 } 784 } 785 return false; 786 } 787 788 /** 789 * Obtains an audio input stream of the indicated encoding, by converting 790 * the provided audio input stream. 791 * 792 * @param targetEncoding the desired encoding after conversion 793 * @param sourceStream the stream to be converted 794 * @return an audio input stream of the indicated encoding 795 * @throws IllegalArgumentException if the conversion is not supported 796 * @see #getTargetEncodings(AudioFormat.Encoding) 797 * @see #getTargetEncodings(AudioFormat) 798 * @see #isConversionSupported(AudioFormat.Encoding, AudioFormat) 799 * @see #getAudioInputStream(AudioFormat, AudioInputStream) 800 */ 801 public static AudioInputStream getAudioInputStream(AudioFormat.Encoding targetEncoding, 802 AudioInputStream sourceStream) { 803 804 List<FormatConversionProvider> codecs = getFormatConversionProviders(); 805 806 for(int i = 0; i < codecs.size(); i++) { 807 FormatConversionProvider codec = codecs.get(i); 808 if( codec.isConversionSupported( targetEncoding, sourceStream.getFormat() ) ) { 809 return codec.getAudioInputStream( targetEncoding, sourceStream ); 810 } 811 } 812 // we ran out of options, throw an exception 813 throw new IllegalArgumentException("Unsupported conversion: " + targetEncoding + " from " + sourceStream.getFormat()); 814 } 815 816 /** 817 * Obtains the formats that have a particular encoding and that the system 818 * can obtain from a stream of the specified format using the set of 819 * installed format converters. 820 * 821 * @param targetEncoding the desired encoding after conversion 822 * @param sourceFormat the audio format before conversion 823 * @return array of formats. If no formats of the specified encoding are 824 * supported, an array of length 0 is returned. 825 */ 826 public static AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) { 827 828 List<FormatConversionProvider> codecs = getFormatConversionProviders(); 829 Vector<AudioFormat[]> formats = new Vector<>(); 830 831 int size = 0; 832 int index = 0; 833 AudioFormat fmts[] = null; 834 835 // gather from all the codecs 836 837 for(int i=0; i<codecs.size(); i++ ) { 838 FormatConversionProvider codec = codecs.get(i); 839 fmts = codec.getTargetFormats(targetEncoding, sourceFormat); 840 size += fmts.length; 841 formats.addElement( fmts ); 842 } 843 844 // now build a new array 845 846 AudioFormat fmts2[] = new AudioFormat[size]; 847 for(int i=0; i<formats.size(); i++ ) { 848 fmts = formats.get(i); 849 for(int j=0; j<fmts.length; j++ ) { 850 fmts2[index++] = fmts[j]; 851 } 852 } 853 return fmts2; 854 } 855 856 /** 857 * Indicates whether an audio input stream of a specified format can be 858 * obtained from an audio input stream of another specified format. 859 * 860 * @param targetFormat the desired audio format after conversion 861 * @param sourceFormat the audio format before conversion 862 * @return {@code true} if the conversion is supported, otherwise 863 * {@code false} 864 */ 865 public static boolean isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat) { 866 867 List<FormatConversionProvider> codecs = getFormatConversionProviders(); 868 869 for(int i=0; i<codecs.size(); i++ ) { 870 FormatConversionProvider codec = codecs.get(i); 871 if(codec.isConversionSupported(targetFormat, sourceFormat) ) { 872 return true; 873 } 874 } 875 return false; 876 } 877 878 /** 879 * Obtains an audio input stream of the indicated format, by converting the 880 * provided audio input stream. 881 * 882 * @param targetFormat the desired audio format after conversion 883 * @param sourceStream the stream to be converted 884 * @return an audio input stream of the indicated format 885 * @throws IllegalArgumentException if the conversion is not supported 886 * @see #getTargetEncodings(AudioFormat) 887 * @see #getTargetFormats(AudioFormat.Encoding, AudioFormat) 888 * @see #isConversionSupported(AudioFormat, AudioFormat) 889 * @see #getAudioInputStream(AudioFormat.Encoding, AudioInputStream) 890 */ 891 public static AudioInputStream getAudioInputStream(AudioFormat targetFormat, 892 AudioInputStream sourceStream) { 893 894 if (sourceStream.getFormat().matches(targetFormat)) { 895 return sourceStream; 896 } 897 898 List<FormatConversionProvider> codecs = getFormatConversionProviders(); 899 900 for(int i = 0; i < codecs.size(); i++) { 901 FormatConversionProvider codec = codecs.get(i); 902 if(codec.isConversionSupported(targetFormat,sourceStream.getFormat()) ) { 903 return codec.getAudioInputStream(targetFormat,sourceStream); 904 } 905 } 906 907 // we ran out of options... 908 throw new IllegalArgumentException("Unsupported conversion: " + targetFormat + " from " + sourceStream.getFormat()); 909 } 910 911 /** 912 * Obtains the audio file format of the provided input stream. The stream 913 * must point to valid audio file data. The implementation of this method 914 * may require multiple parsers to examine the stream to determine whether 915 * they support it. These parsers must be able to mark the stream, read 916 * enough data to determine whether they support the stream, and, if not, 917 * reset the stream's read pointer to its original position. If the input 918 * stream does not support these operations, this method may fail with an 919 * {@code IOException}. 920 * 921 * @param stream the input stream from which file format information should 922 * be extracted 923 * @return an {@code AudioFileFormat} object describing the stream's audio 924 * file format 925 * @throws UnsupportedAudioFileException if the stream does not point to 926 * valid audio file data recognized by the system 927 * @throws IOException if an input/output exception occurs 928 * @see InputStream#markSupported 929 * @see InputStream#mark 930 */ 931 public static AudioFileFormat getAudioFileFormat(InputStream stream) 932 throws UnsupportedAudioFileException, IOException { 933 934 List<AudioFileReader> providers = getAudioFileReaders(); 935 AudioFileFormat format = null; 936 937 for(int i = 0; i < providers.size(); i++ ) { 938 AudioFileReader reader = providers.get(i); 939 try { 940 format = reader.getAudioFileFormat( stream ); // throws IOException 941 break; 942 } catch (UnsupportedAudioFileException e) { 943 continue; 944 } 945 } 946 947 if( format==null ) { 948 throw new UnsupportedAudioFileException("file is not a supported file type"); 949 } else { 950 return format; 951 } 952 } 953 954 /** 955 * Obtains the audio file format of the specified URL. The URL must point to 956 * valid audio file data. 957 * 958 * @param url the URL from which file format information should be 959 * extracted 960 * @return an {@code AudioFileFormat} object describing the audio file 961 * format 962 * @throws UnsupportedAudioFileException if the URL does not point to valid 963 * audio file data recognized by the system 964 * @throws IOException if an input/output exception occurs 965 */ 966 public static AudioFileFormat getAudioFileFormat(URL url) 967 throws UnsupportedAudioFileException, IOException { 968 969 List<AudioFileReader> providers = getAudioFileReaders(); 970 AudioFileFormat format = null; 971 972 for(int i = 0; i < providers.size(); i++ ) { 973 AudioFileReader reader = providers.get(i); 974 try { 975 format = reader.getAudioFileFormat( url ); // throws IOException 976 break; 977 } catch (UnsupportedAudioFileException e) { 978 continue; 979 } 980 } 981 982 if( format==null ) { 983 throw new UnsupportedAudioFileException("file is not a supported file type"); 984 } else { 985 return format; 986 } 987 } 988 989 /** 990 * Obtains the audio file format of the specified {@code File}. The 991 * {@code File} must point to valid audio file data. 992 * 993 * @param file the {@code File} from which file format information should 994 * be extracted 995 * @return an {@code AudioFileFormat} object describing the audio file 996 * format 997 * @throws UnsupportedAudioFileException if the {@code File} does not point 998 * to valid audio file data recognized by the system 999 * @throws IOException if an I/O exception occurs 1000 */ 1001 public static AudioFileFormat getAudioFileFormat(File file) 1002 throws UnsupportedAudioFileException, IOException { 1003 1004 List<AudioFileReader> providers = getAudioFileReaders(); 1005 AudioFileFormat format = null; 1006 1007 for(int i = 0; i < providers.size(); i++ ) { 1008 AudioFileReader reader = providers.get(i); 1009 try { 1010 format = reader.getAudioFileFormat( file ); // throws IOException 1011 break; 1012 } catch (UnsupportedAudioFileException e) { 1013 continue; 1014 } 1015 } 1016 1017 if( format==null ) { 1018 throw new UnsupportedAudioFileException("file is not a supported file type"); 1019 } else { 1020 return format; 1021 } 1022 } 1023 1024 /** 1025 * Obtains an audio input stream from the provided input stream. The stream 1026 * must point to valid audio file data. The implementation of this method 1027 * may require multiple parsers to examine the stream to determine whether 1028 * they support it. These parsers must be able to mark the stream, read 1029 * enough data to determine whether they support the stream, and, if not, 1030 * reset the stream's read pointer to its original position. If the input 1031 * stream does not support these operation, this method may fail with an 1032 * {@code IOException}. 1033 * 1034 * @param stream the input stream from which the {@code AudioInputStream} 1035 * should be constructed 1036 * @return an {@code AudioInputStream} object based on the audio file data 1037 * contained in the input stream 1038 * @throws UnsupportedAudioFileException if the stream does not point to 1039 * valid audio file data recognized by the system 1040 * @throws IOException if an I/O exception occurs 1041 * @see InputStream#markSupported 1042 * @see InputStream#mark 1043 */ 1044 public static AudioInputStream getAudioInputStream(InputStream stream) 1045 throws UnsupportedAudioFileException, IOException { 1046 1047 List<AudioFileReader> providers = getAudioFileReaders(); 1048 AudioInputStream audioStream = null; 1049 1050 for(int i = 0; i < providers.size(); i++ ) { 1051 AudioFileReader reader = providers.get(i); 1052 try { 1053 audioStream = reader.getAudioInputStream( stream ); // throws IOException 1054 break; 1055 } catch (UnsupportedAudioFileException e) { 1056 continue; 1057 } 1058 } 1059 1060 if( audioStream==null ) { 1061 throw new UnsupportedAudioFileException("could not get audio input stream from input stream"); 1062 } else { 1063 return audioStream; 1064 } 1065 } 1066 1067 /** 1068 * Obtains an audio input stream from the URL provided. The URL must point 1069 * to valid audio file data. 1070 * 1071 * @param url the URL for which the {@code AudioInputStream} should be 1072 * constructed 1073 * @return an {@code AudioInputStream} object based on the audio file data 1074 * pointed to by the URL 1075 * @throws UnsupportedAudioFileException if the URL does not point to valid 1076 * audio file data recognized by the system 1077 * @throws IOException if an I/O exception occurs 1078 */ 1079 public static AudioInputStream getAudioInputStream(URL url) 1080 throws UnsupportedAudioFileException, IOException { 1081 1082 List<AudioFileReader> providers = getAudioFileReaders(); 1083 AudioInputStream audioStream = null; 1084 1085 for(int i = 0; i < providers.size(); i++ ) { 1086 AudioFileReader reader = providers.get(i); 1087 try { 1088 audioStream = reader.getAudioInputStream( url ); // throws IOException 1089 break; 1090 } catch (UnsupportedAudioFileException e) { 1091 continue; 1092 } 1093 } 1094 1095 if( audioStream==null ) { 1096 throw new UnsupportedAudioFileException("could not get audio input stream from input URL"); 1097 } else { 1098 return audioStream; 1099 } 1100 } 1101 1102 /** 1103 * Obtains an audio input stream from the provided {@code File}. The 1104 * {@code File} must point to valid audio file data. 1105 * 1106 * @param file the {@code File} for which the {@code AudioInputStream} 1107 * should be constructed 1108 * @return an {@code AudioInputStream} object based on the audio file data 1109 * pointed to by the {@code File} 1110 * @throws UnsupportedAudioFileException if the {@code File} does not point 1111 * to valid audio file data recognized by the system 1112 * @throws IOException if an I/O exception occurs 1113 */ 1114 public static AudioInputStream getAudioInputStream(File file) 1115 throws UnsupportedAudioFileException, IOException { 1116 1117 List<AudioFileReader> providers = getAudioFileReaders(); 1118 AudioInputStream audioStream = null; 1119 1120 for(int i = 0; i < providers.size(); i++ ) { 1121 AudioFileReader reader = providers.get(i); 1122 try { 1123 audioStream = reader.getAudioInputStream( file ); // throws IOException 1124 break; 1125 } catch (UnsupportedAudioFileException e) { 1126 continue; 1127 } 1128 } 1129 1130 if( audioStream==null ) { 1131 throw new UnsupportedAudioFileException("could not get audio input stream from input file"); 1132 } else { 1133 return audioStream; 1134 } 1135 } 1136 1137 /** 1138 * Obtains the file types for which file writing support is provided by the 1139 * system. 1140 * 1141 * @return array of unique file types. If no file types are supported, an 1142 * array of length 0 is returned. 1143 */ 1144 public static AudioFileFormat.Type[] getAudioFileTypes() { 1145 List<AudioFileWriter> providers = getAudioFileWriters(); 1146 Set<AudioFileFormat.Type> returnTypesSet = new HashSet<>(); 1147 1148 for(int i=0; i < providers.size(); i++) { 1149 AudioFileWriter writer = providers.get(i); 1150 AudioFileFormat.Type[] fileTypes = writer.getAudioFileTypes(); 1151 for(int j=0; j < fileTypes.length; j++) { 1152 returnTypesSet.add(fileTypes[j]); 1153 } 1154 } 1155 AudioFileFormat.Type returnTypes[] = 1156 returnTypesSet.toArray(new AudioFileFormat.Type[0]); 1157 return returnTypes; 1158 } 1159 1160 /** 1161 * Indicates whether file writing support for the specified file type is 1162 * provided by the system. 1163 * 1164 * @param fileType the file type for which write capabilities are queried 1165 * @return {@code true} if the file type is supported, otherwise 1166 * {@code false} 1167 */ 1168 public static boolean isFileTypeSupported(AudioFileFormat.Type fileType) { 1169 1170 List<AudioFileWriter> providers = getAudioFileWriters(); 1171 1172 for(int i=0; i < providers.size(); i++) { 1173 AudioFileWriter writer = providers.get(i); 1174 if (writer.isFileTypeSupported(fileType)) { 1175 return true; 1176 } 1177 } 1178 return false; 1179 } 1180 1181 /** 1182 * Obtains the file types that the system can write from the audio input 1183 * stream specified. 1184 * 1185 * @param stream the audio input stream for which audio file type 1186 * support is queried 1187 * @return array of file types. If no file types are supported, an array of 1188 * length 0 is returned. 1189 */ 1190 public static AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) { 1191 List<AudioFileWriter> providers = getAudioFileWriters(); 1192 Set<AudioFileFormat.Type> returnTypesSet = new HashSet<>(); 1193 1194 for(int i=0; i < providers.size(); i++) { 1195 AudioFileWriter writer = providers.get(i); 1196 AudioFileFormat.Type[] fileTypes = writer.getAudioFileTypes(stream); 1197 for(int j=0; j < fileTypes.length; j++) { 1198 returnTypesSet.add(fileTypes[j]); 1199 } 1200 } 1201 AudioFileFormat.Type returnTypes[] = 1202 returnTypesSet.toArray(new AudioFileFormat.Type[0]); 1203 return returnTypes; 1204 } 1205 1206 /** 1207 * Indicates whether an audio file of the specified file type can be written 1208 * from the indicated audio input stream. 1209 * 1210 * @param fileType the file type for which write capabilities are queried 1211 * @param stream the stream for which file-writing support is queried 1212 * @return {@code true} if the file type is supported for this audio input 1213 * stream, otherwise {@code false} 1214 */ 1215 public static boolean isFileTypeSupported(AudioFileFormat.Type fileType, 1216 AudioInputStream stream) { 1217 1218 List<AudioFileWriter> providers = getAudioFileWriters(); 1219 1220 for(int i=0; i < providers.size(); i++) { 1221 AudioFileWriter writer = providers.get(i); 1222 if(writer.isFileTypeSupported(fileType, stream)) { 1223 return true; 1224 } 1225 } 1226 return false; 1227 } 1228 1229 /** 1230 * Writes a stream of bytes representing an audio file of the specified file 1231 * type to the output stream provided. Some file types require that the 1232 * length be written into the file header; such files cannot be written from 1233 * start to finish unless the length is known in advance. An attempt to 1234 * write a file of such a type will fail with an IOException if the length 1235 * in the audio file type is {@code AudioSystem.NOT_SPECIFIED}. 1236 * 1237 * @param stream the audio input stream containing audio data to be written 1238 * to the file 1239 * @param fileType the kind of audio file to write 1240 * @param out the stream to which the file data should be written 1241 * @return the number of bytes written to the output stream 1242 * @throws IOException if an input/output exception occurs 1243 * @throws IllegalArgumentException if the file type is not supported by the 1244 * system 1245 * @see #isFileTypeSupported 1246 * @see #getAudioFileTypes 1247 */ 1248 public static int write(AudioInputStream stream, AudioFileFormat.Type fileType, 1249 OutputStream out) throws IOException { 1250 1251 List<AudioFileWriter> providers = getAudioFileWriters(); 1252 int bytesWritten = 0; 1253 boolean flag = false; 1254 1255 for(int i=0; i < providers.size(); i++) { 1256 AudioFileWriter writer = providers.get(i); 1257 try { 1258 bytesWritten = writer.write( stream, fileType, out ); // throws IOException 1259 flag = true; 1260 break; 1261 } catch (IllegalArgumentException e) { 1262 // thrown if this provider cannot write the sequence, try the next 1263 continue; 1264 } 1265 } 1266 if(!flag) { 1267 throw new IllegalArgumentException("could not write audio file: file type not supported: " + fileType); 1268 } else { 1269 return bytesWritten; 1270 } 1271 } 1272 1273 /** 1274 * Writes a stream of bytes representing an audio file of the specified file 1275 * type to the external file provided. 1276 * 1277 * @param stream the audio input stream containing audio data to be written 1278 * to the file 1279 * @param fileType the kind of audio file to write 1280 * @param out the external file to which the file data should be written 1281 * @return the number of bytes written to the file 1282 * @throws IOException if an I/O exception occurs 1283 * @throws IllegalArgumentException if the file type is not supported by the 1284 * system 1285 * @see #isFileTypeSupported 1286 * @see #getAudioFileTypes 1287 */ 1288 public static int write(AudioInputStream stream, AudioFileFormat.Type fileType, 1289 File out) throws IOException { 1290 1291 List<AudioFileWriter> providers = getAudioFileWriters(); 1292 int bytesWritten = 0; 1293 boolean flag = false; 1294 1295 for(int i=0; i < providers.size(); i++) { 1296 AudioFileWriter writer = providers.get(i); 1297 try { 1298 bytesWritten = writer.write( stream, fileType, out ); // throws IOException 1299 flag = true; 1300 break; 1301 } catch (IllegalArgumentException e) { 1302 // thrown if this provider cannot write the sequence, try the next 1303 continue; 1304 } 1305 } 1306 if (!flag) { 1307 throw new IllegalArgumentException("could not write audio file: file type not supported: " + fileType); 1308 } else { 1309 return bytesWritten; 1310 } 1311 } 1312 1313 // METHODS FOR INTERNAL IMPLEMENTATION USE 1314 1315 /** 1316 * Obtains the set of MixerProviders on the system. 1317 */ 1318 @SuppressWarnings("unchecked") 1319 private static List<MixerProvider> getMixerProviders() { 1320 return (List<MixerProvider>) getProviders(MixerProvider.class); 1321 } 1322 1323 /** 1324 * Obtains the set of format converters (codecs, transcoders, etc.) that are 1325 * currently installed on the system. 1326 * 1327 * @return an array of {@link javax.sound.sampled.spi.FormatConversionProvider 1328 * FormatConversionProvider} objects representing the available 1329 * format converters. If no format converters readers are available 1330 * on the system, an array of length 0 is returned. 1331 */ 1332 @SuppressWarnings("unchecked") 1333 private static List<FormatConversionProvider> getFormatConversionProviders() { 1334 return (List<FormatConversionProvider>) getProviders(FormatConversionProvider.class); 1335 } 1336 1337 /** 1338 * Obtains the set of audio file readers that are currently installed on the 1339 * system. 1340 * 1341 * @return a List of {@link javax.sound.sampled.spi.AudioFileReader 1342 * AudioFileReader} objects representing the installed audio file 1343 * readers. If no audio file readers are available on the system, an 1344 * empty List is returned. 1345 */ 1346 @SuppressWarnings("unchecked") 1347 private static List<AudioFileReader> getAudioFileReaders() { 1348 return (List<AudioFileReader>)getProviders(AudioFileReader.class); 1349 } 1350 1351 /** 1352 * Obtains the set of audio file writers that are currently installed on the 1353 * system. 1354 * 1355 * @return a List of {@link javax.sound.sampled.spi.AudioFileWriter 1356 * AudioFileWriter} objects representing the available audio file 1357 * writers. If no audio file writers are available on the system, an 1358 * empty List is returned. 1359 */ 1360 @SuppressWarnings("unchecked") 1361 private static List<AudioFileWriter> getAudioFileWriters() { 1362 return (List<AudioFileWriter>)getProviders(AudioFileWriter.class); 1363 } 1364 1365 /** 1366 * Attempts to locate and return a default Mixer that provides lines of the 1367 * specified type. 1368 * 1369 * @param providers the installed mixer providers 1370 * @param info The requested line type TargetDataLine.class, Clip.class or 1371 * Port.class 1372 * @return a Mixer that matches the requirements, or null if no default 1373 * mixer found 1374 */ 1375 private static Mixer getDefaultMixer(List<MixerProvider> providers, Line.Info info) { 1376 Class<?> lineClass = info.getLineClass(); 1377 String providerClassName = JDK13Services.getDefaultProviderClassName(lineClass); 1378 String instanceName = JDK13Services.getDefaultInstanceName(lineClass); 1379 Mixer mixer; 1380 1381 if (providerClassName != null) { 1382 MixerProvider defaultProvider = getNamedProvider(providerClassName, providers); 1383 if (defaultProvider != null) { 1384 if (instanceName != null) { 1385 mixer = getNamedMixer(instanceName, defaultProvider, info); 1386 if (mixer != null) { 1387 return mixer; 1388 } 1389 } else { 1390 mixer = getFirstMixer(defaultProvider, info, 1391 false /* mixing not required*/); 1392 if (mixer != null) { 1393 return mixer; 1394 } 1395 } 1396 1397 } 1398 } 1399 1400 /* Provider class not specified or 1401 provider class cannot be found, or 1402 provider class and instance specified and instance cannot be found or is not appropriate */ 1403 if (instanceName != null) { 1404 mixer = getNamedMixer(instanceName, providers, info); 1405 if (mixer != null) { 1406 return mixer; 1407 } 1408 } 1409 1410 1411 /* No default are specified, or if something is specified, everything 1412 failed. */ 1413 return null; 1414 } 1415 1416 /** 1417 * Return a MixerProvider of a given class from the list of MixerProviders. 1418 * This method never requires the returned Mixer to do mixing. 1419 * 1420 * @param providerClassName The class name of the provider to be returned 1421 * @param providers The list of MixerProviders that is searched 1422 * @return A MixerProvider of the requested class, or null if none is found 1423 */ 1424 private static MixerProvider getNamedProvider(String providerClassName, 1425 List<MixerProvider> providers) { 1426 for(int i = 0; i < providers.size(); i++) { 1427 MixerProvider provider = providers.get(i); 1428 if (provider.getClass().getName().equals(providerClassName)) { 1429 return provider; 1430 } 1431 } 1432 return null; 1433 } 1434 1435 /** 1436 * Return a Mixer with a given name from a given MixerProvider. This method 1437 * never requires the returned Mixer to do mixing. 1438 * 1439 * @param mixerName The name of the Mixer to be returned 1440 * @param provider The MixerProvider to check for Mixers 1441 * @param info The type of line the returned Mixer is required to support 1442 * @return A Mixer matching the requirements, or null if none is found 1443 */ 1444 private static Mixer getNamedMixer(String mixerName, 1445 MixerProvider provider, 1446 Line.Info info) { 1447 Mixer.Info[] infos = provider.getMixerInfo(); 1448 for (int i = 0; i < infos.length; i++) { 1449 if (infos[i].getName().equals(mixerName)) { 1450 Mixer mixer = provider.getMixer(infos[i]); 1451 if (isAppropriateMixer(mixer, info, false)) { 1452 return mixer; 1453 } 1454 } 1455 } 1456 return null; 1457 } 1458 1459 /** 1460 * From a List of MixerProviders, return a Mixer with a given name. This 1461 * method never requires the returned Mixer to do mixing. 1462 * 1463 * @param mixerName The name of the Mixer to be returned 1464 * @param providers The List of MixerProviders to check for Mixers 1465 * @param info The type of line the returned Mixer is required to support 1466 * @return A Mixer matching the requirements, or null if none is found 1467 */ 1468 private static Mixer getNamedMixer(String mixerName, 1469 List<MixerProvider> providers, 1470 Line.Info info) { 1471 for(int i = 0; i < providers.size(); i++) { 1472 MixerProvider provider = providers.get(i); 1473 Mixer mixer = getNamedMixer(mixerName, provider, info); 1474 if (mixer != null) { 1475 return mixer; 1476 } 1477 } 1478 return null; 1479 } 1480 1481 /** 1482 * From a given MixerProvider, return the first appropriate Mixer. 1483 * 1484 * @param provider The MixerProvider to check for Mixers 1485 * @param info The type of line the returned Mixer is required to support 1486 * @param isMixingRequired If true, only Mixers that support mixing are 1487 * returned for line types of SourceDataLine and Clip 1488 * @return A Mixer that is considered appropriate, or null if none is found 1489 */ 1490 private static Mixer getFirstMixer(MixerProvider provider, 1491 Line.Info info, 1492 boolean isMixingRequired) { 1493 Mixer.Info[] infos = provider.getMixerInfo(); 1494 for (int j = 0; j < infos.length; j++) { 1495 Mixer mixer = provider.getMixer(infos[j]); 1496 if (isAppropriateMixer(mixer, info, isMixingRequired)) { 1497 return mixer; 1498 } 1499 } 1500 return null; 1501 } 1502 1503 /** 1504 * Checks if a Mixer is appropriate. A Mixer is considered appropriate if it 1505 * support the given line type. If isMixingRequired is true and the line 1506 * type is an output one (SourceDataLine, Clip), the mixer is appropriate if 1507 * it supports at least 2 (concurrent) lines of the given type. 1508 * 1509 * @return {@code true} if the mixer is considered appropriate according to 1510 * the rules given above, {@code false} otherwise 1511 */ 1512 private static boolean isAppropriateMixer(Mixer mixer, 1513 Line.Info lineInfo, 1514 boolean isMixingRequired) { 1515 if (! mixer.isLineSupported(lineInfo)) { 1516 return false; 1517 } 1518 Class<?> lineClass = lineInfo.getLineClass(); 1519 if (isMixingRequired 1520 && (SourceDataLine.class.isAssignableFrom(lineClass) || 1521 Clip.class.isAssignableFrom(lineClass))) { 1522 int maxLines = mixer.getMaxLines(lineInfo); 1523 return ((maxLines == NOT_SPECIFIED) || (maxLines > 1)); 1524 } 1525 return true; 1526 } 1527 1528 /** 1529 * Like getMixerInfo, but return List. 1530 */ 1531 private static List<Mixer.Info> getMixerInfoList() { 1532 List<MixerProvider> providers = getMixerProviders(); 1533 return getMixerInfoList(providers); 1534 } 1535 1536 /** 1537 * Like getMixerInfo, but return List. 1538 */ 1539 private static List<Mixer.Info> getMixerInfoList(List<MixerProvider> providers) { 1540 List<Mixer.Info> infos = new ArrayList<>(); 1541 1542 Mixer.Info[] someInfos; // per-mixer 1543 Mixer.Info[] allInfos; // for all mixers 1544 1545 for(int i = 0; i < providers.size(); i++ ) { 1546 someInfos = providers.get(i).getMixerInfo(); 1547 1548 for (int j = 0; j < someInfos.length; j++) { 1549 infos.add(someInfos[j]); 1550 } 1551 } 1552 1553 return infos; 1554 } 1555 1556 /** 1557 * Obtains the set of services currently installed on the system using the 1558 * SPI mechanism in 1.3. 1559 * 1560 * @return a List of instances of providers for the requested service. If no 1561 * providers are available, a vector of length 0 will be returned. 1562 */ 1563 private static List<?> getProviders(Class<?> providerClass) { 1564 return JDK13Services.getProviders(providerClass); 1565 } 1566 } --- EOF ---