blob: cf277ad7af6f7145730c62cffc107a33aed234f3 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1999-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package javax.sound.sampled;
27
28import java.io.File;
29import java.io.InputStream;
30import java.io.IOException;
31import java.io.OutputStream;
32import java.net.URL;
33
34import java.util.HashSet;
35import java.util.List;
36import java.util.Set;
37import java.util.Vector;
38import java.util.ArrayList;
39
40import javax.sound.sampled.spi.AudioFileWriter;
41import javax.sound.sampled.spi.AudioFileReader;
42import javax.sound.sampled.spi.FormatConversionProvider;
43import javax.sound.sampled.spi.MixerProvider;
44
45import 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 Sun reference implementation, the properties file is
67 * &quot;lib/sound.properties&quot; 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 (&quot;#&quot;).
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>&quot;com.sun.media.sound.MixerProvider#SunClip&quot;</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>&quot;SunClip&quot;</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>&quot;SunClip&quot;</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 */
168public 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 &quot;sound.properties&quot;,
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 &quot;sound.properties&quot;,
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 &quot;sound.properties&quot;,
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 * @param format an <code>AudioFormat</code> object specifying
674 * the supported audio format of the returned line,
675 * or <code>null</code> for any audio format
676 * @return the desired <code>TargetDataLine</code> object
677 *
678 * @throws LineUnavailableException if a matching target data line
679 * is not available due to resource restrictions
680 * @throws SecurityException if a matching target data line
681 * is not available due to security restrictions
682 * @throws IllegalArgumentException if the system does not
683 * support at least one target data line supporting the
684 * specified audio format through any installed mixer
685 *
686 * @see #getTargetDataLine(AudioFormat, Mixer.Info)
687 * @see AudioPermission
688 * @since 1.5
689 */
690 public static TargetDataLine getTargetDataLine(AudioFormat format)
691 throws LineUnavailableException{
692
693 DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
694 return (TargetDataLine) AudioSystem.getLine(info);
695 }
696
697
698
699 /**
700 * Obtains a target data line that can be used for recording
701 * audio data in the format specified by the
702 * <code>AudioFormat</code> object, provided by the mixer
703 * specified by the <code>Mixer.Info</code> object.
704 *
705 * <p>The returned line should be opened with the
706 * <code>open(AudioFormat)</code> or
707 * <code>open(AudioFormat, int)</code> method.
708 *
709 * <p>This is a high-level method that uses <code>getMixer</code>
710 * and <code>getLine</code> internally.
711 *
712 * <p>The returned <code>TargetDataLine</code>'s default
713 * audio format will be initialized with <code>format</code>.
714 *
715 * <p>If the system property
716 * <code>javax.sound.sampled.TargetDataLine</code>
717 * is defined or it is defined in the file &quot;sound.properties&quot;,
718 * it is used to retrieve the default target data line.
719 * For details, refer to the {@link AudioSystem class description}.
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}