001/* MidiSystem.java -- Access system MIDI resources
002   Copyright (C) 2005 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 deviceProviders =
080        ServiceFactory.lookupProviders(MidiDeviceProvider.class);
081    List infoList = new ArrayList();
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 (MidiDevice.Info[])
092        infoList.toArray(new MidiDevice.Info[infoList.size()]);
093  }
094
095  /**
096   * Get the specified MIDI device.
097   *
098   * @param info a description of the device we're looking for
099   * @return the requested MIDI device
100   * @throws MidiUnavailableException if no MIDI devices are configured or found
101   * @throws IllegalArgumentException if the device described by info is not found
102   */
103  public static MidiDevice getMidiDevice(MidiDevice.Info info)
104    throws MidiUnavailableException
105  {
106    Iterator deviceProviders =
107        ServiceFactory.lookupProviders(MidiDeviceProvider.class);
108
109    if (! deviceProviders.hasNext())
110      throw new MidiUnavailableException("No MIDI device providers available.");
111
112    do
113    {
114      MidiDeviceProvider provider =
115        (MidiDeviceProvider) deviceProviders.next();
116      if (provider.isDeviceSupported(info))
117        return provider.getDevice(info);
118    } while (deviceProviders.hasNext());
119
120    throw new IllegalArgumentException("MIDI device "
121                                       + info + " not available.");
122  }
123
124  /**
125   * Get the default Receiver instance.  This just picks the first one
126   * it finds for now.
127   *
128   * @return the default Receiver instance
129   * @throws MidiUnavailableException if no Receiver is found
130   */
131  public static Receiver getReceiver() throws MidiUnavailableException
132  {
133    // TODO: The 1.5 spec has a fancy mechanism to specify the default
134    // receiver device.  For now, well just return the first one we find.
135    MidiDevice.Info[] infos = getMidiDeviceInfo();
136    for (int i = 0; i < infos.length; i++)
137    {
138      MidiDevice device = getMidiDevice(infos[i]);
139      if (device instanceof Receiver)
140        return (Receiver) device;
141    }
142    throw new MidiUnavailableException("No Receiver device available");
143  }
144
145  /**
146   * Get the default Transmitter instance.  This just picks the first one
147   * it finds for now.
148   *
149   * @return the default Transmitter instance
150   * @throws MidiUnavailableException if no Transmitter is found
151   */
152  public static Transmitter getTransmitter() throws MidiUnavailableException
153  {
154    // TODO: The 1.5 spec has a fancy mechanism to specify the default
155    // Transmitter device.  For now, well just return the first one we find.
156    MidiDevice.Info[] infos = getMidiDeviceInfo();
157    for (int i = 0; i < infos.length; i++)
158    {
159      MidiDevice device = getMidiDevice(infos[i]);
160      if (device instanceof Transmitter)
161        return (Transmitter) device;
162    }
163    throw new MidiUnavailableException("No Transmitter device available");
164  }
165
166  /**
167   * Get the default Synthesizer instance.  This just picks the first one
168   * it finds for now.
169   *
170   * @return the default Synthesizer instance
171   * @throws MidiUnavailableException if no Synthesizer is found
172   */
173  public static Synthesizer getSynthesizer() throws MidiUnavailableException
174  {
175    // TODO: The 1.5 spec has a fancy mechanism to specify the default
176    // Synthesizer device.  For now, well just return the first one we find.
177    MidiDevice.Info[] infos = getMidiDeviceInfo();
178    for (int i = 0; i < infos.length; i++)
179    {
180      MidiDevice device = getMidiDevice(infos[i]);
181      if (device instanceof Synthesizer)
182        return (Synthesizer) device;
183    }
184    throw new MidiUnavailableException("No Synthesizer device available");
185  }
186
187  /**
188   * Get the default Sequencer instance.  This just picks the first one
189   * it finds for now.
190   *
191   * @return the default Sequencer instance
192   * @throws MidiUnavailableException if no Sequencer is found
193   */
194  public static Sequencer getSequencer() throws MidiUnavailableException
195  {
196    // TODO: The 1.5 spec has a fancy mechanism to specify the default
197    // Sequencer device.  For now, well just return the first one we find.
198    MidiDevice.Info[] infos = getMidiDeviceInfo();
199    for (int i = 0; i < infos.length; i++)
200    {
201      MidiDevice device = getMidiDevice(infos[i]);
202      if (device instanceof Sequencer)
203        return (Sequencer) device;
204    }
205    throw new MidiUnavailableException("No Sequencer device available");
206  }
207
208  /**
209   * Read a Soundbank object from the given stream.
210   *
211   * @param stream the stream from which to read the Soundbank
212   * @return the Soundbank object
213   * @throws InvalidMidiDataException if we were unable to read the soundbank
214   * @throws IOException if an I/O error happened while reading
215   */
216  public static Soundbank getSoundbank(InputStream stream)
217    throws InvalidMidiDataException, IOException
218  {
219    Iterator readers = ServiceFactory.lookupProviders(SoundbankReader.class);
220    while (readers.hasNext())
221    {
222      SoundbankReader sr = (SoundbankReader) 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 readers = ServiceFactory.lookupProviders(SoundbankReader.class);
242    while (readers.hasNext())
243    {
244      SoundbankReader sr = (SoundbankReader) readers.next();
245      Soundbank sb = sr.getSoundbank(url);
246      if (sb != null)
247        return sb;
248    }
249    throw new InvalidMidiDataException("Cannot read from url " + url);
250  }
251
252  /**
253   * Read a Soundbank object from the given file.
254   *
255   * @param file the file from which to read the Soundbank
256   * @return the Soundbank object
257   * @throws InvalidMidiDataException if we were unable to read the soundbank
258   * @throws IOException if an I/O error happened while reading
259   */
260  public static Soundbank getSoundbank(File file)
261    throws InvalidMidiDataException, IOException
262  {
263    Iterator readers = ServiceFactory.lookupProviders(SoundbankReader.class);
264    while (readers.hasNext())
265    {
266      SoundbankReader sr = (SoundbankReader) readers.next();
267      Soundbank sb = sr.getSoundbank(file);
268      if (sb != null)
269        return sb;
270    }
271    throw new InvalidMidiDataException("Cannot read soundbank from file "
272                                       + file);
273  }
274
275  /**
276   * Read a MidiFileFormat object from the given stream.
277   *
278   * @param stream the stream from which to read the MidiFileFormat
279   * @return the MidiFileFormat object
280   * @throws InvalidMidiDataException if we were unable to read the MidiFileFormat
281   * @throws IOException if an I/O error happened while reading
282   */
283  public static MidiFileFormat getMidiFileFormat(InputStream stream)
284    throws InvalidMidiDataException, IOException
285  {
286    Iterator readers = ServiceFactory.lookupProviders(MidiFileReader.class);
287    while (readers.hasNext())
288    {
289      MidiFileReader sr = (MidiFileReader) readers.next();
290      MidiFileFormat sb = sr.getMidiFileFormat(stream);
291      if (sb != null)
292        return sb;
293    }
294    throw new InvalidMidiDataException("Can't read MidiFileFormat from stream");
295  }
296
297  /**
298   * Read a MidiFileFormat object from the given url.
299   *
300   * @param url the url from which to read the MidiFileFormat
301   * @return the MidiFileFormat object
302   * @throws InvalidMidiDataException if we were unable to read the MidiFileFormat
303   * @throws IOException if an I/O error happened while reading
304   */
305  public static MidiFileFormat getMidiFileFormat(URL url)
306    throws InvalidMidiDataException, IOException
307  {
308    Iterator readers = ServiceFactory.lookupProviders(MidiFileReader.class);
309    while (readers.hasNext())
310    {
311      MidiFileReader sr = (MidiFileReader) readers.next();
312      MidiFileFormat sb = sr.getMidiFileFormat(url);
313      if (sb != null)
314        return sb;
315    }
316    throw new InvalidMidiDataException("Cannot read from url " + url);
317  }
318
319  /**
320   * Read a MidiFileFormat object from the given file.
321   *
322   * @param file the file from which to read the MidiFileFormat
323   * @return the MidiFileFormat object
324   * @throws InvalidMidiDataException if we were unable to read the MidiFileFormat
325   * @throws IOException if an I/O error happened while reading
326   */
327  public static MidiFileFormat getMidiFileFormat(File file)
328    throws InvalidMidiDataException, IOException
329  {
330    Iterator readers = ServiceFactory.lookupProviders(MidiFileReader.class);
331    while (readers.hasNext())
332    {
333      MidiFileReader sr = (MidiFileReader) readers.next();
334      MidiFileFormat sb = sr.getMidiFileFormat(file);
335      if (sb != null)
336        return sb;
337    }
338    throw new InvalidMidiDataException("Can't read MidiFileFormat from file "
339                                       + file);
340  }
341
342  /**
343   * Read a Sequence object from the given stream.
344   *
345   * @param stream the stream from which to read the Sequence
346   * @return the Sequence object
347   * @throws InvalidMidiDataException if we were unable to read the Sequence
348   * @throws IOException if an I/O error happened while reading
349   */
350  public static Sequence getSequence(InputStream stream)
351    throws InvalidMidiDataException, IOException
352  {
353    Iterator readers = ServiceFactory.lookupProviders(MidiFileReader.class);
354    while (readers.hasNext())
355    {
356      MidiFileReader sr = (MidiFileReader) readers.next();
357      Sequence sq = sr.getSequence(stream);
358      if (sq != null)
359        return sq;
360    }
361    throw new InvalidMidiDataException("Can't read Sequence from stream");
362  }
363
364  /**
365   * Read a Sequence object from the given url.
366   *
367   * @param url the url from which to read the Sequence
368   * @return the Sequence object
369   * @throws InvalidMidiDataException if we were unable to read the Sequence
370   * @throws IOException if an I/O error happened while reading
371   */
372  public static Sequence getSequence(URL url)
373    throws InvalidMidiDataException, IOException
374  {
375    Iterator readers = ServiceFactory.lookupProviders(MidiFileReader.class);
376    while (readers.hasNext())
377    {
378      MidiFileReader sr = (MidiFileReader) readers.next();
379      Sequence sq = sr.getSequence(url);
380      if (sq != null)
381        return sq;
382    }
383    throw new InvalidMidiDataException("Cannot read from url " + url);
384  }
385
386  /**
387   * Read a Sequence object from the given file.
388   *
389   * @param file the file from which to read the Sequence
390   * @return the Sequence object
391   * @throws InvalidMidiDataException if we were unable to read the Sequence
392   * @throws IOException if an I/O error happened while reading
393   */
394  public static Sequence getSequence(File file)
395    throws InvalidMidiDataException, IOException
396  {
397    Iterator readers = ServiceFactory.lookupProviders(MidiFileReader.class);
398    while (readers.hasNext())
399    {
400      MidiFileReader sr = (MidiFileReader) readers.next();
401      Sequence sq = sr.getSequence(file);
402      if (sq != null)
403        return sq;
404    }
405    throw new InvalidMidiDataException("Can't read Sequence from file "
406                                       + file);
407  }
408
409  /**
410   * Return an array of supported MIDI file types on this system.
411   *
412   * @return the array of supported MIDI file types
413   */
414  public static int[] getMidiFileTypes()
415  {
416    // We only support a max of 3 MIDI file types.
417    boolean supported[] = new boolean[3];
418    // The number of supported formats.
419    int count = 0;
420    Iterator writers = ServiceFactory.lookupProviders(MidiFileWriter.class);
421    while (writers.hasNext())
422    {
423      MidiFileWriter fw = (MidiFileWriter) writers.next();
424      int types[] = fw.getMidiFileTypes();
425      for (int i = types.length; i > 0;)
426      {
427        int type = types[--i];
428        if (supported[type] == false)
429        {
430          count++;
431          supported[type] = true;
432        }
433      }
434    }
435    int result[] = new int[count];
436    for (int i = supported.length; i > 0;)
437    {
438      if (supported[--i])
439        result[--count] = i;
440    }
441    return result;
442  }
443
444  /**
445   * Return true if the system supports writing files of type fileType.
446   *
447   * @param fileType the MIDI file type we want to write
448   * @return true if we can write fileType files, false otherwise
449   */
450  public static boolean isFileTypeSupported(int fileType)
451  {
452    Iterator writers = ServiceFactory.lookupProviders(MidiFileWriter.class);
453    while (writers.hasNext())
454    {
455      MidiFileWriter fw = (MidiFileWriter) writers.next();
456
457      if (fw.isFileTypeSupported(fileType))
458        return true;
459    }
460    return false;
461  }
462
463  /**
464   * Return an array of supported MIDI file types on this system
465   * for the given sequnce.
466   *
467   * @param sequence the sequnce to write
468   * @return the array of supported MIDI file types
469   */
470  public static int[] getMidiFileTypes(Sequence sequence)
471  {
472    // We only support a max of 3 MIDI file types.
473    boolean supported[] = new boolean[3];
474    // The number of supported formats.
475    int count = 0;
476    Iterator writers = ServiceFactory.lookupProviders(MidiFileWriter.class);
477    while (writers.hasNext())
478    {
479      MidiFileWriter fw = (MidiFileWriter) writers.next();
480      int types[] = fw.getMidiFileTypes(sequence);
481      for (int i = types.length; i > 0;)
482      {
483        int type = types[--i];
484        if (supported[type] == false)
485        {
486          count++;
487          supported[type] = true;
488        }
489      }
490    }
491    int result[] = new int[count];
492    for (int i = supported.length; i > 0;)
493    {
494      if (supported[--i])
495        result[--count] = i;
496    }
497    return result;
498  }
499
500  /**
501   * Return true if the system supports writing files of type fileType
502   * for the given sequence.
503   *
504   * @param fileType the MIDI file type we want to write
505   * @param sequence the Sequence we want to write
506   * @return true if we can write fileType files for sequence, false otherwise
507   */
508  public static boolean isFileTypeSupported(int fileType, Sequence sequence)
509  {
510    Iterator writers = ServiceFactory.lookupProviders(MidiFileWriter.class);
511    while (writers.hasNext())
512    {
513      MidiFileWriter fw = (MidiFileWriter) writers.next();
514
515      if (fw.isFileTypeSupported(fileType, sequence))
516        return true;
517    }
518    return false;
519  }
520
521  /**
522   * Write a sequence to an output stream using a specific MIDI file format.
523   *
524   * @param in the sequence to write
525   * @param fileType the MIDI file format to use
526   * @param out the output stream to write to
527   * @return the number of bytes written
528   * @throws IOException if an I/O exception happens
529   * @throws IllegalArgumentException if fileType is not supported for in
530   */
531  public static int write(Sequence in, int fileType, OutputStream out)
532    throws IOException
533  {
534    Iterator writers = ServiceFactory.lookupProviders(MidiFileWriter.class);
535    while (writers.hasNext())
536    {
537      MidiFileWriter fw = (MidiFileWriter) writers.next();
538
539      if (fw.isFileTypeSupported(fileType, in))
540        return fw.write(in, fileType, out);
541    }
542    throw new IllegalArgumentException("File type "
543                                       + fileType + " is not supported");
544  }
545
546  /**
547   * Write a sequence to a file using a specific MIDI file format.
548   *
549   * @param in the sequence to write
550   * @param fileType the MIDI file format to use
551   * @param out the file to write to
552   * @return the number of bytes written
553   * @throws IOException if an I/O exception happens
554   * @throws IllegalArgumentException if fileType is not supported for in
555   */
556  public static int write(Sequence in, int fileType, File out)
557    throws IOException
558  {
559    Iterator writers = ServiceFactory.lookupProviders(MidiFileWriter.class);
560    while (writers.hasNext())
561    {
562      MidiFileWriter fw = (MidiFileWriter) writers.next();
563
564      if (fw.isFileTypeSupported(fileType, in))
565        return fw.write(in, fileType, out);
566    }
567    throw new IllegalArgumentException("File type "
568                                       + fileType + " is not supported");
569  }
570}