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