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