1. /*
  2. * @(#)AudioSystem.java 1.66 03/03/21
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.sound.sampled;
  8. import java.io.File;
  9. import java.io.InputStream;
  10. import java.io.IOException;
  11. import java.io.OutputStream;
  12. import java.net.URL;
  13. import java.util.Vector;
  14. import java.lang.reflect.Method;
  15. import java.lang.reflect.InvocationTargetException;
  16. import javax.sound.sampled.spi.AudioFileWriter;
  17. import javax.sound.sampled.spi.AudioFileReader;
  18. import javax.sound.sampled.spi.FormatConversionProvider;
  19. import javax.sound.sampled.spi.MixerProvider;
  20. /**
  21. * The <code>AudioSystem</code> class acts as the entry point to the
  22. * sampled-audio system resources. This class lets you query and
  23. * access the mixers that are installed on the system.
  24. * <code>AudioSystem</code> includes a number of
  25. * methods for converting audio data between different formats, and for
  26. * translating between audio files and streams. It also provides a method
  27. * for obtaining a <code>{@link Line}</code> directly from the
  28. * <code>AudioSystem</code> without dealing explicitly
  29. * with mixers.
  30. *
  31. * @author Kara Kytle
  32. * @version 1.66, 03/03/21
  33. *
  34. * @see AudioFormat
  35. * @see AudioInputStream
  36. * @see Mixer
  37. * @see Line
  38. * @see Line.Info
  39. * @since 1.3
  40. */
  41. public class AudioSystem {
  42. /**
  43. * An integer that stands for an unknown numeric value.
  44. * This value is appropriate only for signed quantities that do not
  45. * normally take negative values. Examples include file sizes, frame
  46. * sizes, buffer sizes, and sample rates.
  47. * A number of Java Sound constructors accept
  48. * a value of <code>NOT_SPECIFIED</code> for such parameters. Other
  49. * methods may also accept or return this value, as documented.
  50. */
  51. public static final int NOT_SPECIFIED = -1;
  52. /**
  53. * Private strings for services classes and methods
  54. */
  55. private static final String defaultServicesClassName =
  56. "com.sun.media.sound.DefaultServices";
  57. private static final String jdk13ServicesClassName =
  58. "com.sun.media.sound.JDK13Services";
  59. private static final String servicesMethodName =
  60. "getProviders";
  61. private static final Class[] servicesParamTypes =
  62. new Class[] { String.class };
  63. /**
  64. * debugging
  65. */
  66. private final static boolean DEBUG = false;
  67. /**
  68. * Private no-args constructor for ensuring against instantiation.
  69. */
  70. private AudioSystem() {
  71. }
  72. /**
  73. * Obtains an array of mixer info objects that represents
  74. * the set of audio mixers that are currently installed on the system.
  75. * @return an array of info objects for the currently installed mixers. If no mixers
  76. * are available on the system, an array of length 0 is returned.
  77. * @see #getMixer
  78. */
  79. public static Mixer.Info[] getMixerInfo() {
  80. int i;
  81. int j;
  82. Vector providers = getMixerProviders();
  83. Vector infos = new Vector();
  84. Mixer.Info[] someInfos; // per-mixer
  85. Mixer.Info[] allInfos; // for all mixers
  86. //$$fb 2002-11-07: addendum fix for
  87. // bug 4487550: Service providers cannot be used to replace existing providers
  88. for(i = providers.size() - 1; i >= 0; i-- ) {
  89. someInfos = (Mixer.Info[])
  90. ((MixerProvider)providers.elementAt(i)).getMixerInfo();
  91. for (j = 0; j < someInfos.length; j++) {
  92. infos.addElement(someInfos[j]);
  93. }
  94. }
  95. allInfos = new Mixer.Info[infos.size()];
  96. for (i = 0; i < allInfos.length; i++) {
  97. allInfos[i] = (Mixer.Info)infos.elementAt(i);
  98. }
  99. return allInfos;
  100. }
  101. /**
  102. * Obtains the requested audio mixer.
  103. * @param info a <code>Mixer.Info</code> object representing the desired
  104. * mixer, or <code>null</code> for the system default mixer
  105. * @return the requested mixer
  106. * @throws SecurityException if the requested mixer
  107. * is unavailable because of security restrictions
  108. * @throws IllegalArgumentException if the info object does not represent
  109. * a mixer installed on the system
  110. * @see #getMixerInfo
  111. */
  112. public static Mixer getMixer(Mixer.Info info) {
  113. Mixer mixer = null;
  114. Vector providers = getMixerProviders();
  115. //$$fb 2002-11-07: addendum fix for
  116. // bug 4487550: Service providers cannot be used to replace existing providers
  117. for(int i = providers.size() -1; i >= 0; i-- ) {
  118. try {
  119. return ((MixerProvider)providers.elementAt(i)).getMixer(info);
  120. } catch (IllegalArgumentException e) {
  121. } catch (NullPointerException e) {
  122. // $$jb 08.20.99: If the strings in the info object aren't
  123. // set, then Netscape (using jdk1.1.5) tends to throw
  124. // NPE's when doing some string manipulation. This is
  125. // probably not the best fix, but is solves the problem
  126. // of the NPE in Netscape using local classes
  127. // $$jb 11.01.99: Replacing this patch.
  128. }
  129. }
  130. //$$fb if looking for default mixer, and not found yet, add a round of looking
  131. if (info == null) {
  132. for(int i = providers.size() -1; i >= 0; i-- ) {
  133. try {
  134. MixerProvider provider = (MixerProvider) providers.elementAt(i);
  135. Mixer.Info[] infos = provider.getMixerInfo();
  136. // start from 0 to last device (do not reverse this order)
  137. for (int ii = 0; ii < infos.length; ii++) {
  138. try {
  139. return provider.getMixer(infos[ii]);
  140. } catch (IllegalArgumentException e) {
  141. // this is not a good default device :)
  142. }
  143. }
  144. } catch (IllegalArgumentException e) {
  145. } catch (NullPointerException e) {
  146. }
  147. }
  148. }
  149. throw new IllegalArgumentException("Mixer not supported: "
  150. + (info!=null?info.toString():"null"));
  151. }
  152. //$$fb 2002-11-26: fix for 4757930: DOC: AudioSystem.getTarget/SourceLineInfo() is ambiguous
  153. /**
  154. * Obtains information about all source lines of a particular type that are supported
  155. * by the installed mixers.
  156. * @param info a <code>Line.Info</code> object that specifies the kind of
  157. * lines about which information is requested
  158. * @return an array of <code>Line.Info</code> objects describing source lines matching
  159. * the type requested. If no matching source lines are supported, an array of length 0
  160. * is returned.
  161. *
  162. * @see Mixer#getSourceLineInfo(Line.Info)
  163. */
  164. public static Line.Info[] getSourceLineInfo(Line.Info info) {
  165. Vector vector = new Vector();
  166. Line.Info[] currentInfoArray;
  167. Mixer mixer;
  168. Line.Info fullInfo = null;
  169. Mixer.Info[] infoArray = getMixerInfo();
  170. for (int i = 0; i < infoArray.length; i++) {
  171. mixer = getMixer(infoArray[i]);
  172. currentInfoArray = mixer.getSourceLineInfo(info);
  173. for (int j = 0; j < currentInfoArray.length; j++) {
  174. vector.addElement(currentInfoArray[j]);
  175. }
  176. }
  177. Line.Info[] returnedArray = new Line.Info[vector.size()];
  178. for (int i = 0; i < returnedArray.length; i++) {
  179. returnedArray[i] = (Line.Info)vector.elementAt(i);
  180. }
  181. return returnedArray;
  182. }
  183. /**
  184. * Obtains information about all target lines of a particular type that are supported
  185. * by the installed mixers.
  186. * @param info a <code>Line.Info</code> object that specifies the kind of
  187. * lines about which information is requested
  188. * @return an array of <code>Line.Info</code> objects describing target lines matching
  189. * the type requested. If no matching target lines are supported, an array of length 0
  190. * is returned.
  191. *
  192. * @see Mixer#getTargetLineInfo(Line.Info)
  193. */
  194. public static Line.Info[] getTargetLineInfo(Line.Info info) {
  195. Vector vector = new Vector();
  196. Line.Info[] currentInfoArray;
  197. Mixer mixer;
  198. Line.Info fullInfo = null;
  199. Mixer.Info[] infoArray = getMixerInfo();
  200. for (int i = 0; i < infoArray.length; i++) {
  201. mixer = getMixer(infoArray[i]);
  202. currentInfoArray = mixer.getTargetLineInfo(info);
  203. for (int j = 0; j < currentInfoArray.length; j++) {
  204. vector.addElement(currentInfoArray[j]);
  205. }
  206. }
  207. Line.Info[] returnedArray = new Line.Info[vector.size()];
  208. for (int i = 0; i < returnedArray.length; i++) {
  209. returnedArray[i] = (Line.Info)vector.elementAt(i);
  210. }
  211. return returnedArray;
  212. }
  213. /**
  214. * Indicates whether the system supports any lines that match
  215. * the specified <code>Line.Info</code> object. A line is supported if
  216. * any installed mixer supports it.
  217. * @param info a <code>Line.Info</code> object describing the line for which support is queried
  218. * @return <code>true</code> if at least one matching line is
  219. * supported, otherwise <code>false</code>
  220. *
  221. * @see Mixer#isLineSupported(Line.Info)
  222. */
  223. public static boolean isLineSupported(Line.Info info) {
  224. Mixer mixer;
  225. Mixer.Info[] infoArray = getMixerInfo();
  226. for (int i = 0; i < infoArray.length; i++) {
  227. if( infoArray[i] != null ) {
  228. mixer = getMixer(infoArray[i]);
  229. if (mixer.isLineSupported(info)) {
  230. return true;
  231. }
  232. }
  233. }
  234. return false;
  235. }
  236. /**
  237. * Obtains a line that matches the description in the specified
  238. * <code>Line.Info</code> object.
  239. *
  240. * @param info a <code>Line.Info</code> object describing the desired kind of line
  241. * @return a line of the requested kind
  242. *
  243. * @throws LineUnavailableException if a matching line
  244. * is not available due to resource restrictions
  245. * @throws SecurityException if a matching line
  246. * is not available due to security restrictions
  247. * @throws IllegalArgumentException if the system does not
  248. * support at least one line matching the specified <code>Line.Info</code> object
  249. * through any installed mixer
  250. */
  251. public static Line getLine(Line.Info info)
  252. throws LineUnavailableException {
  253. Mixer mixer;
  254. Mixer.Info[] infoArray = getMixerInfo();
  255. LineUnavailableException lue = null;
  256. for (int i = 0; i < infoArray.length; i++) {
  257. mixer = getMixer(infoArray[i]);
  258. if (mixer.isLineSupported(info)) {
  259. try {
  260. return mixer.getLine(info);
  261. } catch (LineUnavailableException e) {
  262. lue = e;
  263. }
  264. }
  265. }
  266. // if this line was supported but was not available, throw the last
  267. // LineUnavailableException we got (??).
  268. if (lue != null) {
  269. throw lue;
  270. }
  271. // otherwise, the requested line was not supported, so throw
  272. // an Illegal argument exception
  273. throw new IllegalArgumentException("No line matching " +
  274. info.toString() + " is supported.");
  275. }
  276. // $$fb 2002-04-12: fix for 4662082: behavior of AudioSystem.getTargetEncodings() methods doesn't match the spec
  277. /**
  278. * Obtains the encodings that the system can obtain from an
  279. * audio input stream with the specified encoding using the set
  280. * of installed format converters.
  281. * @param sourceEncoding the encoding for which conversion support
  282. * is queried
  283. * @return array of encodings. If <code>sourceEncoding</code>is not supported,
  284. * an array of length 0 is returned. Otherwise, the array will have a length
  285. * of at least 1, representing <code>sourceEncoding</code> (no conversion).
  286. */
  287. public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat.Encoding sourceEncoding) {
  288. FormatConversionProvider codecs[] = getFormatConversionProviders();
  289. Vector encodings = new Vector();
  290. int size = 0;
  291. int index = 0;
  292. AudioFormat.Encoding encs[] = null;
  293. // gather from all the codecs
  294. for(int i=0; i<codecs.length; i++ ) {
  295. if( codecs[i].isSourceEncodingSupported( sourceEncoding ) ) {
  296. encs = codecs[i].getTargetEncodings();
  297. size += encs.length;
  298. encodings.addElement( encs );
  299. }
  300. }
  301. // now build a new array
  302. AudioFormat.Encoding encs2[] = new AudioFormat.Encoding[size];
  303. for(int i=0; i<encodings.size(); i++ ) {
  304. encs = (AudioFormat.Encoding [])(encodings.elementAt(i));
  305. for(int j=0; j<encs.length; j++ ) {
  306. encs2[index++] = encs[j];
  307. }
  308. }
  309. return encs2;
  310. }
  311. // $$fb 2002-04-12: fix for 4662082: behavior of AudioSystem.getTargetEncodings() methods doesn't match the spec
  312. /**
  313. * Obtains the encodings that the system can obtain from an
  314. * audio input stream with the specified format using the set
  315. * of installed format converters.
  316. * @param sourceFormat the audio format for which conversion
  317. * is queried
  318. * @return array of encodings. If <code>sourceFormat</code>is not supported,
  319. * an array of length 0 is returned. Otherwise, the array will have a length
  320. * of at least 1, representing the encoding of <code>sourceFormat</code> (no conversion).
  321. */
  322. public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat) {
  323. FormatConversionProvider codecs[] = getFormatConversionProviders();
  324. Vector encodings = new Vector();
  325. int size = 0;
  326. int index = 0;
  327. AudioFormat.Encoding encs[] = null;
  328. // gather from all the codecs
  329. for(int i=0; i<codecs.length; i++ ) {
  330. encs = codecs[i].getTargetEncodings(sourceFormat);
  331. size += encs.length;
  332. encodings.addElement( encs );
  333. }
  334. // now build a new array
  335. AudioFormat.Encoding encs2[] = new AudioFormat.Encoding[size];
  336. for(int i=0; i<encodings.size(); i++ ) {
  337. encs = (AudioFormat.Encoding [])(encodings.elementAt(i));
  338. for(int j=0; j<encs.length; j++ ) {
  339. encs2[index++] = encs[j];
  340. }
  341. }
  342. return encs2;
  343. }
  344. /**
  345. * Indicates whether an audio input stream of the specified encoding
  346. * can be obtained from an audio input stream that has the specified
  347. * format.
  348. * @param targetEncoding the desired encoding after conversion
  349. * @param sourceFormat the audio format before conversion
  350. * @return <code>true</code> if the conversion is supported,
  351. * otherwise <code>false</code>
  352. */
  353. public static boolean isConversionSupported(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) {
  354. FormatConversionProvider codecs[] = getFormatConversionProviders();
  355. for(int i=0; i<codecs.length; i++ ) {
  356. if(codecs[i].isConversionSupported(targetEncoding,sourceFormat) ) {
  357. return true;
  358. }
  359. }
  360. return false;
  361. }
  362. /**
  363. * Obtains an audio input stream of the indicated encoding, by converting the
  364. * provided audio input stream.
  365. * @param targetEncoding the desired encoding after conversion
  366. * @param sourceStream the stream to be converted
  367. * @return an audio input stream of the indicated encoding
  368. * @throws IllegalArgumentException if the conversion is not supported
  369. * @see #getTargetEncodings(AudioFormat.Encoding)
  370. * @see #getTargetEncodings(AudioFormat)
  371. * @see #isConversionSupported(AudioFormat.Encoding, AudioFormat)
  372. * @see #getAudioInputStream(AudioFormat, AudioInputStream)
  373. */
  374. public static AudioInputStream getAudioInputStream(AudioFormat.Encoding targetEncoding,
  375. AudioInputStream sourceStream) {
  376. FormatConversionProvider codecs[] = getFormatConversionProviders();
  377. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  378. for(int i = codecs.length-1; i >= 0; i-- ) {
  379. if( codecs[i].isConversionSupported( targetEncoding, sourceStream.getFormat() ) ) {
  380. return codecs[i].getAudioInputStream( targetEncoding, sourceStream );
  381. }
  382. }
  383. // we ran out of options, throw an exception
  384. throw new IllegalArgumentException("Unsupported conversion: " + targetEncoding + " from " + sourceStream.getFormat());
  385. }
  386. /**
  387. * Obtains the formats that have a particular encoding and that the system can
  388. * obtain from a stream of the specified format using the set of
  389. * installed format converters.
  390. * @param targetEncoding the desired encoding after conversion
  391. * @param sourceFormat the audio format before conversion
  392. * @return array of formats. If no formats of the specified
  393. * encoding are supported, an array of length 0 is returned.
  394. */
  395. public static AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) {
  396. FormatConversionProvider codecs[] = getFormatConversionProviders();
  397. Vector formats = new Vector();
  398. int size = 0;
  399. int index = 0;
  400. AudioFormat fmts[] = null;
  401. // gather from all the codecs
  402. for(int i=0; i<codecs.length; i++ ) {
  403. fmts = codecs[i].getTargetFormats(targetEncoding, sourceFormat);
  404. size += fmts.length;
  405. formats.addElement( fmts );
  406. }
  407. // now build a new array
  408. AudioFormat fmts2[] = new AudioFormat[size];
  409. for(int i=0; i<formats.size(); i++ ) {
  410. fmts = (AudioFormat [])(formats.elementAt(i));
  411. for(int j=0; j<fmts.length; j++ ) {
  412. fmts2[index++] = fmts[j];
  413. }
  414. }
  415. return fmts2;
  416. }
  417. /**
  418. * Indicates whether an audio input stream of a specified format
  419. * can be obtained from an audio input stream of another specified format.
  420. * @param targetFormat the desired audio format after conversion
  421. * @param sourceFormat the audio format before conversion
  422. * @return <code>true</code> if the conversion is supported,
  423. * otherwise <code>false</code>
  424. */
  425. public static boolean isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat) {
  426. FormatConversionProvider codecs[] = getFormatConversionProviders();
  427. for(int i=0; i<codecs.length; i++ ) {
  428. if(codecs[i].isConversionSupported(targetFormat, sourceFormat) ) {
  429. return true;
  430. }
  431. }
  432. return false;
  433. }
  434. /**
  435. * Obtains an audio input stream of the indicated format, by converting the
  436. * provided audio input stream.
  437. * @param targetFormat the desired audio format after conversion
  438. * @param sourceStream the stream to be converted
  439. * @return an audio input stream of the indicated format
  440. * @throws IllegalArgumentException if the conversion is not supported
  441. * #see #getTargetEncodings(AudioFormat)
  442. * @see #getTargetFormats(AudioFormat.Encoding, AudioFormat)
  443. * @see #isConversionSupported(AudioFormat, AudioFormat)
  444. * @see #getAudioInputStream(AudioFormat.Encoding, AudioInputStream)
  445. */
  446. public static AudioInputStream getAudioInputStream(AudioFormat targetFormat,
  447. AudioInputStream sourceStream) {
  448. if (sourceStream.getFormat().matches(targetFormat)) {
  449. return sourceStream;
  450. }
  451. FormatConversionProvider codecs[] = getFormatConversionProviders();
  452. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  453. for(int i = codecs.length-1; i >= 0; i-- ) {
  454. if(codecs[i].isConversionSupported(targetFormat,sourceStream.getFormat()) ) {
  455. return codecs[i].getAudioInputStream(targetFormat,sourceStream);
  456. }
  457. }
  458. // we ran out of options...
  459. throw new IllegalArgumentException("Unsupported conversion: " + targetFormat + " from " + sourceStream.getFormat());
  460. }
  461. /**
  462. * Obtains the audio file format of the provided input stream. The stream must
  463. * point to valid audio file data. The implementation of this method may require
  464. * multiple parsers to examine the stream to determine whether they support it.
  465. * These parsers must be able to mark the stream, read enough data to determine whether they
  466. * support the stream, and, if not, reset the stream's read pointer to its original
  467. * position. If the input stream does not support these operations, this method may fail
  468. * with an <code>IOException</code>.
  469. * @param stream the input stream from which file format information should be
  470. * extracted
  471. * @return an <code>AudioFileFormat</code> object describing the stream's audio file format
  472. * @throws UnsupportedAudioFileException if the stream does not point to valid audio
  473. * file data recognized by the system
  474. * @throws IOException if an input/output exception occurs
  475. * @see InputStream#markSupported
  476. * @see InputStream#mark
  477. */
  478. public static AudioFileFormat getAudioFileFormat(InputStream stream)
  479. throws UnsupportedAudioFileException, IOException {
  480. AudioFileReader providers[] = getAudioFileReaders();
  481. AudioFileFormat format = null;
  482. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  483. for(int i = providers.length-1; i >= 0; i-- ) {
  484. try {
  485. format = providers[i].getAudioFileFormat( stream ); // throws IOException
  486. break;
  487. } catch (UnsupportedAudioFileException e) {
  488. continue;
  489. }
  490. }
  491. if( format==null ) {
  492. throw new UnsupportedAudioFileException("file is not a supported file type");
  493. } else {
  494. return format;
  495. }
  496. }
  497. /**
  498. * Obtains the audio file format of the specified URL. The URL must
  499. * point to valid audio file data.
  500. * @param url the URL from which file format information should be
  501. * extracted
  502. * @return an <code>AudioFileFormat</code> object describing the audio file format
  503. * @throws UnsupportedAudioFileException if the URL does not point to valid audio
  504. * file data recognized by the system
  505. * @throws IOException if an input/output exception occurs
  506. */
  507. public static AudioFileFormat getAudioFileFormat(URL url)
  508. throws UnsupportedAudioFileException, IOException {
  509. AudioFileReader providers[] = getAudioFileReaders();
  510. AudioFileFormat format = null;
  511. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  512. for(int i = providers.length-1; i >= 0; i-- ) {
  513. try {
  514. format = providers[i].getAudioFileFormat( url ); // throws IOException
  515. break;
  516. } catch (UnsupportedAudioFileException e) {
  517. continue;
  518. }
  519. }
  520. if( format==null ) {
  521. throw new UnsupportedAudioFileException("file is not a supported file type");
  522. } else {
  523. return format;
  524. }
  525. }
  526. /**
  527. * Obtains the audio file format of the specified <code>File</code>. The <code>File</code> must
  528. * point to valid audio file data.
  529. * @param file the <code>File</code> from which file format information should be
  530. * extracted
  531. * @return an <code>AudioFileFormat</code> object describing the audio file format
  532. * @throws UnsupportedAudioFileException if the <code>File</code> does not point to valid audio
  533. * file data recognized by the system
  534. * @throws IOException if an I/O exception occurs
  535. */
  536. public static AudioFileFormat getAudioFileFormat(File file)
  537. throws UnsupportedAudioFileException, IOException {
  538. AudioFileReader providers[] = getAudioFileReaders();
  539. AudioFileFormat format = null;
  540. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  541. for(int i = providers.length-1; i >= 0; i-- ) {
  542. try {
  543. format = providers[i].getAudioFileFormat( file ); // throws IOException
  544. break;
  545. } catch (UnsupportedAudioFileException e) {
  546. continue;
  547. }
  548. }
  549. if( format==null ) {
  550. throw new UnsupportedAudioFileException("file is not a supported file type");
  551. } else {
  552. return format;
  553. }
  554. }
  555. /**
  556. * Obtains an audio input stream from the provided input stream. The stream must
  557. * point to valid audio file data. The implementation of this method may
  558. * require multiple parsers to
  559. * examine the stream to determine whether they support it. These parsers must
  560. * be able to mark the stream, read enough data to determine whether they
  561. * support the stream, and, if not, reset the stream's read pointer to its original
  562. * position. If the input stream does not support these operation, this method may fail
  563. * with an <code>IOException</code>.
  564. * @param stream the input stream from which the <code>AudioInputStream</code> should be
  565. * constructed
  566. * @return an <code>AudioInputStream</code> object based on the audio file data contained
  567. * in the input stream.
  568. * @throws UnsupportedAudioFileException if the stream does not point to valid audio
  569. * file data recognized by the system
  570. * @throws IOException if an I/O exception occurs
  571. * @see InputStream#markSupported
  572. * @see InputStream#mark
  573. */
  574. public static AudioInputStream getAudioInputStream(InputStream stream)
  575. throws UnsupportedAudioFileException, IOException {
  576. AudioFileReader providers[] = getAudioFileReaders();
  577. AudioInputStream audioStream = null;
  578. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  579. for(int i = providers.length-1; i >= 0; i-- ) {
  580. try {
  581. audioStream = providers[i].getAudioInputStream( stream ); // throws IOException
  582. break;
  583. } catch (UnsupportedAudioFileException e) {
  584. continue;
  585. }
  586. }
  587. if( audioStream==null ) {
  588. throw new UnsupportedAudioFileException("could not get audio input stream from input stream");
  589. } else {
  590. return audioStream;
  591. }
  592. }
  593. /**
  594. * Obtains an audio input stream from the URL provided. The URL must
  595. * point to valid audio file data.
  596. * @param url the URL for which the <code>AudioInputStream</code> should be
  597. * constructed
  598. * @return an <code>AudioInputStream</code> object based on the audio file data pointed
  599. * to by the URL
  600. * @throws UnsupportedAudioFileException if the URL does not point to valid audio
  601. * file data recognized by the system
  602. * @throws IOException if an I/O exception occurs
  603. */
  604. public static AudioInputStream getAudioInputStream(URL url)
  605. throws UnsupportedAudioFileException, IOException {
  606. AudioFileReader providers[] = getAudioFileReaders();
  607. AudioInputStream audioStream = null;
  608. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  609. for(int i = providers.length-1; i >= 0; i-- ) {
  610. try {
  611. audioStream = providers[i].getAudioInputStream( url ); // throws IOException
  612. break;
  613. } catch (UnsupportedAudioFileException e) {
  614. continue;
  615. }
  616. }
  617. if( audioStream==null ) {
  618. throw new UnsupportedAudioFileException("could not get audio input stream from input URL");
  619. } else {
  620. return audioStream;
  621. }
  622. }
  623. /**
  624. * Obtains an audio input stream from the provided <code>File</code>. The <code>File</code> must
  625. * point to valid audio file data.
  626. * @param file the <code>File</code> for which the <code>AudioInputStream</code> should be
  627. * constructed
  628. * @return an <code>AudioInputStream</code> object based on the audio file data pointed
  629. * to by the <code>File</code>
  630. * @throws UnsupportedAudioFileException if the <code>File</code> does not point to valid audio
  631. * file data recognized by the system
  632. * @throws IOException if an I/O exception occurs
  633. */
  634. public static AudioInputStream getAudioInputStream(File file)
  635. throws UnsupportedAudioFileException, IOException {
  636. AudioFileReader providers[] = getAudioFileReaders();
  637. AudioInputStream audioStream = null;
  638. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  639. for(int i = providers.length-1; i >= 0; i-- ) {
  640. try {
  641. audioStream = providers[i].getAudioInputStream( file ); // throws IOException
  642. break;
  643. } catch (UnsupportedAudioFileException e) {
  644. continue;
  645. }
  646. }
  647. if( audioStream==null ) {
  648. throw new UnsupportedAudioFileException("could not get audio input stream from input file");
  649. } else {
  650. return audioStream;
  651. }
  652. }
  653. /**
  654. * Obtains the file types for which file writing support is provided by the system.
  655. * @return array of file types. If no file types are supported,
  656. * an array of length 0 is returned.
  657. */
  658. public static AudioFileFormat.Type[] getAudioFileTypes() {
  659. //$$fb TODO: this implementation may return duplicates!
  660. AudioFileWriter providers[] = getAudioFileWriters();
  661. AudioFileFormat.Type fileTypes[][] = new AudioFileFormat.Type[ providers.length ][];
  662. AudioFileFormat.Type returnTypes[] = null;
  663. int numTypes = 0;
  664. int index = 0;
  665. // Get all file types
  666. for(int i=0; i < providers.length; i++) {
  667. fileTypes[i] = providers[i].getAudioFileTypes();
  668. numTypes += fileTypes[i].length;
  669. }
  670. // Now put them in a 1-D array
  671. returnTypes = new AudioFileFormat.Type[ numTypes ];
  672. for(int i=0; i < providers.length; i++) {
  673. for(int j=0; j < fileTypes[i].length; j++) {
  674. returnTypes[ index ] = fileTypes[i][j];
  675. index++;
  676. }
  677. }
  678. return returnTypes;
  679. }
  680. /**
  681. * Indicates whether file writing support for the specified file type is provided
  682. * by the system.
  683. * @param fileType the file type for which write capabilities are queried
  684. * @return <code>true</code> if the file type is supported,
  685. * otherwise <code>false</code>
  686. */
  687. public static boolean isFileTypeSupported(AudioFileFormat.Type fileType) {
  688. AudioFileWriter providers[] = getAudioFileWriters();
  689. boolean isSupported = false;
  690. for(int i=0; i < providers.length; i++ ) {
  691. isSupported = providers[i].isFileTypeSupported(fileType);
  692. if(isSupported==true) {
  693. return isSupported;
  694. }
  695. }
  696. return isSupported;
  697. }
  698. /**
  699. * Obtains the file types that the system can write from the
  700. * audio input stream specified.
  701. * @param stream the audio input stream for which audio file type support
  702. * is queried
  703. * @return array of file types. If no file types are supported,
  704. * an array of length 0 is returned.
  705. */
  706. public static AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) {
  707. AudioFileWriter providers[] = getAudioFileWriters();
  708. AudioFileFormat.Type fileTypes[][] = new AudioFileFormat.Type[ providers.length ][];
  709. AudioFileFormat.Type returnTypes[] = null;
  710. int numTypes = 0;
  711. int index = 0;
  712. // Get all file types
  713. for(int i=0; i < providers.length; i++) {
  714. fileTypes[i] = providers[i].getAudioFileTypes(stream);
  715. numTypes += fileTypes[i].length;
  716. }
  717. // Now put them in a 1-D array
  718. returnTypes = new AudioFileFormat.Type[ numTypes ];
  719. for(int i=0; i < providers.length; i++) {
  720. for(int j=0; j < fileTypes[i].length; j++) {
  721. returnTypes[ index ] = fileTypes[i][j];
  722. index++;
  723. }
  724. }
  725. return returnTypes;
  726. }
  727. /**
  728. * Indicates whether an audio file of the specified file type can be written
  729. * from the indicated audio input stream.
  730. * @param fileType the file type for which write capabilities are queried
  731. * @param stream the stream for which file-writing support is queried
  732. * @return <code>true</code> if the file type is supported for this audio input stream,
  733. * otherwise <code>false</code>
  734. */
  735. public static boolean isFileTypeSupported(AudioFileFormat.Type fileType,
  736. AudioInputStream stream) {
  737. AudioFileWriter providers[] = getAudioFileWriters();
  738. boolean isSupported = false;
  739. for(int i=0; i < providers.length; i++ ) {
  740. isSupported = providers[i].isFileTypeSupported(fileType, stream);
  741. if(isSupported==true) {
  742. return isSupported;
  743. }
  744. }
  745. return isSupported;
  746. }
  747. /**
  748. * Writes a stream of bytes representing an audio file of the specified file type
  749. * to the output stream provided. Some file types require that
  750. * the length be written into the file header; such files cannot be written from
  751. * start to finish unless the length is known in advance. An attempt
  752. * to write a file of such a type will fail with an IOException if the length in
  753. * the audio file type is <code>AudioSystem.NOT_SPECIFIED</code>.
  754. *
  755. * @param stream the audio input stream containing audio data to be
  756. * written to the file
  757. * @param fileType the kind of audio file to write
  758. * @param out the stream to which the file data should be written
  759. * @return the number of bytes written to the output stream
  760. * @throws IOException if an input/output exception occurs
  761. * @throws IllegalArgumentException if the file type is not supported by
  762. * the system
  763. * @see #isFileTypeSupported
  764. * @see #getAudioFileTypes
  765. */
  766. public static int write(AudioInputStream stream, AudioFileFormat.Type fileType,
  767. OutputStream out) throws IOException {
  768. AudioFileWriter providers[] = getAudioFileWriters();
  769. int bytesWritten = 0;
  770. boolean flag = false;
  771. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  772. for(int i = providers.length-1; i >= 0; i-- ) {
  773. try {
  774. bytesWritten = providers[i].write( stream, fileType, out ); // throws IOException
  775. flag = true;
  776. break;
  777. } catch (IllegalArgumentException e) {
  778. // thrown if this provider cannot write the sequence, try the next
  779. continue;
  780. }
  781. }
  782. if( flag==false ) {
  783. throw new IllegalArgumentException("could not write audio file: file type not supported: " + fileType);
  784. } else {
  785. return bytesWritten;
  786. }
  787. }
  788. /**
  789. * Writes a stream of bytes representing an audio file of the specified file type
  790. * to the external file provided.
  791. * @param stream the audio input stream containing audio data to be
  792. * written to the file
  793. * @param fileType the kind of audio file to write
  794. * @param out the external file to which the file data should be written
  795. * @return the number of bytes written to the file
  796. * @throws IOException if an I/O exception occurs
  797. * @throws IllegalArgumentException if the file type is not supported by
  798. * the system
  799. * @see #isFileTypeSupported
  800. * @see #getAudioFileTypes
  801. */
  802. public static int write(AudioInputStream stream, AudioFileFormat.Type fileType,
  803. File out) throws IOException {
  804. AudioFileWriter providers[] = getAudioFileWriters();
  805. int bytesWritten = 0;
  806. boolean flag = false;
  807. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  808. for(int i = providers.length-1; i >= 0; i-- ) {
  809. try {
  810. bytesWritten = providers[i].write( stream, fileType, out ); // throws IOException
  811. flag = true;
  812. break;
  813. } catch (IllegalArgumentException e) {
  814. // thrown if this provider cannot write the sequence, try the next
  815. continue;
  816. }
  817. }
  818. if( flag==false ) {
  819. throw new IllegalArgumentException("could not write audio file: file type not supported: " + fileType);
  820. } else {
  821. return bytesWritten;
  822. }
  823. }
  824. // METHODS FOR INTERNAL IMPLEMENTATION USE
  825. /**
  826. * Obtains the set of MixerProviders on the system.
  827. */
  828. private static Vector getMixerProviders() {
  829. Vector providers = null;
  830. try {
  831. Class.forName( "sun.misc.Service" );
  832. providers = getJDK13Services("javax.sound.sampled.spi.MixerProvider");
  833. } catch (Exception e) {
  834. if (DEBUG) e.printStackTrace();
  835. // we're not running with 1.3's SPI mechanism
  836. providers = getDefaultServices("javax.sound.sampled.spi.MixerProvider");
  837. }
  838. return providers;
  839. }
  840. /**
  841. * Obtains the set of format converters (codecs, transcoders, etc.)
  842. * that are currently installed on the system.
  843. * @return an array of
  844. * {@link javax.sound.sampled.spi.FormatConversionProvider
  845. * FormatConversionProvider}
  846. * objects representing the available format converters. If no format
  847. * converters readers are available on the system, an array of length 0 is
  848. * returned.
  849. */
  850. private static FormatConversionProvider[] getFormatConversionProviders() {
  851. Vector v = new Vector();
  852. FormatConversionProvider varray[];
  853. try {
  854. Class.forName( "sun.misc.Service" );
  855. v = getJDK13Services("javax.sound.sampled.spi.FormatConversionProvider");
  856. } catch (Exception e) {
  857. if (DEBUG) e.printStackTrace();
  858. // we're not running with 1.3's SPI mechanism
  859. v = getDefaultServices("javax.sound.sampled.spi.FormatConversionProvider");
  860. }
  861. varray = new FormatConversionProvider[ v.size() ];
  862. for( int i=0; i < varray.length; i++ ) {
  863. varray[i] = (FormatConversionProvider)(v.elementAt(i));
  864. }
  865. return varray;
  866. }
  867. /**
  868. * Obtains the set of audio file readers that are currently installed on the system.
  869. * @return an array of
  870. * {@link javax.sound.sampled.spi.AudioFileReader
  871. * AudioFileReader}
  872. * objects representing the installed audio file readers. If no audio file
  873. * readers are available on the system, an array of length 0 is returned.
  874. */
  875. private static AudioFileReader[] getAudioFileReaders() {
  876. Vector v = new Vector();
  877. AudioFileReader varray[];
  878. try {
  879. Class.forName( "sun.misc.Service" );
  880. v = getJDK13Services("javax.sound.sampled.spi.AudioFileReader");
  881. } catch (Exception e) {
  882. if (DEBUG) e.printStackTrace();
  883. // we're not running with 1.3's SPI mechanism
  884. v = getDefaultServices("javax.sound.sampled.spi.AudioFileReader");
  885. }
  886. varray = new AudioFileReader[ v.size() ];
  887. for( int i=0; i < varray.length; i++ ) {
  888. varray[i] = (AudioFileReader)(v.elementAt(i));
  889. }
  890. return varray;
  891. }
  892. /**
  893. * Obtains the set of audio file writers that are currently installed on the system.
  894. * @return an array of
  895. * {@link javax.sound.samples.spi.AudioFileWriter AudioFileWriter}
  896. * objects representing the available audio file writers. If no audio file
  897. * writers are available on the system, an array of length 0 is returned.
  898. */
  899. private static AudioFileWriter[] getAudioFileWriters() {
  900. Vector v = new Vector();
  901. AudioFileWriter varray[];
  902. try {
  903. Class.forName( "sun.misc.Service" );
  904. v = getJDK13Services("javax.sound.sampled.spi.AudioFileWriter");
  905. } catch (Exception e) {
  906. if (DEBUG) e.printStackTrace();
  907. // we're not running with 1.3's SPI mechanism
  908. v = getDefaultServices("javax.sound.sampled.spi.AudioFileWriter");
  909. }
  910. varray = new AudioFileWriter[ v.size() ];
  911. for( int i=0; i < varray.length; i++ ) {
  912. varray[i] = (AudioFileWriter)(v.elementAt(i));
  913. }
  914. return varray;
  915. }
  916. /**
  917. * Obtains the set of services currently installed on the system
  918. * using sun.misc.Service, the SPI mechanism in 1.3.
  919. * @return a Vector of instances of providers for the requested service.
  920. * If no providers are available, a vector of length 0 will be returned.
  921. */
  922. private static Vector getJDK13Services( String serviceName ) {
  923. Vector v = null;
  924. try {
  925. Class jdk13Services =
  926. Class.forName( jdk13ServicesClassName );
  927. Method m = jdk13Services.getMethod(
  928. servicesMethodName,
  929. servicesParamTypes);
  930. Object[] arguments = new Object[] { serviceName };
  931. v = (Vector) m.invoke(jdk13Services,arguments);
  932. } catch(InvocationTargetException e1) {
  933. if (DEBUG) e1.printStackTrace();
  934. v = new Vector();
  935. } catch(ClassNotFoundException e2) {
  936. if (DEBUG) e2.printStackTrace();
  937. v = new Vector();
  938. } catch(IllegalAccessException e3) {
  939. if (DEBUG) e3.printStackTrace();
  940. v = new Vector();
  941. } catch(NoSuchMethodException e4) {
  942. if (DEBUG) e4.printStackTrace();
  943. v = new Vector();
  944. }
  945. return v;
  946. }
  947. /**
  948. * Obtains the default set of services currently installed on the system.
  949. * This method is only invoked if sun.misc.Service is not available.
  950. * @return a Vector of instances of providers for the requested service.
  951. * If no providers are available, a vector of length 0 will be returned.
  952. */
  953. private static Vector getDefaultServices( String serviceName ) {
  954. Vector v = null;
  955. try {
  956. Class defaultServices =
  957. Class.forName( defaultServicesClassName );
  958. Method m = defaultServices.getMethod(
  959. servicesMethodName,
  960. servicesParamTypes);
  961. Object[] arguments = new Object[] { serviceName };
  962. v = (Vector) m.invoke(defaultServices,arguments);
  963. } catch(InvocationTargetException e1) {
  964. if (DEBUG) e1.printStackTrace();
  965. v = new Vector();
  966. } catch(ClassNotFoundException e2) {
  967. if (DEBUG) e2.printStackTrace();
  968. v = new Vector();
  969. } catch(IllegalAccessException e3) {
  970. if (DEBUG) e3.printStackTrace();
  971. v = new Vector();
  972. } catch(NoSuchMethodException e4) {
  973. if (DEBUG) e4.printStackTrace();
  974. v = new Vector();
  975. }
  976. return v;
  977. }
  978. }