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