001    /* MidiSystem.java -- Access system MIDI resources
002       Copyright (C) 2005 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.sound.midi;
040    
041    import gnu.classpath.ServiceFactory;
042    
043    import java.io.File;
044    import java.io.IOException;
045    import java.io.InputStream;
046    import java.io.OutputStream;
047    import java.net.URL;
048    import java.util.ArrayList;
049    import java.util.List;
050    import java.util.Iterator;
051    
052    import javax.sound.midi.spi.MidiDeviceProvider;
053    import javax.sound.midi.spi.MidiFileReader;
054    import javax.sound.midi.spi.MidiFileWriter;
055    import 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     */
065    public 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    }