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