001    /* Main interface to audio system
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.sampled;
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.HashSet;
049    import java.util.Iterator;
050    
051    import javax.sound.sampled.spi.AudioFileReader;
052    import javax.sound.sampled.spi.AudioFileWriter;
053    import javax.sound.sampled.spi.FormatConversionProvider;
054    import javax.sound.sampled.spi.MixerProvider;
055    
056    /**
057     * This clas is the primary interface to the audio system.  It contains
058     * a number of static methods which can be used to access this package's
059     * functionality.
060     *
061     * @since 1.3
062     */
063    public class AudioSystem
064    {
065      /**
066       * A constant which can be passed to a number of methods in this package,
067       * to indicate an unspecified value.
068       */
069      public static final int NOT_SPECIFIED = -1;
070    
071      // This class is not instantiable.
072      private AudioSystem()
073      {
074      }
075    
076      /**
077       * Return the file format of a given File.
078       * @param f the file to check
079       * @return the format of the file
080       * @throws UnsupportedAudioFileException if the file's format is not
081       * recognized
082       * @throws IOException if there is an I/O error reading the file
083       */
084      public static AudioFileFormat getAudioFileFormat(File f)
085        throws UnsupportedAudioFileException, IOException
086      {
087        Iterator i = ServiceFactory.lookupProviders(AudioFileReader.class);
088        while (i.hasNext())
089          {
090            AudioFileReader reader = (AudioFileReader) i.next();
091            try
092              {
093                return reader.getAudioFileFormat(f);
094              }
095            catch (UnsupportedAudioFileException _)
096              {
097                // Try the next provider.
098              }
099          }
100        throw new UnsupportedAudioFileException("file type not recognized");
101      }
102    
103      /**
104       * Return the file format of a given input stream.
105       * @param is the input stream to check
106       * @return the format of the stream
107       * @throws UnsupportedAudioFileException if the stream's format is not
108       * recognized
109       * @throws IOException if there is an I/O error reading the stream
110       */
111      public static AudioFileFormat getAudioFileFormat(InputStream is)
112        throws UnsupportedAudioFileException, IOException
113      {
114        Iterator i = ServiceFactory.lookupProviders(AudioFileReader.class);
115        while (i.hasNext())
116          {
117            AudioFileReader reader = (AudioFileReader) i.next();
118            try
119              {
120                return reader.getAudioFileFormat(is);
121              }
122            catch (UnsupportedAudioFileException _)
123              {
124                // Try the next provider.
125              }
126          }
127        throw new UnsupportedAudioFileException("input stream type not recognized");
128      }
129    
130      /**
131       * Return the file format of a given URL.
132       * @param url the URL to check
133       * @return the format of the URL
134       * @throws UnsupportedAudioFileException if the URL's format is not
135       * recognized
136       * @throws IOException if there is an I/O error reading the URL
137       */
138      public static AudioFileFormat getAudioFileFormat(URL url)
139        throws UnsupportedAudioFileException, IOException
140      {
141        Iterator i = ServiceFactory.lookupProviders(AudioFileReader.class);
142        while (i.hasNext())
143          {
144            AudioFileReader reader = (AudioFileReader) i.next();
145            try
146              {
147                return reader.getAudioFileFormat(url);
148              }
149            catch (UnsupportedAudioFileException _)
150              {
151                // Try the next provider.
152              }
153          }
154        throw new UnsupportedAudioFileException("URL type not recognized");
155      }
156    
157      /**
158       * Return an array of all the supported AudioFileFormat types.
159       * @return an array of unique types
160       */
161      public static AudioFileFormat.Type[] getAudioFileTypes()
162      {
163        HashSet<AudioFileFormat.Type> result
164          = new HashSet<AudioFileFormat.Type>();
165        Iterator i = ServiceFactory.lookupProviders(AudioFileWriter.class);
166        while (i.hasNext())
167          {
168            AudioFileWriter writer = (AudioFileWriter) i.next();
169            AudioFileFormat.Type[] types = writer.getAudioFileTypes();
170            for (int j = 0; j < types.length; ++j)
171              result.add(types[j]);
172          }
173        return result.toArray(new AudioFileFormat.Type[result.size()]);
174      }
175    
176      /**
177       * Return an array of all the supported AudioFileFormat types which match the
178       * given audio input stream
179       * @param ais the audio input stream
180       * @return an array of unique types
181       */
182      public static AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream ais)
183      {
184        HashSet<AudioFileFormat.Type> result
185          = new HashSet<AudioFileFormat.Type>();
186        Iterator i = ServiceFactory.lookupProviders(AudioFileWriter.class);
187        while (i.hasNext())
188          {
189            AudioFileWriter writer = (AudioFileWriter) i.next();
190            AudioFileFormat.Type[] types = writer.getAudioFileTypes(ais);
191            for (int j = 0; j < types.length; ++j)
192              result.add(types[j]);
193          }
194        return result.toArray(new AudioFileFormat.Type[result.size()]);
195      }
196    
197      /**
198       * Given an audio input stream, this will try to create a new audio input
199       * stream whose encoding matches the given target encoding.  If no provider
200       * offers this conversion, an exception is thrown.
201       * @param targ the target encoding
202       * @param ais the original audio stream
203       * @return a new audio stream
204       * @throws IllegalArgumentException if the conversion cannot be made
205       */
206      public static AudioInputStream getAudioInputStream(AudioFormat.Encoding targ,
207                                                         AudioInputStream ais)
208      {
209        Iterator i = ServiceFactory.lookupProviders(FormatConversionProvider.class);
210        while (i.hasNext())
211          {
212            FormatConversionProvider prov = (FormatConversionProvider) i.next();
213            if (! prov.isConversionSupported(targ, ais.getFormat()))
214              continue;
215            return prov.getAudioInputStream(targ, ais);
216          }
217        throw new IllegalArgumentException("encoding not supported for stream");
218     }
219    
220      /**
221       * Given an audio input stream, this will try to create a new audio input
222       * stream whose format matches the given target format.  If no provider
223       * offers this conversion, an exception is thrown.
224       * @param targ the target format
225       * @param ais the original audio stream
226       * @return a new audio stream
227       * @throws IllegalArgumentException if the conversion cannot be made
228       */
229      public static AudioInputStream getAudioInputStream(AudioFormat targ,
230                                                         AudioInputStream ais)
231      {
232        Iterator i = ServiceFactory.lookupProviders(FormatConversionProvider.class);
233        while (i.hasNext())
234          {
235            FormatConversionProvider prov = (FormatConversionProvider) i.next();
236            if (! prov.isConversionSupported(targ, ais.getFormat()))
237              continue;
238            return prov.getAudioInputStream(targ, ais);
239          }
240        throw new IllegalArgumentException("format not supported for stream");
241       }
242    
243      /**
244       * Return an audio input stream for the file.
245       * @param f the file to read
246       * @return an audio input stream for the file
247       * @throws UnsupportedAudioFileException if the file's audio format is not
248       * recognized
249       * @throws IOException if there is an error while reading the file
250       */
251      public static AudioInputStream getAudioInputStream(File f)
252        throws UnsupportedAudioFileException, IOException
253      {
254        Iterator i = ServiceFactory.lookupProviders(AudioFileReader.class);
255        while (i.hasNext())
256          {
257            AudioFileReader reader = (AudioFileReader) i.next();
258            try
259              {
260                return reader.getAudioInputStream(f);
261              }
262            catch (UnsupportedAudioFileException _)
263              {
264                // Try the next provider.
265              }
266          }
267        throw new UnsupportedAudioFileException("file type not recognized");
268      }
269    
270      /**
271       * Return an audio input stream given an input stream.
272       * @param is the input stream
273       * @return an audio input stream
274       * @throws UnsupportedAudioFileException if the input stream's audio format
275       * is not supported by any of the installed providers
276       * @throws IOException if there is an error while reading the input stream
277       */
278      public static AudioInputStream getAudioInputStream(InputStream is)
279        throws UnsupportedAudioFileException, IOException
280      {
281        Iterator i = ServiceFactory.lookupProviders(AudioFileReader.class);
282        while (i.hasNext())
283          {
284            AudioFileReader reader = (AudioFileReader) i.next();
285            try
286              {
287                return reader.getAudioInputStream(is);
288              }
289            catch (UnsupportedAudioFileException _)
290              {
291                // Try the next provider.
292              }
293          }
294        throw new UnsupportedAudioFileException("input stream type not recognized");
295      }
296    
297      /**
298       * Return an audio input stream for the given URL.
299       * @param url the URL
300       * @return an audio input stream
301       * @throws UnsupportedAudioFileException if the URL's audio format is not
302       * supported by any of the installed providers
303       * @throws IOException if there is an error while reading the URL
304       */
305      public static AudioInputStream getAudioInputStream(URL url)
306        throws UnsupportedAudioFileException, IOException
307      {
308        Iterator i = ServiceFactory.lookupProviders(AudioFileReader.class);
309        while (i.hasNext())
310          {
311            AudioFileReader reader = (AudioFileReader) i.next();
312            try
313              {
314                return reader.getAudioInputStream(url);
315              }
316            catch (UnsupportedAudioFileException _)
317              {
318                // Try the next provider.
319              }
320          }
321        throw new UnsupportedAudioFileException("URL type not recognized");
322      }
323    
324      /**
325       * Return a new clip which can be used for playing back an audio stream.
326       * @throws LineUnavailableException if a clip is not available for some
327       * reason
328       * @throws SecurityException if a clip cannot be made for security reasons
329       * @since 1.5
330       */
331      public static Clip getClip()
332        throws LineUnavailableException
333      {
334        Mixer.Info[] infos = getMixerInfo();
335        for (int i = 0; i < infos.length; ++i)
336          {
337            Mixer mix = getMixer(infos[i]);
338            Line[] lines = mix.getSourceLines();
339            for (int j = 0; j < lines.length; ++j)
340              {
341                if (lines[j] instanceof Clip)
342                  return (Clip) lines[j];
343              }
344          }
345        throw new LineUnavailableException("no Clip available");
346      }
347    
348      /**
349       * Return a new clip which can be used for playing back an audio stream.
350       * The clip is obtained from the indicated mixer.
351       * @param info the mixer to use
352       * @throws LineUnavailableException if a clip is not available for some
353       * reason
354       * @throws SecurityException if a clip cannot be made for security reasons
355       * @since 1.5
356       */
357      public static Clip getClip(Mixer.Info info)
358        throws LineUnavailableException
359      {
360        Mixer mix = getMixer(info);
361        Line[] lines = mix.getSourceLines();
362        for (int j = 0; j < lines.length; ++j)
363          {
364            if (lines[j] instanceof Clip)
365              return (Clip) lines[j];
366          }
367        throw new LineUnavailableException("no Clip available");
368      }
369    
370      /**
371       * Return a line matching the provided description.  All the providers
372       * on the system are searched for a matching line.
373       * @param info description of the line
374       * @return the matching line
375       * @throws LineUnavailableException if no provider supplies a matching line
376       */
377      public static Line getLine(Line.Info info) throws LineUnavailableException
378      {
379        Mixer.Info[] infos = getMixerInfo();
380        for (int i = 0; i < infos.length; ++i)
381          {
382            Mixer mix = getMixer(infos[i]);
383            try
384            {
385              return mix.getLine(info);
386            }
387            catch (LineUnavailableException _)
388            {
389              // Try the next provider.
390            }
391          }
392        throw new LineUnavailableException("no Clip available");
393      }
394    
395      /**
396       * Return a mixer matching the provided description.  All the providers
397       * on the system are searched for a matching mixer.
398       * @param info description of the mixer
399       * @return the matching mixer
400       * @throws IllegalArgumentException if no provider supplies a matching mixer
401       */
402      public static Mixer getMixer(Mixer.Info info)
403      {
404        Iterator i = ServiceFactory.lookupProviders(MixerProvider.class);
405        while (i.hasNext())
406          {
407            MixerProvider prov = (MixerProvider) i.next();
408            if (prov.isMixerSupported(info))
409              return prov.getMixer(info);
410          }
411        throw new IllegalArgumentException("mixer not found");
412      }
413    
414      /**
415       * Return an array of descriptions of all the mixers provided on the system.
416       */
417      public static Mixer.Info[] getMixerInfo()
418      {
419        HashSet<Mixer.Info> result = new HashSet<Mixer.Info>();
420        Iterator i = ServiceFactory.lookupProviders(MixerProvider.class);
421        while (i.hasNext())
422          {
423            MixerProvider prov = (MixerProvider) i.next();
424            Mixer.Info[] is = prov.getMixerInfo();
425            for (int j = 0; j < is.length; ++j)
426              result.add(is[j]);
427          }
428        return result.toArray(new Mixer.Info[result.size()]);
429      }
430    
431      /**
432       * Return a source data line matching the given audio format.
433       * @param fmt the audio format
434       * @throws LineUnavailableException if no source data line matching
435       * this format is available
436       * @since 1.5
437       */
438      public static SourceDataLine getSourceDataLine(AudioFormat fmt)
439        throws LineUnavailableException
440      {
441        DataLine.Info info = new DataLine.Info(SourceDataLine.class, fmt);
442        Mixer.Info[] mixers = getMixerInfo();
443        for (int i = 0; i < mixers.length; ++i)
444          {
445            Mixer mix = getMixer(mixers[i]);
446            if (mix.isLineSupported(info))
447              return (SourceDataLine) mix.getLine(info);
448          }
449        throw new LineUnavailableException("source data line not found");
450      }
451    
452      /**
453       * Return a target data line matching the given audio format.
454       * @param fmt the audio format
455       * @throws LineUnavailableException if no target data line matching
456       * this format is available
457       * @since 1.5
458       */
459      public static SourceDataLine getSourceDataLine(AudioFormat fmt,
460                                                     Mixer.Info mixer)
461        throws LineUnavailableException
462      {
463        DataLine.Info info = new DataLine.Info(SourceDataLine.class, fmt);
464        Mixer mix = getMixer(mixer);
465        if (mix.isLineSupported(info))
466          return (SourceDataLine) mix.getLine(info);
467        throw new LineUnavailableException("source data line not found");
468      }
469    
470      /**
471       * Return an array of descriptions of all the source lines matching
472       * the given line description.
473       * @param info description of the lines to match
474       */
475      public static Line.Info[] getSourceLineInfo(Line.Info info)
476      {
477        HashSet<Line.Info> result = new HashSet<Line.Info>();
478        Mixer.Info[] infos = getMixerInfo();
479        for (int i = 0; i < infos.length; ++i)
480          {
481            Mixer mix = getMixer(infos[i]);
482            Line.Info[] srcs = mix.getSourceLineInfo(info);
483            for (int j = 0; j < srcs.length; ++j)
484              result.add(srcs[j]);
485          }
486        return result.toArray(new Line.Info[result.size()]);
487      }
488    
489      /**
490       * Find and return a target data line matching the given audio format.
491       * @param fmt the format to match
492       * @throws LineUnavailableException if no matching line was found
493       * @since 1.5
494       */
495      public static TargetDataLine getTargetDataLine(AudioFormat fmt)
496        throws LineUnavailableException
497      {
498        DataLine.Info info = new DataLine.Info(TargetDataLine.class, fmt);
499        Mixer.Info[] mixers = getMixerInfo();
500        for (int i = 0; i < mixers.length; ++i)
501          {
502            Mixer mix = getMixer(mixers[i]);
503            if (mix.isLineSupported(info))
504              return (TargetDataLine) mix.getLine(info);
505          }
506        throw new LineUnavailableException("target data line not found");
507      }
508    
509      /**
510       * Return a target data line matching the given audio format and
511       * mixer.
512       * @param fmt the audio format
513       * @param mixer the mixer description
514       * @return a target data line
515       * @throws LineUnavailableException if no matching target data line was
516       * found
517       * @since 1.5
518       */
519      public static TargetDataLine getTargetDataLine(AudioFormat fmt,
520                                                     Mixer.Info mixer)
521        throws LineUnavailableException
522      {
523        DataLine.Info info = new DataLine.Info(TargetDataLine.class, fmt);
524        Mixer mix = getMixer(mixer);
525        if (mix.isLineSupported(info))
526          return (TargetDataLine) mix.getLine(info);
527        throw new LineUnavailableException("target data line not found");
528      }
529    
530      /**
531       * Given a source encoding, return an array of all target encodings to which
532       * data in this form can be converted.
533       * @param source the source encoding
534       */
535      public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat.Encoding source)
536      {
537        HashSet<AudioFormat.Encoding> result
538          = new HashSet<AudioFormat.Encoding>();
539        Iterator i = ServiceFactory.lookupProviders(FormatConversionProvider.class);
540        while (i.hasNext())
541          {
542            FormatConversionProvider prov = (FormatConversionProvider) i.next();
543            if (! prov.isSourceEncodingSupported(source))
544              continue;
545            AudioFormat.Encoding[] es = prov.getTargetEncodings();
546            for (int j = 0; j < es.length; ++j)
547              result.add(es[j]);
548          }
549        return result.toArray(new AudioFormat.Encoding[result.size()]);
550      }
551    
552      /**
553       * Given a source format, return an array of all the target encodings to
554       * which data in this format can be converted.
555       * @param source the source format
556       */
557      public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat source)
558      {
559        HashSet<AudioFormat.Encoding> result
560          = new HashSet<AudioFormat.Encoding>();
561        Iterator i = ServiceFactory.lookupProviders(FormatConversionProvider.class);
562        while (i.hasNext())
563          {
564            FormatConversionProvider prov = (FormatConversionProvider) i.next();
565            AudioFormat.Encoding[] es = prov.getTargetEncodings(source);
566            for (int j = 0; j < es.length; ++j)
567              result.add(es[j]);
568          }
569        return result.toArray(new AudioFormat.Encoding[result.size()]);
570      }
571    
572      /**
573       * Given a target encoding and a source audio format, return an array of all
574       * matching audio formats to which data in this source format can be converted.
575       * @param encoding the target encoding
576       * @param sourceFmt the source format
577       */
578      public static AudioFormat[] getTargetFormats(AudioFormat.Encoding encoding,
579                                                   AudioFormat sourceFmt)
580      {
581        HashSet<AudioFormat> result = new HashSet<AudioFormat>();
582        Iterator i = ServiceFactory.lookupProviders(FormatConversionProvider.class);
583        while (i.hasNext())
584          {
585            FormatConversionProvider prov = (FormatConversionProvider) i.next();
586            AudioFormat[] es = prov.getTargetFormats(encoding, sourceFmt);
587            for (int j = 0; j < es.length; ++j)
588              result.add(es[j]);
589          }
590        return result.toArray(new AudioFormat[result.size()]);
591      }
592    
593      /**
594       * Given a line description, return an array of descriptions of all
595       * the matching target lines.
596       * @param info the line description
597       */
598      public static Line.Info[] getTargetLineInfo(Line.Info info)
599      {
600        HashSet<Line.Info> result = new HashSet<Line.Info>();
601        Mixer.Info[] infos = getMixerInfo();
602        for (int i = 0; i < infos.length; ++i)
603          {
604            Mixer mix = getMixer(infos[i]);
605            Line.Info[] targs = mix.getTargetLineInfo(info);
606            for (int j = 0; j < targs.length; ++j)
607              result.add(targs[j]);
608          }
609        return result.toArray(new Line.Info[result.size()]);
610      }
611    
612      /**
613       * Return true if the currently installed providers are able to
614       * convert data from the given source format to the given target encoding.
615       * @param targ the target encoding
616       * @param source the source format
617       */
618      public static boolean isConversionSupported(AudioFormat.Encoding targ,
619                                                  AudioFormat source)
620      {
621        Iterator i
622          = ServiceFactory.lookupProviders(FormatConversionProvider.class);
623        while (i.hasNext())
624          {
625            FormatConversionProvider prov = (FormatConversionProvider) i.next();
626            if (prov.isConversionSupported(targ, source))
627              return true;
628          }
629        return false;
630      }
631    
632      /**
633       * Return true if the currently installed providers are able to convert
634       * the given source format to the given target format.
635       * @param targ the target format
636       * @param source the source format
637       */
638      public static boolean isConversionSupported(AudioFormat targ,
639                                                  AudioFormat source)
640      {
641        Iterator i
642          = ServiceFactory.lookupProviders(FormatConversionProvider.class);
643        while (i.hasNext())
644          {
645            FormatConversionProvider prov = (FormatConversionProvider) i.next();
646            if (prov.isConversionSupported(targ, source))
647              return true;
648          }
649        return false;
650      }
651    
652      private static boolean isFileTypeSupported(AudioFileFormat.Type[] types,
653                                                 AudioFileFormat.Type type)
654      {
655        for (int i = 0; i < types.length; ++i)
656          {
657            if (types[i].equals(type))
658              return true;
659          }
660        return false;
661      }
662    
663      /**
664       * Return true if the given audio file format is supported by one of
665       * the providers installed on the system.
666       * @param type the audio file format type
667       */
668      public static boolean isFileTypeSupported(AudioFileFormat.Type type)
669      {
670        return isFileTypeSupported(getAudioFileTypes(), type);
671      }
672    
673      /**
674       * Return true if the given audio file format is supported for the
675       * given audio input stream by one of the providers installed on the
676       * system.
677       * @param type the audio file format type
678       * @param ais the audio input stream
679       */
680      public static boolean isFileTypeSupported(AudioFileFormat.Type type,
681                                                AudioInputStream ais)
682      {
683        return isFileTypeSupported(getAudioFileTypes(ais), type);
684      }
685    
686      /**
687       * Return true if some provider on the system supplies a line
688       * matching the argument.
689       * @param info the line to match
690       */
691      public static boolean isLineSupported(Line.Info info)
692      {
693        Mixer.Info[] infos = getMixerInfo();
694        for (int i = 0; i < infos.length; ++i)
695          {
696            if (getMixer(infos[i]).isLineSupported(info))
697              return true;
698          }
699        return false;
700      }
701    
702      /**
703       * Write an audio input stream to the given file, using the specified
704       * audio file format.  All the providers installed on the system will
705       * be searched to find one that supports this operation.
706       * @param ais the audio input stream to write
707       * @param type the desired audio file format type
708       * @param out the file to write to
709       * @return the number of bytes written
710       * @throws IOException if an I/O error occurs while writing
711       * @throws IllegalArgumentException if the file type is not supported
712       */
713      public static int write(AudioInputStream ais, AudioFileFormat.Type type,
714                              File out)
715        throws IOException
716      {
717        Iterator i = ServiceFactory.lookupProviders(AudioFileWriter.class);
718        while (i.hasNext())
719          {
720            AudioFileWriter w = (AudioFileWriter) i.next();
721            if (w.isFileTypeSupported(type, ais))
722              return w.write(ais, type, out);
723          }
724        throw new IllegalArgumentException("file type not supported by system");
725      }
726    
727      /**
728       * Write an audio input stream to the given output stream, using the
729       * specified audio file format.  All the providers installed on the
730       * system will be searched to find one that supports this operation.
731       * @param ais the audio input stream to write
732       * @param type the desired audio file format type
733       * @param os the output stream to write to
734       * @return the number of bytes written
735       * @throws IOException if an I/O error occurs while writing
736       * @throws IllegalArgumentException if the file type is not supported
737       */
738      public static int write(AudioInputStream ais, AudioFileFormat.Type type,
739                              OutputStream os)
740        throws IOException
741      {
742        Iterator i = ServiceFactory.lookupProviders(AudioFileWriter.class);
743        while (i.hasNext())
744          {
745            AudioFileWriter w = (AudioFileWriter) i.next();
746            if (w.isFileTypeSupported(type, ais))
747              return w.write(ais, type, os);
748          }
749        throw new IllegalArgumentException("file type not supported by system");
750      }
751    }