1 /* 2 * Copyright (c) 1999, 2015, 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.IOException; 30 import java.io.InputStream; 31 import java.io.OutputStream; 32 import java.net.URL; 33 import java.util.ArrayList; 34 import java.util.HashSet; 35 import java.util.List; 36 import java.util.Objects; 37 import java.util.Properties; 38 import java.util.Set; 39 import java.util.Vector; 40 41 import javax.sound.sampled.spi.AudioFileReader; 42 import javax.sound.sampled.spi.AudioFileWriter; 43 import javax.sound.sampled.spi.FormatConversionProvider; 44 import javax.sound.sampled.spi.MixerProvider; 45 46 import com.sun.media.sound.JDK13Services; 47 48 /* $fb TODO: 49 * - consistent usage of (typed) collections 50 */ 51 52 53 /** 54 * The {@code AudioSystem} class acts as the entry point to the sampled-audio 55 * system resources. This class lets you query and access the mixers that are 56 * installed on the system. {@code AudioSystem} includes a number of methods for 57 * converting audio data between different formats, and for translating between 58 * audio files and streams. It also provides a method for obtaining a 59 * {@link Line} directly from the {@code AudioSystem} without dealing explicitly 60 * with mixers. 61 * <p> 62 * Properties can be used to specify the default mixer for specific line types. 63 * Both system properties and a properties file are considered. The 64 * "sound.properties" properties file is read from an implementation-specific 65 * location (typically it is the {@code conf} directory in the Java installation 66 * directory). If a property exists both as a system property and in the 67 * properties file, the system property takes precedence. If none is specified, 68 * a suitable default is chosen among the available devices. The syntax of the 69 * properties file is specified in 70 * {@link Properties#load(InputStream) Properties.load}. The following table 71 * lists the available property keys and which methods consider them: 72 * 73 * <table border=0> 74 * <caption>Audio System Property Keys</caption> 75 * <tr> 76 * <th>Property Key</th> 77 * <th>Interface</th> 78 * <th>Affected Method(s)</th> 79 * </tr> 80 * <tr> 81 * <td>{@code javax.sound.sampled.Clip}</td> 82 * <td>{@link Clip}</td> 83 * <td>{@link #getLine}, {@link #getClip}</td> 84 * </tr> 85 * <tr> 86 * <td>{@code javax.sound.sampled.Port}</td> 87 * <td>{@link Port}</td> 88 * <td>{@link #getLine}</td> 89 * </tr> 90 * <tr> 91 * <td>{@code javax.sound.sampled.SourceDataLine}</td> 92 * <td>{@link SourceDataLine}</td> 93 * <td>{@link #getLine}, {@link #getSourceDataLine}</td> 94 * </tr> 95 * <tr> 96 * <td>{@code javax.sound.sampled.TargetDataLine}</td> 97 * <td>{@link TargetDataLine}</td> 98 * <td>{@link #getLine}, {@link #getTargetDataLine}</td> 99 * </tr> 100 * </table> 101 * 102 * The property value consists of the provider class name and the mixer name, 103 * separated by the hash mark ("#"). The provider class name is the 104 * fully-qualified name of a concrete {@link MixerProvider mixer provider} 105 * class. The mixer name is matched against the {@code String} returned by the 106 * {@code getName} method of {@code Mixer.Info}. Either the class name, or the 107 * mixer name may be omitted. If only the class name is specified, the trailing 108 * hash mark is 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 * @throws NullPointerException if {@code sourceEncoding} is {@code null} 701 */ 702 public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat.Encoding sourceEncoding) { 703 Objects.requireNonNull(sourceEncoding); 704 705 List<FormatConversionProvider> codecs = getFormatConversionProviders(); 706 Vector<AudioFormat.Encoding> encodings = new Vector<>(); 707 708 AudioFormat.Encoding encs[] = null; 709 710 // gather from all the codecs 711 for(int i=0; i<codecs.size(); i++ ) { 712 FormatConversionProvider codec = codecs.get(i); 713 if( codec.isSourceEncodingSupported( sourceEncoding ) ) { 714 encs = codec.getTargetEncodings(); 715 for (int j = 0; j < encs.length; j++) { 716 encodings.addElement( encs[j] ); 717 } 718 } 719 } 720 AudioFormat.Encoding encs2[] = encodings.toArray(new AudioFormat.Encoding[0]); 721 return encs2; 722 } 723 724 // $$fb 2002-04-12: fix for 4662082: behavior of AudioSystem.getTargetEncodings() methods doesn't match the spec 725 726 /** 727 * Obtains the encodings that the system can obtain from an audio input 728 * stream with the specified format using the set of installed format 729 * converters. 730 * 731 * @param sourceFormat the audio format for which conversion is queried 732 * @return array of encodings. If {@code sourceFormat}is not supported, an 733 * array of length 0 is returned. Otherwise, the array will have a 734 * length of at least 1, representing the encoding of 735 * {@code sourceFormat} (no conversion). 736 * @throws NullPointerException if {@code sourceFormat} is {@code null} 737 */ 738 public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat) { 739 Objects.requireNonNull(sourceFormat); 740 741 List<FormatConversionProvider> codecs = getFormatConversionProviders(); 742 Vector<AudioFormat.Encoding[]> encodings = new Vector<>(); 743 744 int size = 0; 745 int index = 0; 746 AudioFormat.Encoding encs[] = null; 747 748 // gather from all the codecs 749 750 for(int i=0; i<codecs.size(); i++ ) { 751 encs = codecs.get(i).getTargetEncodings(sourceFormat); 752 size += encs.length; 753 encodings.addElement( encs ); 754 } 755 756 // now build a new array 757 758 AudioFormat.Encoding encs2[] = new AudioFormat.Encoding[size]; 759 for(int i=0; i<encodings.size(); i++ ) { 760 encs = encodings.get(i); 761 for(int j=0; j<encs.length; j++ ) { 762 encs2[index++] = encs[j]; 763 } 764 } 765 return encs2; 766 } 767 768 /** 769 * Indicates whether an audio input stream of the specified encoding can be 770 * obtained from an audio input stream that has the specified format. 771 * 772 * @param targetEncoding the desired encoding after conversion 773 * @param sourceFormat the audio format before conversion 774 * @return {@code true} if the conversion is supported, otherwise 775 * {@code false} 776 * @throws NullPointerException if {@code targetEncoding} or 777 * {@code sourceFormat} are {@code null} 778 */ 779 public static boolean isConversionSupported(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) { 780 Objects.requireNonNull(targetEncoding); 781 Objects.requireNonNull(sourceFormat); 782 783 List<FormatConversionProvider> codecs = getFormatConversionProviders(); 784 785 for(int i=0; i<codecs.size(); i++ ) { 786 FormatConversionProvider codec = codecs.get(i); 787 if(codec.isConversionSupported(targetEncoding,sourceFormat) ) { 788 return true; 789 } 790 } 791 return false; 792 } 793 794 /** 795 * Obtains an audio input stream of the indicated encoding, by converting 796 * the provided audio input stream. 797 * 798 * @param targetEncoding the desired encoding after conversion 799 * @param sourceStream the stream to be converted 800 * @return an audio input stream of the indicated encoding 801 * @throws IllegalArgumentException if the conversion is not supported 802 * @throws NullPointerException if {@code targetEncoding} or 803 * {@code sourceStream} are {@code null} 804 * @see #getTargetEncodings(AudioFormat.Encoding) 805 * @see #getTargetEncodings(AudioFormat) 806 * @see #isConversionSupported(AudioFormat.Encoding, AudioFormat) 807 * @see #getAudioInputStream(AudioFormat, AudioInputStream) 808 */ 809 public static AudioInputStream getAudioInputStream(AudioFormat.Encoding targetEncoding, 810 AudioInputStream sourceStream) { 811 Objects.requireNonNull(targetEncoding); 812 Objects.requireNonNull(sourceStream); 813 814 List<FormatConversionProvider> codecs = getFormatConversionProviders(); 815 816 for(int i = 0; i < codecs.size(); i++) { 817 FormatConversionProvider codec = codecs.get(i); 818 if( codec.isConversionSupported( targetEncoding, sourceStream.getFormat() ) ) { 819 return codec.getAudioInputStream( targetEncoding, sourceStream ); 820 } 821 } 822 // we ran out of options, throw an exception 823 throw new IllegalArgumentException("Unsupported conversion: " + targetEncoding + " from " + sourceStream.getFormat()); 824 } 825 826 /** 827 * Obtains the formats that have a particular encoding and that the system 828 * can obtain from a stream of the specified format using the set of 829 * installed format converters. 830 * 831 * @param targetEncoding the desired encoding after conversion 832 * @param sourceFormat the audio format before conversion 833 * @return array of formats. If no formats of the specified encoding are 834 * supported, an array of length 0 is returned. 835 * @throws NullPointerException if {@code targetEncoding} or 836 * {@code sourceFormat} are {@code null} 837 */ 838 public static AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) { 839 Objects.requireNonNull(targetEncoding); 840 Objects.requireNonNull(sourceFormat); 841 842 List<FormatConversionProvider> codecs = getFormatConversionProviders(); 843 Vector<AudioFormat[]> formats = new Vector<>(); 844 845 int size = 0; 846 int index = 0; 847 AudioFormat fmts[] = null; 848 849 // gather from all the codecs 850 851 for(int i=0; i<codecs.size(); i++ ) { 852 FormatConversionProvider codec = codecs.get(i); 853 fmts = codec.getTargetFormats(targetEncoding, sourceFormat); 854 size += fmts.length; 855 formats.addElement( fmts ); 856 } 857 858 // now build a new array 859 860 AudioFormat fmts2[] = new AudioFormat[size]; 861 for(int i=0; i<formats.size(); i++ ) { 862 fmts = formats.get(i); 863 for(int j=0; j<fmts.length; j++ ) { 864 fmts2[index++] = fmts[j]; 865 } 866 } 867 return fmts2; 868 } 869 870 /** 871 * Indicates whether an audio input stream of a specified format can be 872 * obtained from an audio input stream of another specified format. 873 * 874 * @param targetFormat the desired audio format after conversion 875 * @param sourceFormat the audio format before conversion 876 * @return {@code true} if the conversion is supported, otherwise 877 * {@code false} 878 * @throws NullPointerException if {@code targetFormat} or 879 * {@code sourceFormat} are {@code null} 880 */ 881 public static boolean isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat) { 882 Objects.requireNonNull(targetFormat); 883 Objects.requireNonNull(sourceFormat); 884 885 List<FormatConversionProvider> codecs = getFormatConversionProviders(); 886 887 for(int i=0; i<codecs.size(); i++ ) { 888 FormatConversionProvider codec = codecs.get(i); 889 if(codec.isConversionSupported(targetFormat, sourceFormat) ) { 890 return true; 891 } 892 } 893 return false; 894 } 895 896 /** 897 * Obtains an audio input stream of the indicated format, by converting the 898 * provided audio input stream. 899 * 900 * @param targetFormat the desired audio format after conversion 901 * @param sourceStream the stream to be converted 902 * @return an audio input stream of the indicated format 903 * @throws IllegalArgumentException if the conversion is not supported 904 * @throws NullPointerException if {@code targetFormat} or 905 * {@code sourceStream} are {@code null} 906 * @see #getTargetEncodings(AudioFormat) 907 * @see #getTargetFormats(AudioFormat.Encoding, AudioFormat) 908 * @see #isConversionSupported(AudioFormat, AudioFormat) 909 * @see #getAudioInputStream(AudioFormat.Encoding, AudioInputStream) 910 */ 911 public static AudioInputStream getAudioInputStream(AudioFormat targetFormat, 912 AudioInputStream sourceStream) { 913 if (sourceStream.getFormat().matches(targetFormat)) { 914 return sourceStream; 915 } 916 917 List<FormatConversionProvider> codecs = getFormatConversionProviders(); 918 919 for(int i = 0; i < codecs.size(); i++) { 920 FormatConversionProvider codec = codecs.get(i); 921 if(codec.isConversionSupported(targetFormat,sourceStream.getFormat()) ) { 922 return codec.getAudioInputStream(targetFormat,sourceStream); 923 } 924 } 925 926 // we ran out of options... 927 throw new IllegalArgumentException("Unsupported conversion: " + targetFormat + " from " + sourceStream.getFormat()); 928 } 929 930 /** 931 * Obtains the audio file format of the provided input stream. The stream 932 * must point to valid audio file data. The implementation of this method 933 * may require multiple parsers to examine the stream to determine whether 934 * they support it. These parsers must be able to mark the stream, read 935 * enough data to determine whether they support the stream, and reset the 936 * stream's read pointer to its original position. If the input stream does 937 * not support these operations, this method may fail with an 938 * {@code IOException}. 939 * 940 * @param stream the input stream from which file format information should 941 * be extracted 942 * @return an {@code AudioFileFormat} object describing the stream's audio 943 * file format 944 * @throws UnsupportedAudioFileException if the stream does not point to 945 * valid audio file data recognized by the system 946 * @throws IOException if an input/output exception occurs 947 * @throws NullPointerException if {@code stream} is {@code null} 948 * @see InputStream#markSupported 949 * @see InputStream#mark 950 */ 951 public static AudioFileFormat getAudioFileFormat(InputStream stream) 952 throws UnsupportedAudioFileException, IOException { 953 Objects.requireNonNull(stream); 954 955 List<AudioFileReader> providers = getAudioFileReaders(); 956 AudioFileFormat format = null; 957 958 for(int i = 0; i < providers.size(); i++ ) { 959 AudioFileReader reader = providers.get(i); 960 try { 961 format = reader.getAudioFileFormat( stream ); // throws IOException 962 break; 963 } catch (UnsupportedAudioFileException e) { 964 continue; 965 } 966 } 967 968 if( format==null ) { 969 throw new UnsupportedAudioFileException("file is not a supported file type"); 970 } else { 971 return format; 972 } 973 } 974 975 /** 976 * Obtains the audio file format of the specified URL. The URL must point to 977 * valid audio file data. 978 * 979 * @param url the URL from which file format information should be 980 * extracted 981 * @return an {@code AudioFileFormat} object describing the audio file 982 * format 983 * @throws UnsupportedAudioFileException if the URL does not point to valid 984 * audio file data recognized by the system 985 * @throws IOException if an input/output exception occurs 986 * @throws NullPointerException if {@code url} is {@code null} 987 */ 988 public static AudioFileFormat getAudioFileFormat(URL url) 989 throws UnsupportedAudioFileException, IOException { 990 Objects.requireNonNull(url); 991 992 List<AudioFileReader> providers = getAudioFileReaders(); 993 AudioFileFormat format = null; 994 995 for(int i = 0; i < providers.size(); i++ ) { 996 AudioFileReader reader = providers.get(i); 997 try { 998 format = reader.getAudioFileFormat( url ); // throws IOException 999 break; 1000 } catch (UnsupportedAudioFileException e) { 1001 continue; 1002 } 1003 } 1004 1005 if( format==null ) { 1006 throw new UnsupportedAudioFileException("file is not a supported file type"); 1007 } else { 1008 return format; 1009 } 1010 } 1011 1012 /** 1013 * Obtains the audio file format of the specified {@code File}. The 1014 * {@code File} must point to valid audio file data. 1015 * 1016 * @param file the {@code File} from which file format information should 1017 * be extracted 1018 * @return an {@code AudioFileFormat} object describing the audio file 1019 * format 1020 * @throws UnsupportedAudioFileException if the {@code File} does not point 1021 * to valid audio file data recognized by the system 1022 * @throws IOException if an I/O exception occurs 1023 * @throws NullPointerException if {@code file} is {@code null} 1024 */ 1025 public static AudioFileFormat getAudioFileFormat(File file) 1026 throws UnsupportedAudioFileException, IOException { 1027 Objects.requireNonNull(file); 1028 1029 List<AudioFileReader> providers = getAudioFileReaders(); 1030 AudioFileFormat format = null; 1031 1032 for(int i = 0; i < providers.size(); i++ ) { 1033 AudioFileReader reader = providers.get(i); 1034 try { 1035 format = reader.getAudioFileFormat( file ); // throws IOException 1036 break; 1037 } catch (UnsupportedAudioFileException e) { 1038 continue; 1039 } 1040 } 1041 1042 if( format==null ) { 1043 throw new UnsupportedAudioFileException("file is not a supported file type"); 1044 } else { 1045 return format; 1046 } 1047 } 1048 1049 /** 1050 * Obtains an audio input stream from the provided input stream. The stream 1051 * must point to valid audio file data. The implementation of this method 1052 * may require multiple parsers to examine the stream to determine whether 1053 * they support it. These parsers must be able to mark the stream, read 1054 * enough data to determine whether they support the stream, and reset the 1055 * stream's read pointer to its original position. If the input stream does 1056 * not support these operation, this method may fail with an 1057 * {@code IOException}. 1058 * 1059 * @param stream the input stream from which the {@code AudioInputStream} 1060 * should be constructed 1061 * @return an {@code AudioInputStream} object based on the audio file data 1062 * contained in the input stream 1063 * @throws UnsupportedAudioFileException if the stream does not point to 1064 * valid audio file data recognized by the system 1065 * @throws IOException if an I/O exception occurs 1066 * @throws NullPointerException if {@code stream} is {@code null} 1067 * @see InputStream#markSupported 1068 * @see InputStream#mark 1069 */ 1070 public static AudioInputStream getAudioInputStream(InputStream stream) 1071 throws UnsupportedAudioFileException, IOException { 1072 Objects.requireNonNull(stream); 1073 1074 List<AudioFileReader> providers = getAudioFileReaders(); 1075 AudioInputStream audioStream = null; 1076 1077 for(int i = 0; i < providers.size(); i++ ) { 1078 AudioFileReader reader = providers.get(i); 1079 try { 1080 audioStream = reader.getAudioInputStream( stream ); // throws IOException 1081 break; 1082 } catch (UnsupportedAudioFileException e) { 1083 continue; 1084 } 1085 } 1086 1087 if( audioStream==null ) { 1088 throw new UnsupportedAudioFileException("could not get audio input stream from input stream"); 1089 } else { 1090 return audioStream; 1091 } 1092 } 1093 1094 /** 1095 * Obtains an audio input stream from the URL provided. The URL must point 1096 * to valid audio file data. 1097 * 1098 * @param url the URL for which the {@code AudioInputStream} should be 1099 * constructed 1100 * @return an {@code AudioInputStream} object based on the audio file data 1101 * pointed to by the URL 1102 * @throws UnsupportedAudioFileException if the URL does not point to valid 1103 * audio file data recognized by the system 1104 * @throws IOException if an I/O exception occurs 1105 * @throws NullPointerException if {@code url} is {@code null} 1106 */ 1107 public static AudioInputStream getAudioInputStream(URL url) 1108 throws UnsupportedAudioFileException, IOException { 1109 Objects.requireNonNull(url); 1110 1111 List<AudioFileReader> providers = getAudioFileReaders(); 1112 AudioInputStream audioStream = null; 1113 1114 for(int i = 0; i < providers.size(); i++ ) { 1115 AudioFileReader reader = providers.get(i); 1116 try { 1117 audioStream = reader.getAudioInputStream( url ); // throws IOException 1118 break; 1119 } catch (UnsupportedAudioFileException e) { 1120 continue; 1121 } 1122 } 1123 1124 if( audioStream==null ) { 1125 throw new UnsupportedAudioFileException("could not get audio input stream from input URL"); 1126 } else { 1127 return audioStream; 1128 } 1129 } 1130 1131 /** 1132 * Obtains an audio input stream from the provided {@code File}. The 1133 * {@code File} must point to valid audio file data. 1134 * 1135 * @param file the {@code File} for which the {@code AudioInputStream} 1136 * should be constructed 1137 * @return an {@code AudioInputStream} object based on the audio file data 1138 * pointed to by the {@code File} 1139 * @throws UnsupportedAudioFileException if the {@code File} does not point 1140 * to valid audio file data recognized by the system 1141 * @throws IOException if an I/O exception occurs 1142 * @throws NullPointerException if {@code file} is {@code null} 1143 */ 1144 public static AudioInputStream getAudioInputStream(File file) 1145 throws UnsupportedAudioFileException, IOException { 1146 Objects.requireNonNull(file); 1147 1148 List<AudioFileReader> providers = getAudioFileReaders(); 1149 AudioInputStream audioStream = null; 1150 1151 for(int i = 0; i < providers.size(); i++ ) { 1152 AudioFileReader reader = providers.get(i); 1153 try { 1154 audioStream = reader.getAudioInputStream( file ); // throws IOException 1155 break; 1156 } catch (UnsupportedAudioFileException e) { 1157 continue; 1158 } 1159 } 1160 1161 if( audioStream==null ) { 1162 throw new UnsupportedAudioFileException("could not get audio input stream from input file"); 1163 } else { 1164 return audioStream; 1165 } 1166 } 1167 1168 /** 1169 * Obtains the file types for which file writing support is provided by the 1170 * system. 1171 * 1172 * @return array of unique file types. If no file types are supported, an 1173 * array of length 0 is returned. 1174 */ 1175 public static AudioFileFormat.Type[] getAudioFileTypes() { 1176 List<AudioFileWriter> providers = getAudioFileWriters(); 1177 Set<AudioFileFormat.Type> returnTypesSet = new HashSet<>(); 1178 1179 for(int i=0; i < providers.size(); i++) { 1180 AudioFileWriter writer = providers.get(i); 1181 AudioFileFormat.Type[] fileTypes = writer.getAudioFileTypes(); 1182 for(int j=0; j < fileTypes.length; j++) { 1183 returnTypesSet.add(fileTypes[j]); 1184 } 1185 } 1186 AudioFileFormat.Type returnTypes[] = 1187 returnTypesSet.toArray(new AudioFileFormat.Type[0]); 1188 return returnTypes; 1189 } 1190 1191 /** 1192 * Indicates whether file writing support for the specified file type is 1193 * provided by the system. 1194 * 1195 * @param fileType the file type for which write capabilities are queried 1196 * @return {@code true} if the file type is supported, otherwise 1197 * {@code false} 1198 * @throws NullPointerException if {@code fileType} is {@code null} 1199 */ 1200 public static boolean isFileTypeSupported(AudioFileFormat.Type fileType) { 1201 Objects.requireNonNull(fileType); 1202 List<AudioFileWriter> providers = getAudioFileWriters(); 1203 1204 for(int i=0; i < providers.size(); i++) { 1205 AudioFileWriter writer = providers.get(i); 1206 if (writer.isFileTypeSupported(fileType)) { 1207 return true; 1208 } 1209 } 1210 return false; 1211 } 1212 1213 /** 1214 * Obtains the file types that the system can write from the audio input 1215 * stream specified. 1216 * 1217 * @param stream the audio input stream for which audio file type 1218 * support is queried 1219 * @return array of file types. If no file types are supported, an array of 1220 * length 0 is returned. 1221 * @throws NullPointerException if {@code stream} is {@code null} 1222 */ 1223 public static AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) { 1224 Objects.requireNonNull(stream); 1225 List<AudioFileWriter> providers = getAudioFileWriters(); 1226 Set<AudioFileFormat.Type> returnTypesSet = new HashSet<>(); 1227 1228 for(int i=0; i < providers.size(); i++) { 1229 AudioFileWriter writer = providers.get(i); 1230 AudioFileFormat.Type[] fileTypes = writer.getAudioFileTypes(stream); 1231 for(int j=0; j < fileTypes.length; j++) { 1232 returnTypesSet.add(fileTypes[j]); 1233 } 1234 } 1235 AudioFileFormat.Type returnTypes[] = 1236 returnTypesSet.toArray(new AudioFileFormat.Type[0]); 1237 return returnTypes; 1238 } 1239 1240 /** 1241 * Indicates whether an audio file of the specified file type can be written 1242 * from the indicated audio input stream. 1243 * 1244 * @param fileType the file type for which write capabilities are queried 1245 * @param stream the stream for which file-writing support is queried 1246 * @return {@code true} if the file type is supported for this audio input 1247 * stream, otherwise {@code false} 1248 * @throws NullPointerException if {@code fileType} or {@code stream} are 1249 * {@code null} 1250 */ 1251 public static boolean isFileTypeSupported(AudioFileFormat.Type fileType, 1252 AudioInputStream stream) { 1253 Objects.requireNonNull(fileType); 1254 Objects.requireNonNull(stream); 1255 List<AudioFileWriter> providers = getAudioFileWriters(); 1256 1257 for(int i=0; i < providers.size(); i++) { 1258 AudioFileWriter writer = providers.get(i); 1259 if(writer.isFileTypeSupported(fileType, stream)) { 1260 return true; 1261 } 1262 } 1263 return false; 1264 } 1265 1266 /** 1267 * Writes a stream of bytes representing an audio file of the specified file 1268 * type to the output stream provided. Some file types require that the 1269 * length be written into the file header; such files cannot be written from 1270 * start to finish unless the length is known in advance. An attempt to 1271 * write a file of such a type will fail with an IOException if the length 1272 * in the audio file type is {@code AudioSystem.NOT_SPECIFIED}. 1273 * 1274 * @param stream the audio input stream containing audio data to be written 1275 * to the file 1276 * @param fileType the kind of audio file to write 1277 * @param out the stream to which the file data should be written 1278 * @return the number of bytes written to the output stream 1279 * @throws IOException if an input/output exception occurs 1280 * @throws IllegalArgumentException if the file type is not supported by the 1281 * system 1282 * @throws NullPointerException if {@code stream} or {@code fileType} or 1283 * {@code out} are {@code null} 1284 * @see #isFileTypeSupported 1285 * @see #getAudioFileTypes 1286 */ 1287 public static int write(AudioInputStream stream, AudioFileFormat.Type fileType, 1288 OutputStream out) throws IOException { 1289 Objects.requireNonNull(stream); 1290 Objects.requireNonNull(fileType); 1291 Objects.requireNonNull(out); 1292 1293 List<AudioFileWriter> providers = getAudioFileWriters(); 1294 int bytesWritten = 0; 1295 boolean flag = false; 1296 1297 for(int i=0; i < providers.size(); i++) { 1298 AudioFileWriter writer = providers.get(i); 1299 try { 1300 bytesWritten = writer.write( stream, fileType, out ); // throws IOException 1301 flag = true; 1302 break; 1303 } catch (IllegalArgumentException e) { 1304 // thrown if this provider cannot write the sequence, try the next 1305 continue; 1306 } 1307 } 1308 if(!flag) { 1309 throw new IllegalArgumentException("could not write audio file: file type not supported: " + fileType); 1310 } else { 1311 return bytesWritten; 1312 } 1313 } 1314 1315 /** 1316 * Writes a stream of bytes representing an audio file of the specified file 1317 * type to the external file provided. 1318 * 1319 * @param stream the audio input stream containing audio data to be written 1320 * to the file 1321 * @param fileType the kind of audio file to write 1322 * @param out the external file to which the file data should be written 1323 * @return the number of bytes written to the file 1324 * @throws IOException if an I/O exception occurs 1325 * @throws IllegalArgumentException if the file type is not supported by the 1326 * system 1327 * @throws NullPointerException if {@code stream} or {@code fileType} or 1328 * {@code out} are {@code null} 1329 * @see #isFileTypeSupported 1330 * @see #getAudioFileTypes 1331 */ 1332 public static int write(AudioInputStream stream, AudioFileFormat.Type fileType, 1333 File out) throws IOException { 1334 Objects.requireNonNull(stream); 1335 Objects.requireNonNull(fileType); 1336 Objects.requireNonNull(out); 1337 1338 List<AudioFileWriter> providers = getAudioFileWriters(); 1339 int bytesWritten = 0; 1340 boolean flag = false; 1341 1342 for(int i=0; i < providers.size(); i++) { 1343 AudioFileWriter writer = providers.get(i); 1344 try { 1345 bytesWritten = writer.write( stream, fileType, out ); // throws IOException 1346 flag = true; 1347 break; 1348 } catch (IllegalArgumentException e) { 1349 // thrown if this provider cannot write the sequence, try the next 1350 continue; 1351 } 1352 } 1353 if (!flag) { 1354 throw new IllegalArgumentException("could not write audio file: file type not supported: " + fileType); 1355 } else { 1356 return bytesWritten; 1357 } 1358 } 1359 1360 // METHODS FOR INTERNAL IMPLEMENTATION USE 1361 1362 /** 1363 * Obtains the set of MixerProviders on the system. 1364 */ 1365 @SuppressWarnings("unchecked") 1366 private static List<MixerProvider> getMixerProviders() { 1367 return (List<MixerProvider>) getProviders(MixerProvider.class); 1368 } 1369 1370 /** 1371 * Obtains the set of format converters (codecs, transcoders, etc.) that are 1372 * currently installed on the system. 1373 * 1374 * @return an array of {@link FormatConversionProvider} objects representing 1375 * the available format converters. If no format converters readers 1376 * are available on the system, an array of length 0 is returned. 1377 */ 1378 @SuppressWarnings("unchecked") 1379 private static List<FormatConversionProvider> getFormatConversionProviders() { 1380 return (List<FormatConversionProvider>) getProviders(FormatConversionProvider.class); 1381 } 1382 1383 /** 1384 * Obtains the set of audio file readers that are currently installed on the 1385 * system. 1386 * 1387 * @return a List of {@link AudioFileReader} objects representing the 1388 * installed audio file readers. If no audio file readers are 1389 * available on the system, an empty List is returned. 1390 */ 1391 @SuppressWarnings("unchecked") 1392 private static List<AudioFileReader> getAudioFileReaders() { 1393 return (List<AudioFileReader>)getProviders(AudioFileReader.class); 1394 } 1395 1396 /** 1397 * Obtains the set of audio file writers that are currently installed on the 1398 * system. 1399 * 1400 * @return a List of {@link AudioFileWriter} objects representing the 1401 * available audio file writers. If no audio file writers are 1402 * available on the system, an empty List is returned. 1403 */ 1404 @SuppressWarnings("unchecked") 1405 private static List<AudioFileWriter> getAudioFileWriters() { 1406 return (List<AudioFileWriter>)getProviders(AudioFileWriter.class); 1407 } 1408 1409 /** 1410 * Attempts to locate and return a default Mixer that provides lines of the 1411 * specified type. 1412 * 1413 * @param providers the installed mixer providers 1414 * @param info The requested line type TargetDataLine.class, Clip.class or 1415 * Port.class 1416 * @return a Mixer that matches the requirements, or null if no default 1417 * mixer found 1418 */ 1419 private static Mixer getDefaultMixer(List<MixerProvider> providers, Line.Info info) { 1420 Class<?> lineClass = info.getLineClass(); 1421 String providerClassName = JDK13Services.getDefaultProviderClassName(lineClass); 1422 String instanceName = JDK13Services.getDefaultInstanceName(lineClass); 1423 Mixer mixer; 1424 1425 if (providerClassName != null) { 1426 MixerProvider defaultProvider = getNamedProvider(providerClassName, providers); 1427 if (defaultProvider != null) { 1428 if (instanceName != null) { 1429 mixer = getNamedMixer(instanceName, defaultProvider, info); 1430 if (mixer != null) { 1431 return mixer; 1432 } 1433 } else { 1434 mixer = getFirstMixer(defaultProvider, info, 1435 false /* mixing not required*/); 1436 if (mixer != null) { 1437 return mixer; 1438 } 1439 } 1440 1441 } 1442 } 1443 1444 /* Provider class not specified or 1445 provider class cannot be found, or 1446 provider class and instance specified and instance cannot be found or is not appropriate */ 1447 if (instanceName != null) { 1448 mixer = getNamedMixer(instanceName, providers, info); 1449 if (mixer != null) { 1450 return mixer; 1451 } 1452 } 1453 1454 1455 /* No default are specified, or if something is specified, everything 1456 failed. */ 1457 return null; 1458 } 1459 1460 /** 1461 * Return a MixerProvider of a given class from the list of MixerProviders. 1462 * This method never requires the returned Mixer to do mixing. 1463 * 1464 * @param providerClassName The class name of the provider to be returned 1465 * @param providers The list of MixerProviders that is searched 1466 * @return A MixerProvider of the requested class, or null if none is found 1467 */ 1468 private static MixerProvider getNamedProvider(String providerClassName, 1469 List<MixerProvider> providers) { 1470 for(int i = 0; i < providers.size(); i++) { 1471 MixerProvider provider = providers.get(i); 1472 if (provider.getClass().getName().equals(providerClassName)) { 1473 return provider; 1474 } 1475 } 1476 return null; 1477 } 1478 1479 /** 1480 * Return a Mixer with a given name from a given MixerProvider. This method 1481 * never requires the returned Mixer to do mixing. 1482 * 1483 * @param mixerName The name of the Mixer to be returned 1484 * @param provider The MixerProvider to check for Mixers 1485 * @param info The type of line the returned Mixer is required to support 1486 * @return A Mixer matching the requirements, or null if none is found 1487 */ 1488 private static Mixer getNamedMixer(String mixerName, 1489 MixerProvider provider, 1490 Line.Info info) { 1491 Mixer.Info[] infos = provider.getMixerInfo(); 1492 for (int i = 0; i < infos.length; i++) { 1493 if (infos[i].getName().equals(mixerName)) { 1494 Mixer mixer = provider.getMixer(infos[i]); 1495 if (isAppropriateMixer(mixer, info, false)) { 1496 return mixer; 1497 } 1498 } 1499 } 1500 return null; 1501 } 1502 1503 /** 1504 * From a List of MixerProviders, return a Mixer with a given name. This 1505 * method never requires the returned Mixer to do mixing. 1506 * 1507 * @param mixerName The name of the Mixer to be returned 1508 * @param providers The List of MixerProviders to check for Mixers 1509 * @param info The type of line the returned Mixer is required to support 1510 * @return A Mixer matching the requirements, or null if none is found 1511 */ 1512 private static Mixer getNamedMixer(String mixerName, 1513 List<MixerProvider> providers, 1514 Line.Info info) { 1515 for(int i = 0; i < providers.size(); i++) { 1516 MixerProvider provider = providers.get(i); 1517 Mixer mixer = getNamedMixer(mixerName, provider, info); 1518 if (mixer != null) { 1519 return mixer; 1520 } 1521 } 1522 return null; 1523 } 1524 1525 /** 1526 * From a given MixerProvider, return the first appropriate Mixer. 1527 * 1528 * @param provider The MixerProvider to check for Mixers 1529 * @param info The type of line the returned Mixer is required to support 1530 * @param isMixingRequired If true, only Mixers that support mixing are 1531 * returned for line types of SourceDataLine and Clip 1532 * @return A Mixer that is considered appropriate, or null if none is found 1533 */ 1534 private static Mixer getFirstMixer(MixerProvider provider, 1535 Line.Info info, 1536 boolean isMixingRequired) { 1537 Mixer.Info[] infos = provider.getMixerInfo(); 1538 for (int j = 0; j < infos.length; j++) { 1539 Mixer mixer = provider.getMixer(infos[j]); 1540 if (isAppropriateMixer(mixer, info, isMixingRequired)) { 1541 return mixer; 1542 } 1543 } 1544 return null; 1545 } 1546 1547 /** 1548 * Checks if a Mixer is appropriate. A Mixer is considered appropriate if it 1549 * support the given line type. If isMixingRequired is true and the line 1550 * type is an output one (SourceDataLine, Clip), the mixer is appropriate if 1551 * it supports at least 2 (concurrent) lines of the given type. 1552 * 1553 * @return {@code true} if the mixer is considered appropriate according to 1554 * the rules given above, {@code false} otherwise 1555 */ 1556 private static boolean isAppropriateMixer(Mixer mixer, 1557 Line.Info lineInfo, 1558 boolean isMixingRequired) { 1559 if (! mixer.isLineSupported(lineInfo)) { 1560 return false; 1561 } 1562 Class<?> lineClass = lineInfo.getLineClass(); 1563 if (isMixingRequired 1564 && (SourceDataLine.class.isAssignableFrom(lineClass) || 1565 Clip.class.isAssignableFrom(lineClass))) { 1566 int maxLines = mixer.getMaxLines(lineInfo); 1567 return ((maxLines == NOT_SPECIFIED) || (maxLines > 1)); 1568 } 1569 return true; 1570 } 1571 1572 /** 1573 * Like getMixerInfo, but return List. 1574 */ 1575 private static List<Mixer.Info> getMixerInfoList() { 1576 List<MixerProvider> providers = getMixerProviders(); 1577 return getMixerInfoList(providers); 1578 } 1579 1580 /** 1581 * Like getMixerInfo, but return List. 1582 */ 1583 private static List<Mixer.Info> getMixerInfoList(List<MixerProvider> providers) { 1584 List<Mixer.Info> infos = new ArrayList<>(); 1585 1586 Mixer.Info[] someInfos; // per-mixer 1587 Mixer.Info[] allInfos; // for all mixers 1588 1589 for(int i = 0; i < providers.size(); i++ ) { 1590 someInfos = providers.get(i).getMixerInfo(); 1591 1592 for (int j = 0; j < someInfos.length; j++) { 1593 infos.add(someInfos[j]); 1594 } 1595 } 1596 1597 return infos; 1598 } 1599 1600 /** 1601 * Obtains the set of services currently installed on the system using the 1602 * SPI mechanism in 1.3. 1603 * 1604 * @return a List of instances of providers for the requested service. If no 1605 * providers are available, a vector of length 0 will be returned. 1606 */ 1607 private static List<?> getProviders(Class<?> providerClass) { 1608 return JDK13Services.getProviders(providerClass); 1609 } 1610 }