001/* MidiSystem.java -- Access system MIDI resources
002   Copyright (C) 2005, 2012 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.sound.midi;
040
041import gnu.classpath.ServiceFactory;
042
043import java.io.File;
044import java.io.IOException;
045import java.io.InputStream;
046import java.io.OutputStream;
047import java.net.URL;
048import java.util.ArrayList;
049import java.util.List;
050import java.util.Iterator;
051
052import javax.sound.midi.spi.MidiDeviceProvider;
053import javax.sound.midi.spi.MidiFileReader;
054import javax.sound.midi.spi.MidiFileWriter;
055import javax.sound.midi.spi.SoundbankReader;
056
057/**
058 * MidiSystem provides access to the computer system's MIDI resources,
059 * as well as utility routines for reading MIDI files and more.
060 *
061 * @author Anthony Green (green@redhat.com)
062 * @since 1.3
063 *
064 */
065public class MidiSystem
066{
067  private MidiSystem()
068  {
069    // Not instantiable.
070  }
071
072  /**
073   * Get an array of all available MIDI devices.
074   *
075   * @return a possibly empty array of all available MIDI devices
076   */
077  public static MidiDevice.Info[] getMidiDeviceInfo()
078  {
079    Iterator<MidiDeviceProvider> deviceProviders =
080        ServiceFactory.lookupProviders(MidiDeviceProvider.class);
081    List<MidiDevice.Info> infoList = new ArrayList<MidiDevice.Info>();
082
083    while (deviceProviders.hasNext())
084    {
085      MidiDeviceProvider provider = (MidiDeviceProvider) deviceProviders.next();
086      MidiDevice.Info[] infos = provider.getDeviceInfo();
087      for (int i = infos.length; i > 0; )
088        infoList.add(infos[--i]);
089    }
090
091    return infoList.toArray(new MidiDevice.Info[infoList.size()]);
092  }
093
094  /**
095   * Get the specified MIDI device.
096   *
097   * @param info a description of the device we're looking for
098   * @return the requested MIDI device
099   * @throws MidiUnavailableException if no MIDI devices are configured or found
100   * @throws IllegalArgumentException if the device described by info is not found
101   */
102  public static MidiDevice getMidiDevice(MidiDevice.Info info)
103    throws MidiUnavailableException
104  {
105    Iterator<MidiDeviceProvider> deviceProviders =
106        ServiceFactory.lookupProviders(MidiDeviceProvider.class);
107
108    if (! deviceProviders.hasNext())
109      throw new MidiUnavailableException("No MIDI device providers available.");
110
111    do
112    {
113      MidiDeviceProvider provider =
114        (MidiDeviceProvider) deviceProviders.next();
115      if (provider.isDeviceSupported(info))
116        return provider.getDevice(info);
117    } while (deviceProviders.hasNext());
118
119    throw new IllegalArgumentException("MIDI device "
120                                       + info + " not available.");
121  }
122
123  /**
124   * Get the default Receiver instance.  This just picks the first one
125   * it finds for now.
126   *
127   * @return the default Receiver instance
128   * @throws MidiUnavailableException if no Receiver is found
129   */
130  public static Receiver getReceiver() throws MidiUnavailableException
131  {
132    // TODO: The 1.5 spec has a fancy mechanism to specify the default
133    // receiver device.  For now, well just return the first one we find.
134    MidiDevice.Info[] infos = getMidiDeviceInfo();
135    for (int i = 0; i < infos.length; i++)
136    {
137      MidiDevice device = getMidiDevice(infos[i]);
138      if (device instanceof Receiver)
139        return (Receiver) device;
140    }
141    throw new MidiUnavailableException("No Receiver device available");
142  }
143
144  /**
145   * Get the default Transmitter instance.  This just picks the first one
146   * it finds for now.
147   *
148   * @return the default Transmitter instance
149   * @throws MidiUnavailableException if no Transmitter is found
150   */
151  public static Transmitter getTransmitter() throws MidiUnavailableException
152  {
153    // TODO: The 1.5 spec has a fancy mechanism to specify the default
154    // Transmitter device.  For now, well just return the first one we find.
155    MidiDevice.Info[] infos = getMidiDeviceInfo();
156    for (int i = 0; i < infos.length; i++)
157    {
158      MidiDevice device = getMidiDevice(infos[i]);
159      if (device instanceof Transmitter)
160        return (Transmitter) device;
161    }
162    throw new MidiUnavailableException("No Transmitter device available");
163  }
164
165  /**
166   * Get the default Synthesizer instance.  This just picks the first one
167   * it finds for now.
168   *
169   * @return the default Synthesizer instance
170   * @throws MidiUnavailableException if no Synthesizer is found
171   */
172  public static Synthesizer getSynthesizer() throws MidiUnavailableException
173  {
174    // TODO: The 1.5 spec has a fancy mechanism to specify the default
175    // Synthesizer device.  For now, well just return the first one we find.
176    MidiDevice.Info[] infos = getMidiDeviceInfo();
177    for (int i = 0; i < infos.length; i++)
178    {
179      MidiDevice device = getMidiDevice(infos[i]);
180      if (device instanceof Synthesizer)
181        return (Synthesizer) device;
182    }
183    throw new MidiUnavailableException("No Synthesizer device available");
184  }
185
186  /**
187   * Get the default Sequencer instance.  This just picks the first one
188   * it finds for now.
189   *
190   * @return the default Sequencer instance
191   * @throws MidiUnavailableException if no Sequencer is found
192   */
193  public static Sequencer getSequencer() throws MidiUnavailableException
194  {
195    // TODO: The 1.5 spec has a fancy mechanism to specify the default
196    // Sequencer device.  For now, well just return the first one we find.
197    MidiDevice.Info[] infos = getMidiDeviceInfo();
198    for (int i = 0; i < infos.length; i++)
199    {
200      MidiDevice device = getMidiDevice(infos[i]);
201      if (device instanceof Sequencer)
202        return (Sequencer) device;
203    }
204    throw new MidiUnavailableException("No Sequencer device available");
205  }
206
207  /**
208   * Read a Soundbank object from the given stream.
209   *
210   * @param stream the stream from which to read the Soundbank
211   * @return the Soundbank object
212   * @throws InvalidMidiDataException if we were unable to read the soundbank
213   * @throws IOException if an I/O error happened while reading
214   */
215  public static Soundbank getSoundbank(InputStream stream)
216    throws InvalidMidiDataException, IOException
217  {
218    Iterator<SoundbankReader> readers =
219      ServiceFactory.lookupProviders(SoundbankReader.class);
220    while (readers.hasNext())
221    {
222      SoundbankReader sr = readers.next();
223      Soundbank sb = sr.getSoundbank(stream);
224      if (sb != null)
225        return sb;
226    }
227    throw new InvalidMidiDataException("Cannot read soundbank from stream");
228  }
229
230  /**
231   * Read a Soundbank object from the given url.
232   *
233   * @param url the url from which to read the Soundbank
234   * @return the Soundbank object
235   * @throws InvalidMidiDataException if we were unable to read the soundbank
236   * @throws IOException if an I/O error happened while reading
237   */
238  public static Soundbank getSoundbank(URL url)
239    throws InvalidMidiDataException, IOException
240  {
241    Iterator<SoundbankReader> readers =
242      ServiceFactory.lookupProviders(SoundbankReader.class);
243    while (readers.hasNext())
244    {
245      SoundbankReader sr = readers.next();
246      Soundbank sb = sr.getSoundbank(url);
247      if (sb != null)
248        return sb;
249    }
250    throw new InvalidMidiDataException("Cannot read from url " + url);
251  }
252
253  /**
254   * Read a Soundbank object from the given file.
255   *
256   * @param file the file from which to read the Soundbank
257   * @return the Soundbank object
258   * @throws InvalidMidiDataException if we were unable to read the soundbank
259   * @throws IOException if an I/O error happened while reading
260   */
261  public static Soundbank getSoundbank(File file)
262    throws InvalidMidiDataException, IOException
263  {
264    Iterator<SoundbankReader> readers =
265      ServiceFactory.lookupProviders(SoundbankReader.class);
266    while (readers.hasNext())
267    {
268      SoundbankReader sr = (SoundbankReader) readers.next();
269      Soundbank sb = sr.getSoundbank(file);
270      if (sb != null)
271        return sb;
272    }
273    throw new InvalidMidiDataException("Cannot read soundbank from file "
274                                       + file);
275  }
276
277  /**
278   * Read a MidiFileFormat object from the given stream.
279   *
280   * @param stream the stream from which to read the MidiFileFormat
281   * @return the MidiFileFormat object
282   * @throws InvalidMidiDataException if we were unable to read the MidiFileFormat
283   * @throws IOException if an I/O error happened while reading
284   */
285  public static MidiFileFormat getMidiFileFormat(InputStream stream)
286    throws InvalidMidiDataException, IOException
287  {
288    Iterator<MidiFileReader> readers =
289      ServiceFactory.lookupProviders(MidiFileReader.class);
290    while (readers.hasNext())
291    {
292      MidiFileReader sr = readers.next();
293      MidiFileFormat sb = sr.getMidiFileFormat(stream);
294      if (sb != null)
295        return sb;
296    }
297    throw new InvalidMidiDataException("Can't read MidiFileFormat from stream");
298  }
299
300  /**
301   * Read a MidiFileFormat object from the given url.
302   *
303   * @param url the url from which to read the MidiFileFormat
304   * @return the MidiFileFormat object
305   * @throws InvalidMidiDataException if we were unable to read the MidiFileFormat
306   * @throws IOException if an I/O error happened while reading
307   */
308  public static MidiFileFormat getMidiFileFormat(URL url)
309    throws InvalidMidiDataException, IOException
310  {
311    Iterator<MidiFileReader> readers =
312      ServiceFactory.lookupProviders(MidiFileReader.class);
313    while (readers.hasNext())
314    {
315      MidiFileReader sr = readers.next();
316      MidiFileFormat sb = sr.getMidiFileFormat(url);
317      if (sb != null)
318        return sb;
319    }
320    throw new InvalidMidiDataException("Cannot read from url " + url);
321  }
322
323  /**
324   * Read a MidiFileFormat object from the given file.
325   *
326   * @param file the file from which to read the MidiFileFormat
327   * @return the MidiFileFormat object
328   * @throws InvalidMidiDataException if we were unable to read the MidiFileFormat
329   * @throws IOException if an I/O error happened while reading
330   */
331  public static MidiFileFormat getMidiFileFormat(File file)
332    throws InvalidMidiDataException, IOException
333  {
334    Iterator<MidiFileReader> readers =
335      ServiceFactory.lookupProviders(MidiFileReader.class);
336    while (readers.hasNext())
337    {
338      MidiFileReader sr = readers.next();
339      MidiFileFormat sb = sr.getMidiFileFormat(file);
340      if (sb != null)
341        return sb;
342    }
343    throw new InvalidMidiDataException("Can't read MidiFileFormat from file "
344                                       + file);
345  }
346
347  /**
348   * Read a Sequence object from the given stream.
349   *
350   * @param stream the stream from which to read the Sequence
351   * @return the Sequence object
352   * @throws InvalidMidiDataException if we were unable to read the Sequence
353   * @throws IOException if an I/O error happened while reading
354   */
355  public static Sequence getSequence(InputStream stream)
356    throws InvalidMidiDataException, IOException
357  {
358    Iterator<MidiFileReader> readers =
359      ServiceFactory.lookupProviders(MidiFileReader.class);
360    while (readers.hasNext())
361    {
362      MidiFileReader sr = readers.next();
363      Sequence sq = sr.getSequence(stream);
364      if (sq != null)
365        return sq;
366    }
367    throw new InvalidMidiDataException("Can't read Sequence from stream");
368  }
369
370  /**
371   * Read a Sequence object from the given url.
372   *
373   * @param url the url from which to read the Sequence
374   * @return the Sequence object
375   * @throws InvalidMidiDataException if we were unable to read the Sequence
376   * @throws IOException if an I/O error happened while reading
377   */
378  public static Sequence getSequence(URL url)
379    throws InvalidMidiDataException, IOException
380  {
381    Iterator<MidiFileReader> readers =
382      ServiceFactory.lookupProviders(MidiFileReader.class);
383    while (readers.hasNext())
384    {
385      MidiFileReader sr = readers.next();
386      Sequence sq = sr.getSequence(url);
387      if (sq != null)
388        return sq;
389    }
390    throw new InvalidMidiDataException("Cannot read from url " + url);
391  }
392
393  /**
394   * Read a Sequence object from the given file.
395   *
396   * @param file the file from which to read the Sequence
397   * @return the Sequence object
398   * @throws InvalidMidiDataException if we were unable to read the Sequence
399   * @throws IOException if an I/O error happened while reading
400   */
401  public static Sequence getSequence(File file)
402    throws InvalidMidiDataException, IOException
403  {
404    Iterator<MidiFileReader> readers =
405      ServiceFactory.lookupProviders(MidiFileReader.class);
406    while (readers.hasNext())
407    {
408      MidiFileReader sr = readers.next();
409      Sequence sq = sr.getSequence(file);
410      if (sq != null)
411        return sq;
412    }
413    throw new InvalidMidiDataException("Can't read Sequence from file "
414                                       + file);
415  }
416
417  /**
418   * Return an array of supported MIDI file types on this system.
419   *
420   * @return the array of supported MIDI file types
421   */
422  public static int[] getMidiFileTypes()
423  {
424    // We only support a max of 3 MIDI file types.
425    boolean supported[] = new boolean[3];
426    // The number of supported formats.
427    int count = 0;
428    Iterator<MidiFileWriter> writers =
429      ServiceFactory.lookupProviders(MidiFileWriter.class);
430    while (writers.hasNext())
431    {
432      MidiFileWriter fw = writers.next();
433      int types[] = fw.getMidiFileTypes();
434      for (int i = types.length; i > 0;)
435      {
436        int type = types[--i];
437        if (supported[type] == false)
438        {
439          count++;
440          supported[type] = true;
441        }
442      }
443    }
444    int result[] = new int[count];
445    for (int i = supported.length; i > 0;)
446    {
447      if (supported[--i])
448        result[--count] = i;
449    }
450    return result;
451  }
452
453  /**
454   * Return true if the system supports writing files of type fileType.
455   *
456   * @param fileType the MIDI file type we want to write
457   * @return true if we can write fileType files, false otherwise
458   */
459  public static boolean isFileTypeSupported(int fileType)
460  {
461    Iterator<MidiFileWriter> writers = ServiceFactory.lookupProviders(MidiFileWriter.class);
462    while (writers.hasNext())
463    {
464      MidiFileWriter fw = writers.next();
465
466      if (fw.isFileTypeSupported(fileType))
467        return true;
468    }
469    return false;
470  }
471
472  /**
473   * Return an array of supported MIDI file types on this system
474   * for the given sequnce.
475   *
476   * @param sequence the sequnce to write
477   * @return the array of supported MIDI file types
478   */
479  public static int[] getMidiFileTypes(Sequence sequence)
480  {
481    // We only support a max of 3 MIDI file types.
482    boolean supported[] = new boolean[3];
483    // The number of supported formats.
484    int count = 0;
485    Iterator<MidiFileWriter> writers = ServiceFactory.lookupProviders(MidiFileWriter.class);
486    while (writers.hasNext())
487    {
488      MidiFileWriter fw = (MidiFileWriter) writers.next();
489      int types[] = fw.getMidiFileTypes(sequence);
490      for (int i = types.length; i > 0;)
491      {
492        int type = types[--i];
493        if (supported[type] == false)
494        {
495          count++;
496          supported[type] = true;
497        }
498      }
499    }
500    int result[] = new int[count];
501    for (int i = supported.length; i > 0;)
502    {
503      if (supported[--i])
504        result[--count] = i;
505    }
506    return result;
507  }
508
509  /**
510   * Return true if the system supports writing files of type fileType
511   * for the given sequence.
512   *
513   * @param fileType the MIDI file type we want to write
514   * @param sequence the Sequence we want to write
515   * @return true if we can write fileType files for sequence, false otherwise
516   */
517  public static boolean isFileTypeSupported(int fileType, Sequence sequence)
518  {
519    Iterator<MidiFileWriter> writers = ServiceFactory.lookupProviders(MidiFileWriter.class);
520    while (writers.hasNext())
521    {
522      MidiFileWriter fw = (MidiFileWriter) writers.next();
523
524      if (fw.isFileTypeSupported(fileType, sequence))
525        return true;
526    }
527    return false;
528  }
529
530  /**
531   * Write a sequence to an output stream using a specific MIDI file format.
532   *
533   * @param in the sequence to write
534   * @param fileType the MIDI file format to use
535   * @param out the output stream to write to
536   * @return the number of bytes written
537   * @throws IOException if an I/O exception happens
538   * @throws IllegalArgumentException if fileType is not supported for in
539   */
540  public static int write(Sequence in, int fileType, OutputStream out)
541    throws IOException
542  {
543    Iterator<MidiFileWriter> writers = ServiceFactory.lookupProviders(MidiFileWriter.class);
544    while (writers.hasNext())
545    {
546      MidiFileWriter fw = (MidiFileWriter) writers.next();
547
548      if (fw.isFileTypeSupported(fileType, in))
549        return fw.write(in, fileType, out);
550    }
551    throw new IllegalArgumentException("File type "
552                                       + fileType + " is not supported");
553  }
554
555  /**
556   * Write a sequence to a file using a specific MIDI file format.
557   *
558   * @param in the sequence to write
559   * @param fileType the MIDI file format to use
560   * @param out the file to write to
561   * @return the number of bytes written
562   * @throws IOException if an I/O exception happens
563   * @throws IllegalArgumentException if fileType is not supported for in
564   */
565  public static int write(Sequence in, int fileType, File out)
566    throws IOException
567  {
568    Iterator<MidiFileWriter> writers = ServiceFactory.lookupProviders(MidiFileWriter.class);
569    while (writers.hasNext())
570    {
571      MidiFileWriter fw = (MidiFileWriter) writers.next();
572
573      if (fw.isFileTypeSupported(fileType, in))
574        return fw.write(in, fileType, out);
575    }
576    throw new IllegalArgumentException("File type "
577                                       + fileType + " is not supported");
578  }
579}