1. /*
  2. * @(#)MidiSystem.java 1.49 03/01/27
  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.midi;
  8. import java.io.FileInputStream;
  9. import java.io.File;
  10. import java.io.InputStream;
  11. import java.io.OutputStream;
  12. import java.io.IOException;
  13. import java.util.Vector;
  14. import java.lang.reflect.Method;
  15. import java.lang.reflect.InvocationTargetException;
  16. import java.net.URL;
  17. import javax.sound.midi.spi.MidiFileWriter;
  18. import javax.sound.midi.spi.MidiFileReader;
  19. import javax.sound.midi.spi.SoundbankReader;
  20. import javax.sound.midi.spi.MidiDeviceProvider;
  21. /**
  22. * The <code>MidiSystem</code> class provides access to the installed MIDI
  23. * system resources, including devices such as synthesizers, sequencers, and
  24. * MIDI input and output ports. A typical simple MIDI application might
  25. * begin by invoking one or more <code>MidiSystem</code> methods to learn
  26. * what devices are installed and to obtain the ones needed in that
  27. * application.
  28. * <p>
  29. * The class also has methods for reading files, streams, and URLs that
  30. * contain standard MIDI file data or soundbanks. You can query the
  31. * <code>MidiSystem</code> for the format of a specified MIDI file.
  32. * <p>
  33. * You cannot instantiate a <code>MidiSystem</code> all the methods are
  34. * static.
  35. *
  36. * @version 1.49, 03/01/27
  37. * @author Kara Kytle
  38. */
  39. public class MidiSystem {
  40. /**
  41. * Private strings for default services class and method
  42. */
  43. private static final String defaultServicesClassName =
  44. "com.sun.media.sound.DefaultServices";
  45. private static final String jdk13ServicesClassName =
  46. "com.sun.media.sound.JDK13Services";
  47. private static final String servicesMethodName =
  48. "getProviders";
  49. private static final Class[] servicesParamTypes =
  50. new Class[] { String.class };
  51. /**
  52. * Private no-args constructor for ensuring against instantiation.
  53. */
  54. private MidiSystem() {
  55. }
  56. /**
  57. * Obtains an array of information objects representing
  58. * the set of all MIDI devices available on the system.
  59. * A returned information object can then be used to obtain the
  60. * corresponding device object, by invoking
  61. * {@link #getMidiDevice(MidiDevice.Info) getMidiDevice}.
  62. *
  63. * @return an array of <code>MidiDevice.Info</code> objects, one
  64. * for each installed MIDI device. If no such devices are installed,
  65. * an array of length 0 is returned.
  66. */
  67. public static MidiDevice.Info[] getMidiDeviceInfo() {
  68. return getGenericDeviceInfo( MidiDevice.class );
  69. }
  70. /**
  71. * Obtains the requested MIDI device.
  72. *
  73. * @param info a device information object representing the desired device.
  74. * @return the requested device
  75. * @throws MidiUnavailableException if the requested device is not available
  76. * due to resource restrictions
  77. * @throws IllegalArgumentException if the info object does not represent
  78. * a MIDI device installed on the system
  79. * @see #getMidiDeviceInfo
  80. */
  81. public static MidiDevice getMidiDevice(MidiDevice.Info info) throws MidiUnavailableException {
  82. return (MidiDevice)getGenericDevice( null, info );
  83. }
  84. /*
  85. * re-deleted
  86. */
  87. /*
  88. * un-deleted, AbstractPlayer.addPlatformSynth() conflict
  89. */
  90. /**
  91. * Obtains an array of information objects representing
  92. * the set of MIDI devices available on the system that support
  93. * one or more receivers.
  94. * A returned information object can then be used to obtain a
  95. * receiver from the corresponding device object, by invoking
  96. * {@link #getReceiver(MidiDevice.Info) getReceiver}.
  97. *
  98. * @return an array of <code>MidiDevice.Info</code> objects, one
  99. * for each installed device that supports one or more
  100. * {@link Receiver Receivers}.
  101. * If no such devices are installed, an array of length
  102. * 0 is returned.
  103. */
  104. /*
  105. public static MidiDevice.Info[] getReceiverInfo() {
  106. return getGenericDeviceInfo( Receiver.class );
  107. }
  108. */
  109. /*
  110. * re-changed
  111. */
  112. /*
  113. * un-changed, AbstractPlayer.addPlatformSynth() conflict
  114. */
  115. /**
  116. * Obtains a MIDI receiver from an external MIDI port
  117. * or other default source.
  118. * @return the default MIDI receiver
  119. * @throws MidiUnavailableException if the default receiver is not
  120. * available due to resource restrictions
  121. */
  122. public static Receiver getReceiver() throws MidiUnavailableException {
  123. return ( ((MidiDevice)getGenericDevice(Receiver.class, null)).getReceiver() );
  124. }
  125. /*
  126. public static Receiver getReceiver(MidiDevice.Info info) throws MidiUnavailableException {
  127. return ( ((MidiDevice)getGenericDevice(Receiver.class, info)).getReceiver() );
  128. }
  129. */
  130. /*
  131. * deleted
  132. */
  133. /**
  134. * Obtains an array of information objects representing
  135. * the set of MIDI devices available on the system that support
  136. * one or more transmitters.
  137. * A returned information object can then be used to obtain a
  138. * transmitter from the corresponding device object, by invoking
  139. * {@link #getTransmitter(MidiDevice.Info) getTransmitter}.
  140. *
  141. * @return an array of <code>MidiDevice.Info</code> objects, one
  142. * for each installed device that supports one or more
  143. * {@link Transmitter Transmitters}.
  144. * If no such devices are installed, an array of length
  145. * 0 is returned.
  146. */
  147. /* public static MidiDevice.Info[] getTransmitterInfo() {
  148. return getGenericDeviceInfo( Transmitter.class );
  149. }
  150. */
  151. /**
  152. * Obtains a MIDI transmitter from an external MIDI port
  153. * or other default source.
  154. * @return the default MIDI transmitter
  155. * @throws MidiUnavailableException if the default transmitter is not
  156. * available due to resource restrictions
  157. */
  158. public static Transmitter getTransmitter() throws MidiUnavailableException {
  159. return ( ((MidiDevice)getGenericDevice(Transmitter.class, null)).getTransmitter() );
  160. }
  161. /* public static Transmitter getTransmitter(MidiDevice.Info info) throws MidiUnavailableException {
  162. return ( ((MidiDevice)getGenericDevice(Transmitter.class, info)).getTransmitter() );
  163. }
  164. */
  165. /*
  166. * deleted
  167. */
  168. /**
  169. * Obtains an array of information objects representing
  170. * the set of synthesizers available on the system. A
  171. * returned information object can then be used to obtain the corresponding
  172. * synthesizer, by invoking
  173. * {@link #getSynthesizer(MidiDevice.Info) getSynthesizer}.
  174. *
  175. * @return an array of <code>MidiDevice.Info</code> objects, one
  176. * for each installed device that is a {@link Synthesizer}.
  177. * If no synthesizers are installed, an array of length
  178. * 0 is returned.
  179. */
  180. /* public static MidiDevice.Info[] getSynthesizerInfo() {
  181. return getGenericDeviceInfo( Synthesizer.class );
  182. }
  183. */
  184. /**
  185. * Obtains the default synthesizer.
  186. * @return the default synthesizer
  187. * @throws MidiUnavailableException if the synthesizer is not
  188. * available due to resource restrictions
  189. */
  190. public static Synthesizer getSynthesizer() throws MidiUnavailableException {
  191. return (Synthesizer) getGenericDevice( Synthesizer.class, null );
  192. }
  193. /* public static Synthesizer getSynthesizer(MidiDevice.Info info) {
  194. return (Synthesizer) getGenericDevice( Synthesizer.class, info );
  195. }
  196. */
  197. /*
  198. * deleted
  199. */
  200. /**
  201. * Obtains an array of information objects representing
  202. * the set of sequencers available on the system. A
  203. * returned information object can then be used to obtain the corresponding
  204. * sequencer, by invoking
  205. * {@link #getSequencer(MidiDevice.Info) getSequencer}.
  206. *
  207. * @return an array of <code>MidiDevice.Info</code> objects, one
  208. * for each installed device that is a {@link Sequencer}.
  209. * If no sequencers are installed, an array of length
  210. * 0 is returned.
  211. */
  212. /* public static MidiDevice.Info[] getSequencerInfo() {
  213. return getGenericDeviceInfo( Sequencer.class );
  214. }
  215. */
  216. /**
  217. * Obtains the default sequencer.
  218. * @return the default sequencer
  219. * @throws MidiUnavailableException if the sequencer is not
  220. * available due to resource restrictions
  221. */
  222. public static Sequencer getSequencer() throws MidiUnavailableException {
  223. return (Sequencer) getGenericDevice( Sequencer.class, null );
  224. }
  225. /* public static Sequencer getSequencer(MidiDevice.Info info) {
  226. return (Sequencer) getGenericDevice( Sequencer.class, info );
  227. }
  228. */
  229. /**
  230. * Constructs a MIDI sound bank by reading it from the specified stream.
  231. * The stream must point to
  232. * a valid MIDI soundbank file. In general, MIDI soundbank providers may
  233. * need to read some data from the stream before determining whether they
  234. * support it. These parsers must
  235. * be able to mark the stream, read enough data to determine whether they
  236. * support the stream, and, if not, reset the stream's read pointer to
  237. * its original position. If the input stream does not support this,
  238. * this method may fail with an IOException.
  239. * @param stream the source of the sound bank data.
  240. * @return the sound bank
  241. * @throws InvalidMidiDataException if the stream does not point to
  242. * valid MIDI soundbank data recognized by the system
  243. * @throws IOException if an I/O error occurred when loading the soundbank
  244. * @see InputStream#markSupported
  245. * @see InputStream#mark
  246. */
  247. public static Soundbank getSoundbank(InputStream stream)
  248. throws InvalidMidiDataException, IOException {
  249. SoundbankReader sp = null;
  250. Soundbank s = null;
  251. Vector providers = getSoundbankReaders();
  252. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  253. for(int i = providers.size()-1; i >= 0; i-- ) {
  254. sp = (SoundbankReader)providers.elementAt(i);
  255. s = sp.getSoundbank(stream);
  256. if( s!= null) {
  257. return s;
  258. }
  259. }
  260. throw new InvalidMidiDataException("cannot get soundbank from stream");
  261. }
  262. /**
  263. * Constructs a <code>Soundbank</code> by reading it from the specified URL.
  264. * The URL must point to a valid MIDI soundbank file.
  265. *
  266. * @param url the source of the sound bank data
  267. * @return the sound bank
  268. * @throws InvalidMidiDataException if the URL does not point to valid MIDI
  269. * soundbank data recognized by the system
  270. * @throws IOException if an I/O error occurred when loading the soundbank
  271. */
  272. public static Soundbank getSoundbank(URL url)
  273. throws InvalidMidiDataException, IOException {
  274. SoundbankReader sp = null;
  275. Soundbank s = null;
  276. Vector providers = getSoundbankReaders();
  277. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  278. for(int i = providers.size()-1; i >= 0; i-- ) {
  279. sp = (SoundbankReader)providers.elementAt(i);
  280. s = sp.getSoundbank(url);
  281. if( s!= null) {
  282. return s;
  283. }
  284. }
  285. throw new InvalidMidiDataException("cannot get soundbank from stream");
  286. }
  287. /**
  288. * Constructs a <code>Soundbank</code> by reading it from the specified
  289. * <code>File</code>.
  290. * The <code>File</code> must point to a valid MIDI soundbank file.
  291. *
  292. * @param file the source of the sound bank data
  293. * @return the sound bank
  294. * @throws InvalidMidiDataException if the <code>File</code> does not
  295. * point to valid MIDI soundbank data recognized by the system
  296. * @throws IOException if an I/O error occurred when loading the soundbank
  297. */
  298. public static Soundbank getSoundbank(File file)
  299. throws InvalidMidiDataException, IOException {
  300. FileInputStream fis = new FileInputStream( file );
  301. return getSoundbank( fis );
  302. }
  303. /**
  304. * Obtains the MIDI file format of the data in the specified input stream.
  305. * The stream must point to valid MIDI file data for a file type recognized
  306. * by the system.
  307. * <p>
  308. * This method and/or the code it invokes may need to read some data from
  309. * the stream to determine whether its data format is supported. The
  310. * implementation may therefore
  311. * need to mark the stream, read enough data to determine whether it is in
  312. * a supported format, and reset the stream's read pointer to its original
  313. * position. If the input stream does not permit this set of operations,
  314. * this method may fail with an <code>IOException</code>.
  315. * <p>
  316. * This operation can only succeed for files of a type which can be parsed
  317. * by an installed file reader. It may fail with an InvalidMidiDataException
  318. * even for valid files if no compatible file reader is installed. It
  319. * will also fail with an InvalidMidiDataException if a compatible file reader
  320. * is installed, but encounters errors while determining the file format.
  321. *
  322. * @param stream the input stream from which file format information
  323. * should be extracted
  324. * @return an <code>MidiFileFormat</code> object describing the MIDI file
  325. * format
  326. * @throws InvalidMidiDataException if the stream does not point to valid
  327. * MIDI file data recognized by the system
  328. * @throws IOException if an I/O exception occurs while accessing the
  329. * stream
  330. * @see #getMidiFileFormat(URL)
  331. * @see #getMidiFileFormat(File)
  332. * @see InputStream#markSupported
  333. * @see InputStream#mark
  334. */
  335. public static MidiFileFormat getMidiFileFormat(InputStream stream)
  336. throws InvalidMidiDataException, IOException {
  337. MidiFileReader providers[] = getMidiFileReaders();
  338. MidiFileFormat format = null;
  339. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  340. for(int i = providers.length-1; i >= 0; i-- ) {
  341. try {
  342. format = providers[i].getMidiFileFormat( stream ); // throws IOException
  343. break;
  344. } catch (InvalidMidiDataException e) {
  345. continue;
  346. }
  347. }
  348. if( format==null ) {
  349. throw new InvalidMidiDataException("input stream is not a supported file type");
  350. } else {
  351. return format;
  352. }
  353. }
  354. /**
  355. * Obtains the MIDI file format of the data in the specified URL. The URL
  356. * must point to valid MIDI file data for a file type recognized
  357. * by the system.
  358. * <p>
  359. * This operation can only succeed for files of a type which can be parsed
  360. * by an installed file reader. It may fail with an InvalidMidiDataException
  361. * even for valid files if no compatible file reader is installed. It
  362. * will also fail with an InvalidMidiDataException if a compatible file reader
  363. * is installed, but encounters errors while determining the file format.
  364. *
  365. * @param url the URL from which file format information should be
  366. * extracted
  367. * @return a <code>MidiFileFormat</code> object describing the MIDI file
  368. * format
  369. * @throws InvalidMidiDataException if the URL does not point to valid MIDI
  370. * file data recognized by the system
  371. * @throws IOException if an I/O exception occurs while accessing the URL
  372. *
  373. * @see #getMidiFileFormat(InputStream)
  374. * @see #getMidiFileFormat(File)
  375. */
  376. public static MidiFileFormat getMidiFileFormat(URL url)
  377. throws InvalidMidiDataException, IOException {
  378. MidiFileReader providers[] = getMidiFileReaders();
  379. MidiFileFormat format = null;
  380. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  381. for(int i = providers.length-1; i >= 0; i-- ) {
  382. try {
  383. format = providers[i].getMidiFileFormat( url ); // throws IOException
  384. break;
  385. } catch (InvalidMidiDataException e) {
  386. continue;
  387. }
  388. }
  389. if( format==null ) {
  390. throw new InvalidMidiDataException("url is not a supported file type");
  391. } else {
  392. return format;
  393. }
  394. }
  395. /**
  396. * Obtains the MIDI file format of the specified <code>File</code>. The
  397. * <code>File</code> must point to valid MIDI file data for a file type
  398. * recognized by the system.
  399. * <p>
  400. * This operation can only succeed for files of a type which can be parsed
  401. * by an installed file reader. It may fail with an InvalidMidiDataException
  402. * even for valid files if no compatible file reader is installed. It
  403. * will also fail with an InvalidMidiDataException if a compatible file reader
  404. * is installed, but encounters errors while determining the file format.
  405. *
  406. * @param file the <code>File</code> from which file format information
  407. * should be extracted
  408. * @return a <code>MidiFileFormat</code> object describing the MIDI file
  409. * format
  410. * @throws InvalidMidiDataException if the <code>File</code> does not point
  411. * to valid MIDI file data recognized by the system
  412. * @throws IOException if an I/O exception occurs while accessing the file
  413. *
  414. * @see #getMidiFileFormat(InputStream)
  415. * @see #getMidiFileFormat(URL)
  416. */
  417. public static MidiFileFormat getMidiFileFormat(File file)
  418. throws InvalidMidiDataException, IOException {
  419. MidiFileReader providers[] = getMidiFileReaders();
  420. MidiFileFormat format = null;
  421. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  422. for(int i = providers.length-1; i >= 0; i-- ) {
  423. try {
  424. format = providers[i].getMidiFileFormat( file ); // throws IOException
  425. break;
  426. } catch (InvalidMidiDataException e) {
  427. continue;
  428. }
  429. }
  430. if( format==null ) {
  431. throw new InvalidMidiDataException("file is not a supported file type");
  432. } else {
  433. return format;
  434. }
  435. }
  436. /**
  437. * Obtains a MIDI sequence from the specified input stream. The stream must
  438. * point to valid MIDI file data for a file type recognized
  439. * by the system.
  440. * <p>
  441. * This method and/or the code it invokes may need to read some data
  442. * from the stream to determine whether
  443. * its data format is supported. The implementation may therefore
  444. * need to mark the stream, read enough data to determine whether it is in
  445. * a supported format, and reset the stream's read pointer to its original
  446. * position. If the input stream does not permit this set of operations,
  447. * this method may fail with an <code>IOException</code>.
  448. * <p>
  449. * This operation can only succeed for files of a type which can be parsed
  450. * by an installed file reader. It may fail with an InvalidMidiDataException
  451. * even for valid files if no compatible file reader is installed. It
  452. * will also fail with an InvalidMidiDataException if a compatible file reader
  453. * is installed, but encounters errors while constructing the <code>Sequence</code>
  454. * object from the file data.
  455. *
  456. * @param stream the input stream from which the <code>Sequence</code>
  457. * should be constructed
  458. * @return a <code>Sequence</code> object based on the MIDI file data
  459. * contained in the input stream
  460. * @throws InvalidMidiDataException if the stream does not point to
  461. * valid MIDI file data recognized by the system
  462. * @throws IOException if an I/O exception occurs while accessing the
  463. * stream
  464. * @see InputStream#markSupported
  465. * @see InputStream#mark
  466. */
  467. public static Sequence getSequence(InputStream stream)
  468. throws InvalidMidiDataException, IOException {
  469. MidiFileReader providers[] = getMidiFileReaders();
  470. Sequence sequence = null;
  471. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  472. for(int i = providers.length-1; i >= 0; i-- ) {
  473. try {
  474. sequence = providers[i].getSequence( stream ); // throws IOException
  475. break;
  476. } catch (InvalidMidiDataException e) {
  477. continue;
  478. }
  479. }
  480. if( sequence==null ) {
  481. throw new InvalidMidiDataException("could not get sequence from input stream");
  482. } else {
  483. return sequence;
  484. }
  485. }
  486. /**
  487. * Obtains a MIDI sequence from the specified URL. The URL must
  488. * point to valid MIDI file data for a file type recognized
  489. * by the system.
  490. * <p>
  491. * This operation can only succeed for files of a type which can be parsed
  492. * by an installed file reader. It may fail with an InvalidMidiDataException
  493. * even for valid files if no compatible file reader is installed. It
  494. * will also fail with an InvalidMidiDataException if a compatible file reader
  495. * is installed, but encounters errors while constructing the <code>Sequence</code>
  496. * object from the file data.
  497. *
  498. * @param url the URL from which the <code>Sequence</code> should be
  499. * constructed
  500. * @return a <code>Sequence</code> object based on the MIDI file data
  501. * pointed to by the URL
  502. * @throws InvalidMidiDataException if the URL does not point to valid MIDI
  503. * file data recognized by the system
  504. * @throws IOException if an I/O exception occurs while accessing the URL
  505. */
  506. public static Sequence getSequence(URL url)
  507. throws InvalidMidiDataException, IOException {
  508. MidiFileReader providers[] = getMidiFileReaders();
  509. Sequence sequence = null;
  510. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  511. for(int i = providers.length-1; i >= 0; i-- ) {
  512. try {
  513. sequence = providers[i].getSequence( url ); // throws IOException
  514. break;
  515. } catch (InvalidMidiDataException e) {
  516. continue;
  517. }
  518. }
  519. if( sequence==null ) {
  520. throw new InvalidMidiDataException("could not get sequence from URL");
  521. } else {
  522. return sequence;
  523. }
  524. }
  525. /**
  526. * Obtains a MIDI sequence from the specified <code>File</code>.
  527. * The <code>File</code> must point to valid MIDI file data
  528. * for a file type recognized by the system.
  529. * <p>
  530. * This operation can only succeed for files of a type which can be parsed
  531. * by an installed file reader. It may fail with an InvalidMidiDataException
  532. * even for valid files if no compatible file reader is installed. It
  533. * will also fail with an InvalidMidiDataException if a compatible file reader
  534. * is installed, but encounters errors while constructing the <code>Sequence</code>
  535. * object from the file data.
  536. *
  537. * @param file the <code>File</code> from which the <code>Sequence</code>
  538. * should be constructed
  539. * @return a <code>Sequence</code> object based on the MIDI file data
  540. * pointed to by the File
  541. * @throws InvalidMidiDataException if the File does not point to valid MIDI
  542. * file data recognized by the system
  543. * @throws IOException if an I/O exception occurs
  544. */
  545. public static Sequence getSequence(File file)
  546. throws InvalidMidiDataException, IOException {
  547. MidiFileReader providers[] = getMidiFileReaders();
  548. Sequence sequence = null;
  549. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  550. for(int i = providers.length-1; i >= 0; i-- ) {
  551. try {
  552. sequence = providers[i].getSequence( file ); // throws IOException
  553. break;
  554. } catch (InvalidMidiDataException e) {
  555. continue;
  556. }
  557. }
  558. if( sequence==null ) {
  559. throw new InvalidMidiDataException("could not get sequence from file");
  560. } else {
  561. return sequence;
  562. }
  563. }
  564. /**
  565. * Obtains the set of MIDI file types for which file writing support is
  566. * provided by the system.
  567. * @return array of file types. If no file types are supported,
  568. * an array of length 0 is returned.
  569. */
  570. public static int[] getMidiFileTypes() {
  571. MidiFileWriter providers[] = getMidiFileWriters();
  572. Vector allTypes = new Vector();
  573. int size = 0;
  574. int index = 0;
  575. int types[] = null;
  576. // gather from all the providers
  577. for(int i=0; i<providers.length; i++ ) {
  578. types = providers[i].getMidiFileTypes();
  579. size += types.length;
  580. allTypes.addElement( types );
  581. }
  582. // now build a new array
  583. int types2[] = new int[size];
  584. for(int i=0; i<allTypes.size(); i++ ) {
  585. types = (int [])(allTypes.elementAt(i));
  586. for(int j=0; j<types.length; j++ ) {
  587. types2[index++] = types[j];
  588. }
  589. }
  590. return types2;
  591. }
  592. /**
  593. * Indicates whether file writing support for the specified MIDI file type
  594. * is provided by the system.
  595. * @param fileType the file type for which write capabilities are queried
  596. * @return <code>true</code> if the file type is supported,
  597. * otherwise <code>false</code>
  598. */
  599. public static boolean isFileTypeSupported(int fileType) {
  600. MidiFileWriter providers[] = getMidiFileWriters();
  601. for(int i=0; i<providers.length; i++) {
  602. if( providers[i].isFileTypeSupported(fileType) == true ) {
  603. return true;
  604. }
  605. }
  606. return false;
  607. }
  608. /**
  609. * Obtains the set of MIDI file types that the system can write from the
  610. * sequence specified.
  611. * @param sequence the sequence for which MIDI file type support
  612. * is queried
  613. * @return the set of supported file types. If no file types are supported,
  614. * returns an array of length 0.
  615. */
  616. public static int[] getMidiFileTypes(Sequence sequence) {
  617. MidiFileWriter providers[] = getMidiFileWriters();
  618. int types[][] = new int[ providers.length ][];
  619. int returnTypes[] = null;
  620. int numTypes = 0;
  621. int index = 0;
  622. // Get all supported file types
  623. for(int i=0; i < providers.length; i++) {
  624. types[i] = providers[i].getMidiFileTypes(sequence);
  625. numTypes += types[i].length;
  626. }
  627. // Now put them in a 1-D array
  628. returnTypes = new int[ numTypes ];
  629. for(int i=0; i < providers.length; i++) {
  630. for(int j=0; j < types[i].length; j++) {
  631. returnTypes[ index ] = types[i][j];
  632. index++;
  633. }
  634. }
  635. return returnTypes;
  636. }
  637. /**
  638. * Indicates whether a MIDI file of the file type specified can be written
  639. * from the sequence indicated.
  640. * @param fileType the file type for which write capabilities
  641. * are queried
  642. * @param sequence the sequence for which file writing support is queried
  643. * @return <code>true</code> if the file type is supported for this
  644. * sequence, otherwise <code>false</code>
  645. */
  646. public static boolean isFileTypeSupported(int fileType, Sequence sequence) {
  647. MidiFileWriter providers[] = getMidiFileWriters();
  648. for(int i=0; i<providers.length; i++) {
  649. if( providers[i].isFileTypeSupported(fileType,sequence) == true ) {
  650. return true;
  651. }
  652. }
  653. return false;
  654. }
  655. /**
  656. * Writes a stream of bytes representing a file of the MIDI file type
  657. * indicated to the output stream provided.
  658. * @param in sequence containing MIDI data to be written to the file
  659. * @param fileType the file type of the file to be written to the output stream
  660. * @param out stream to which the file data should be written
  661. * @return the number of bytes written to the output stream
  662. * @throws IOException if an I/O exception occurs
  663. * @throws IllegalArgumentException if the file format is not supported by
  664. * the system
  665. * @see #isFileTypeSupported(int, Sequence)
  666. * @see #getMidiFileTypes(Sequence)
  667. */
  668. public static int write(Sequence in, int fileType, OutputStream out) throws IOException {
  669. MidiFileWriter providers[] = getMidiFileWriters();
  670. //$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences
  671. int bytesWritten = -2;
  672. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  673. for(int i = providers.length-1; i >= 0; i-- ) {
  674. if( providers[i].isFileTypeSupported( fileType, in ) == true ) {
  675. bytesWritten = providers[i].write(in, fileType, out);
  676. break;
  677. }
  678. }
  679. if (bytesWritten == -2) {
  680. throw new IllegalArgumentException("MIDI file type is not supported");
  681. }
  682. return bytesWritten;
  683. }
  684. /**
  685. * Writes a stream of bytes representing a file of the MIDI file type
  686. * indicated to the external file provided.
  687. * @param in sequence containing MIDI data to be written to the file
  688. * @param type the file type of the file to be written to the output stream
  689. * @param out external file to which the file data should be written
  690. * @return the number of bytes written to the file
  691. * @throws IOException if an I/O exception occurs
  692. * @throws IllegalArgumentException if the file type is not supported by
  693. * the system
  694. * @see #isFileTypeSupported(int, Sequence)
  695. * @see #getMidiFileTypes(Sequence)
  696. */
  697. public static int write(Sequence in, int type, File out) throws IOException {
  698. MidiFileWriter providers[] = getMidiFileWriters();
  699. //$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences
  700. int bytesWritten = -2;
  701. //$$fb 2001-08-03: reverse this loop to give external providers more priority (Bug #4487550)
  702. for(int i = providers.length-1; i >= 0; i-- ) {
  703. if( providers[i].isFileTypeSupported( type, in ) == true ) {
  704. bytesWritten = providers[i].write(in, type, out);
  705. break;
  706. }
  707. }
  708. if (bytesWritten == -2) {
  709. throw new IllegalArgumentException("MIDI file type is not supported");
  710. }
  711. return bytesWritten;
  712. }
  713. // HELPER METHODS
  714. private static synchronized boolean isA( Class testClass, Class targetClass ) {
  715. return (targetClass.isAssignableFrom(testClass)) ? true : false;
  716. }
  717. private static Vector getMidiDeviceProviders() {
  718. Vector providers = null;
  719. try {
  720. Class.forName( "sun.misc.Service" );
  721. providers = getJDK13Services("javax.sound.midi.spi.MidiDeviceProvider");
  722. } catch (Exception e) {
  723. // we're not running with 1.3's SPI mechanism
  724. providers = getDefaultServices("javax.sound.midi.spi.MidiDeviceProvider");
  725. }
  726. return providers;
  727. }
  728. private static Vector getSoundbankReaders() {
  729. Vector providers = null;
  730. try {
  731. Class.forName( "sun.misc.Service" );
  732. providers = getJDK13Services("javax.sound.midi.spi.SoundbankReader");
  733. } catch (Exception e) {
  734. // we're not running with 1.3's SPI mechanism
  735. providers = getDefaultServices("javax.sound.midi.spi.SoundbankReader");
  736. }
  737. return providers;
  738. }
  739. private static MidiFileWriter[] getMidiFileWriters() {
  740. Vector v = new Vector();
  741. MidiFileWriter varray[];
  742. try {
  743. Class.forName( "sun.misc.Service" );
  744. v = getJDK13Services("javax.sound.midi.spi.MidiFileWriter");
  745. } catch (Exception e) {
  746. // we're not running with 1.3's SPI mechanism
  747. v = getDefaultServices("javax.sound.midi.spi.MidiFileWriter");
  748. }
  749. varray = new MidiFileWriter[ v.size() ];
  750. for( int i=0; i < varray.length; i++ ) {
  751. varray[i] = (MidiFileWriter)(v.elementAt(i));
  752. }
  753. return varray;
  754. }
  755. private static MidiFileReader[] getMidiFileReaders() {
  756. Vector v = new Vector();
  757. MidiFileReader varray[];
  758. try {
  759. Class.forName( "sun.misc.Service" );
  760. v = getJDK13Services("javax.sound.midi.spi.MidiFileReader");
  761. } catch (Exception e) {
  762. // we're not running with 1.3's SPI mechanism
  763. v = getDefaultServices("javax.sound.midi.spi.MidiFileReader");
  764. }
  765. varray = new MidiFileReader[ v.size() ];
  766. for( int i=0; i < varray.length; i++ ) {
  767. varray[i] = (MidiFileReader)(v.elementAt(i));
  768. }
  769. return varray;
  770. }
  771. private static MidiDevice.Info[] getGenericDeviceInfo( Class deviceClass ) {
  772. int i;
  773. int j;
  774. Vector v = new Vector();
  775. MidiDevice.Info[] tmpinfo;
  776. MidiDevice.Info[] varray;
  777. Vector v1;
  778. v1 = getMidiDeviceProviders();
  779. for(i = 0; i < v1.size(); i++) {
  780. tmpinfo = ((MidiDeviceProvider) v1.elementAt(i)).getDeviceInfo();
  781. for (j = 0; j < tmpinfo.length; j++) {
  782. // $$kk: 10.12.99: ugh! i have wrecked this!!
  783. //if( isA( tmpinfo[j].getDeviceClass(), deviceClass ) ) {
  784. MidiDevice device = ((MidiDeviceProvider) v1.elementAt(i)).getDevice(tmpinfo[j]);
  785. if (isA(deviceClass, Receiver.class)) {
  786. if(device.getMaxReceivers() != 0) {
  787. v.addElement( tmpinfo[j] );
  788. }
  789. } else if (isA(deviceClass, Transmitter.class)) {
  790. if(device.getMaxTransmitters() != 0) {
  791. v.addElement( tmpinfo[j] );
  792. }
  793. } else if (deviceClass.isInstance(device)) {
  794. v.addElement( tmpinfo[j] );
  795. }
  796. }
  797. }
  798. varray = new MidiDevice.Info[ v.size() ];
  799. for(i = 0; i < varray.length; i++ ) {
  800. varray[i] = (MidiDevice.Info)(v.elementAt(i));
  801. }
  802. return varray;
  803. }
  804. /**
  805. * Attempts to locate and return the specified device
  806. * @throws IllegalArgumentException on failure.
  807. */
  808. private static Object getGenericDevice( Class deviceClass, MidiDevice.Info info) {
  809. int i;
  810. int j;
  811. MidiDevice s = null;
  812. MidiDevice.Info[] tmpinfo = null;
  813. Vector v1 = new Vector();
  814. v1 = getMidiDeviceProviders();
  815. for(i = 0; i < v1.size(); i++) {
  816. tmpinfo = ((MidiDeviceProvider) v1.elementAt(i)).getDeviceInfo();
  817. if( info == null ) {
  818. // $$jb: 06.04.99: Should we do more to guarantee which
  819. // device is the default?
  820. for (j = 0; j < tmpinfo.length; j++) {
  821. // $$kk: 10.12.99: ugh! i have wrecked this!!
  822. //if( isA(tmpinfo[j].getDeviceClass(), deviceClass ) ) {
  823. // return ((MidiDeviceProvider) v1.elementAt(i)).getDevice(tmpinfo[j]);
  824. //}
  825. MidiDevice device = ((MidiDeviceProvider) v1.elementAt(i)).getDevice(tmpinfo[j]);
  826. if (isA(deviceClass, Receiver.class)) {
  827. if(device.getMaxReceivers() != 0) {
  828. return device;
  829. }
  830. } else if (isA(deviceClass, Transmitter.class)) {
  831. if(device.getMaxTransmitters() != 0) {
  832. return device;
  833. }
  834. } else if (deviceClass.isInstance(device)) {
  835. return device;
  836. }
  837. }
  838. } else {
  839. for (j = 0; j < tmpinfo.length; j++) {
  840. if( tmpinfo[j].equals( info ) ) {
  841. // $$kk: 10.12.99: ugh! i have wrecked this!!
  842. // $$kk: 10.12.99: shouldn't need this check?
  843. //if( isA(tmpinfo[j].getDeviceClass(), deviceClass ) ) {
  844. // return ((MidiDeviceProvider) v1.elementAt(i)).getDevice(tmpinfo[j]);
  845. //}
  846. MidiDevice device = ((MidiDeviceProvider) v1.elementAt(i)).getDevice(tmpinfo[j]);
  847. if (deviceClass == null) {
  848. return device;
  849. }
  850. if (isA(deviceClass, Receiver.class)) {
  851. if(device.getMaxReceivers() != 0) {
  852. return device;
  853. }
  854. } else if (isA(deviceClass, Transmitter.class)) {
  855. if(device.getMaxTransmitters() != 0) {
  856. return device;
  857. }
  858. }
  859. }
  860. }
  861. }
  862. }
  863. throw new IllegalArgumentException("Requested device not installed: " + info);
  864. }
  865. /**
  866. * Obtains the set of services currently installed on the system
  867. * using sun.misc.Service, the SPI mechanism in 1.3.
  868. * @return a Vector of instances of providers for the requested service.
  869. * If no providers are available, a vector of length 0 will be returned.
  870. */
  871. private static Vector getJDK13Services( String serviceName ) {
  872. Vector v = null;
  873. try {
  874. Class jdk13Services =
  875. Class.forName( jdk13ServicesClassName );
  876. Method m = jdk13Services.getMethod(
  877. servicesMethodName,
  878. servicesParamTypes);
  879. Object[] arguments = new Object[] { serviceName };
  880. v = (Vector) m.invoke(jdk13Services,arguments);
  881. } catch(InvocationTargetException e1) {
  882. v = new Vector();
  883. } catch(ClassNotFoundException e2) {
  884. v = new Vector();
  885. } catch(IllegalAccessException e3) {
  886. v = new Vector();
  887. } catch(NoSuchMethodException e4) {
  888. v = new Vector();
  889. }
  890. return v;
  891. }
  892. /**
  893. * Obtains the default set of services currently installed on the system.
  894. * This method is only invoked if sun.misc.Service is not available.
  895. * @return a Vector of instances of providers for the requested service.
  896. * If no providers are available, a vector of length 0 will be returned.
  897. */
  898. private static Vector getDefaultServices( String serviceName ) {
  899. Vector v = null;
  900. try {
  901. Class defaultServices =
  902. Class.forName( defaultServicesClassName );
  903. Method m = defaultServices.getMethod(
  904. servicesMethodName,
  905. servicesParamTypes);
  906. Object[] arguments = new Object[] { serviceName };
  907. v = (Vector) m.invoke(defaultServices,arguments);
  908. } catch(InvocationTargetException e1) {
  909. v = new Vector();
  910. } catch(ClassNotFoundException e2) {
  911. v = new Vector();
  912. } catch(IllegalAccessException e3) {
  913. v = new Vector();
  914. } catch(NoSuchMethodException e4) {
  915. v = new Vector();
  916. }
  917. return v;
  918. }
  919. }