Path: blob/master/src/java.desktop/share/classes/javax/sound/sampled/AudioSystem.java
41159 views
/*1* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package javax.sound.sampled;2627import java.io.File;28import java.io.IOException;29import java.io.InputStream;30import java.io.OutputStream;31import java.net.URL;32import java.util.ArrayList;33import java.util.Collections;34import java.util.HashSet;35import java.util.List;36import java.util.Objects;37import java.util.Properties;38import java.util.Set;39import java.util.Vector;4041import javax.sound.sampled.spi.AudioFileReader;42import javax.sound.sampled.spi.AudioFileWriter;43import javax.sound.sampled.spi.FormatConversionProvider;44import javax.sound.sampled.spi.MixerProvider;4546import com.sun.media.sound.JDK13Services;4748/* $fb TODO:49* - consistent usage of (typed) collections50*/515253/**54* The {@code AudioSystem} class acts as the entry point to the sampled-audio55* system resources. This class lets you query and access the mixers that are56* installed on the system. {@code AudioSystem} includes a number of methods for57* converting audio data between different formats, and for translating between58* audio files and streams. It also provides a method for obtaining a59* {@link Line} directly from the {@code AudioSystem} without dealing explicitly60* with mixers.61* <p>62* Properties can be used to specify the default mixer for specific line types.63* Both system properties and a properties file are considered. The64* "sound.properties" properties file is read from an implementation-specific65* location (typically it is the {@code conf} directory in the Java installation66* directory). The optional "javax.sound.config.file" system property can be67* used to specify the properties file that will be read as the initial68* configuration. If a property exists both as a system property and in the69* properties file, the system property takes precedence. If none is specified,70* a suitable default is chosen among the available devices. The syntax of the71* properties file is specified in72* {@link Properties#load(InputStream) Properties.load}. The following table73* lists the available property keys and which methods consider them:74*75* <table class="striped">76* <caption>Audio System Property Keys</caption>77* <thead>78* <tr>79* <th scope="col">Property Key80* <th scope="col">Interface81* <th scope="col">Affected Method(s)82* </thead>83* <tbody>84* <tr>85* <th scope="row">{@code javax.sound.sampled.Clip}86* <td>{@link Clip}87* <td>{@link #getLine}, {@link #getClip}88* <tr>89* <th scope="row">{@code javax.sound.sampled.Port}90* <td>{@link Port}91* <td>{@link #getLine}92* <tr>93* <th scope="row">{@code javax.sound.sampled.SourceDataLine}94* <td>{@link SourceDataLine}95* <td>{@link #getLine}, {@link #getSourceDataLine}96* <tr>97* <th scope="row">{@code javax.sound.sampled.TargetDataLine}98* <td>{@link TargetDataLine}99* <td>{@link #getLine}, {@link #getTargetDataLine}100* </tbody>101* </table>102*103* The property value consists of the provider class name and the mixer name,104* separated by the hash mark ("#"). The provider class name is the105* fully-qualified name of a concrete {@link MixerProvider mixer provider}106* class. The mixer name is matched against the {@code String} returned by the107* {@code getName} method of {@code Mixer.Info}. Either the class name, or the108* mixer name may be omitted. If only the class name is specified, the trailing109* hash mark is optional.110* <p>111* If the provider class is specified, and it can be successfully retrieved from112* the installed providers, the list of {@code Mixer.Info} objects is retrieved113* from the provider. Otherwise, or when these mixers do not provide a114* subsequent match, the list is retrieved from {@link #getMixerInfo} to contain115* all available {@code Mixer.Info} objects.116* <p>117* If a mixer name is specified, the resulting list of {@code Mixer.Info}118* objects is searched: the first one with a matching name, and whose119* {@code Mixer} provides the respective line interface, will be returned. If no120* matching {@code Mixer.Info} object is found, or the mixer name is not121* specified, the first mixer from the resulting list, which provides the122* respective line interface, will be returned.123* <p>124* For example, the property {@code javax.sound.sampled.Clip} with a value125* {@code "com.sun.media.sound.MixerProvider#SunClip"} will have the following126* consequences when {@code getLine} is called requesting a {@code Clip}127* instance: if the class {@code com.sun.media.sound.MixerProvider} exists in128* the list of installed mixer providers, the first {@code Clip} from the first129* mixer with name {@code "SunClip"} will be returned. If it cannot be found,130* the first {@code Clip} from the first mixer of the specified provider will be131* returned, regardless of name. If there is none, the first {@code Clip} from132* the first {@code Mixer} with name {@code "SunClip"} in the list of all mixers133* (as returned by {@code getMixerInfo}) will be returned, or, if not found, the134* first {@code Clip} of the first {@code Mixer} that can be found in the list135* of all mixers is returned. If that fails, too, an136* {@code IllegalArgumentException} is thrown.137*138* @author Kara Kytle139* @author Florian Bomers140* @author Matthias Pfisterer141* @author Kevin P. Smith142* @see AudioFormat143* @see AudioInputStream144* @see Mixer145* @see Line146* @see Line.Info147* @since 1.3148*/149public class AudioSystem {150151/**152* An integer that stands for an unknown numeric value. This value is153* appropriate only for signed quantities that do not normally take negative154* values. Examples include file sizes, frame sizes, buffer sizes, and155* sample rates. A number of Java Sound constructors accept a value of156* {@code NOT_SPECIFIED} for such parameters. Other methods may also accept157* or return this value, as documented.158*/159public static final int NOT_SPECIFIED = -1;160161/**162* Private no-args constructor for ensuring against instantiation.163*/164private AudioSystem() {165}166167/**168* Obtains an array of mixer info objects that represents the set of audio169* mixers that are currently installed on the system.170*171* @return an array of info objects for the currently installed mixers. If172* no mixers are available on the system, an array of length 0 is173* returned.174* @see #getMixer175*/176public static Mixer.Info[] getMixerInfo() {177178List<Mixer.Info> infos = getMixerInfoList();179Mixer.Info[] allInfos = infos.toArray(new Mixer.Info[infos.size()]);180return allInfos;181}182183/**184* Obtains the requested audio mixer.185*186* @param info a {@code Mixer.Info} object representing the desired mixer,187* or {@code null} for the system default mixer188* @return the requested mixer189* @throws SecurityException if the requested mixer is unavailable because190* of security restrictions191* @throws IllegalArgumentException if the info object does not represent a192* mixer installed on the system193* @see #getMixerInfo194*/195public static Mixer getMixer(final Mixer.Info info) {196for (final MixerProvider provider : getMixerProviders()) {197try {198return provider.getMixer(info);199} catch (IllegalArgumentException | NullPointerException ignored) {200// The MixerProvider.getMixer(null) should return default Mixer,201// This behaviour was assumed from the beginning, but strictly202// specified only in the jdk9. Since the jdk1.1.5 we skipped203// NPE for some reason and therefore skipped some204// implementations of MixerProviders, which throw NPE. To keep205// support of such implementations, we still ignore NPE.206}207}208throw new IllegalArgumentException(209String.format("Mixer not supported: %s", info));210}211212//$$fb 2002-11-26: fix for 4757930: DOC: AudioSystem.getTarget/SourceLineInfo() is ambiguous213214/**215* Obtains information about all source lines of a particular type that are216* supported by the installed mixers.217*218* @param info a {@code Line.Info} object that specifies the kind of lines219* about which information is requested220* @return an array of {@code Line.Info} objects describing source lines221* matching the type requested. If no matching source lines are222* supported, an array of length 0 is returned.223* @see Mixer#getSourceLineInfo(Line.Info)224*/225public static Line.Info[] getSourceLineInfo(Line.Info info) {226227Vector<Line.Info> vector = new Vector<>();228Line.Info[] currentInfoArray;229230Mixer mixer;231Line.Info fullInfo = null;232Mixer.Info[] infoArray = getMixerInfo();233234for (int i = 0; i < infoArray.length; i++) {235236mixer = getMixer(infoArray[i]);237238currentInfoArray = mixer.getSourceLineInfo(info);239for (int j = 0; j < currentInfoArray.length; j++) {240vector.addElement(currentInfoArray[j]);241}242}243244Line.Info[] returnedArray = new Line.Info[vector.size()];245246for (int i = 0; i < returnedArray.length; i++) {247returnedArray[i] = vector.get(i);248}249250return returnedArray;251}252253/**254* Obtains information about all target lines of a particular type that are255* supported by the installed mixers.256*257* @param info a {@code Line.Info} object that specifies the kind of lines258* about which information is requested259* @return an array of {@code Line.Info} objects describing target lines260* matching the type requested. If no matching target lines are261* supported, an array of length 0 is returned.262* @see Mixer#getTargetLineInfo(Line.Info)263*/264public static Line.Info[] getTargetLineInfo(Line.Info info) {265266Vector<Line.Info> vector = new Vector<>();267Line.Info[] currentInfoArray;268269Mixer mixer;270Line.Info fullInfo = null;271Mixer.Info[] infoArray = getMixerInfo();272273for (int i = 0; i < infoArray.length; i++) {274275mixer = getMixer(infoArray[i]);276277currentInfoArray = mixer.getTargetLineInfo(info);278for (int j = 0; j < currentInfoArray.length; j++) {279vector.addElement(currentInfoArray[j]);280}281}282283Line.Info[] returnedArray = new Line.Info[vector.size()];284285for (int i = 0; i < returnedArray.length; i++) {286returnedArray[i] = vector.get(i);287}288289return returnedArray;290}291292/**293* Indicates whether the system supports any lines that match the specified294* {@code Line.Info} object. A line is supported if any installed mixer295* supports it.296*297* @param info a {@code Line.Info} object describing the line for which298* support is queried299* @return {@code true} if at least one matching line is supported,300* otherwise {@code false}301* @see Mixer#isLineSupported(Line.Info)302*/303public static boolean isLineSupported(Line.Info info) {304305Mixer mixer;306Mixer.Info[] infoArray = getMixerInfo();307308for (int i = 0; i < infoArray.length; i++) {309310if( infoArray[i] != null ) {311mixer = getMixer(infoArray[i]);312if (mixer.isLineSupported(info)) {313return true;314}315}316}317318return false;319}320321/**322* Obtains a line that matches the description in the specified323* {@code Line.Info} object.324* <p>325* If a {@code DataLine} is requested, and {@code info} is an instance of326* {@code DataLine.Info} specifying at least one fully qualified audio327* format, the last one will be used as the default format of the returned328* {@code DataLine}.329* <p>330* If system properties331* {@code javax.sound.sampled.Clip},332* {@code javax.sound.sampled.Port},333* {@code javax.sound.sampled.SourceDataLine} and334* {@code javax.sound.sampled.TargetDataLine} are defined or they are335* defined in the file "sound.properties", they are used to retrieve default336* lines. For details, refer to the {@link AudioSystem class description}.337*338* If the respective property is not set, or the mixer requested in the339* property is not installed or does not provide the requested line, all340* installed mixers are queried for the requested line type. A Line will be341* returned from the first mixer providing the requested line type.342*343* @param info a {@code Line.Info} object describing the desired kind of344* line345* @return a line of the requested kind346* @throws LineUnavailableException if a matching line is not available due347* to resource restrictions348* @throws SecurityException if a matching line is not available due to349* security restrictions350* @throws IllegalArgumentException if the system does not support at least351* one line matching the specified {@code Line.Info} object through352* any installed mixer353*/354public static Line getLine(Line.Info info) throws LineUnavailableException {355LineUnavailableException lue = null;356List<MixerProvider> providers = getMixerProviders();357358359// 1: try from default mixer for this line class360try {361Mixer mixer = getDefaultMixer(providers, info);362if (mixer != null && mixer.isLineSupported(info)) {363return mixer.getLine(info);364}365} catch (LineUnavailableException e) {366lue = e;367} catch (IllegalArgumentException iae) {368// must not happen... but better to catch it here,369// if plug-ins are badly written370}371372373// 2: if that doesn't work, try to find any mixing mixer374for(int i = 0; i < providers.size(); i++) {375MixerProvider provider = providers.get(i);376Mixer.Info[] infos = provider.getMixerInfo();377378for (int j = 0; j < infos.length; j++) {379try {380Mixer mixer = provider.getMixer(infos[j]);381// see if this is an appropriate mixer which can mix382if (isAppropriateMixer(mixer, info, true)) {383return mixer.getLine(info);384}385} catch (LineUnavailableException e) {386lue = e;387} catch (IllegalArgumentException iae) {388// must not happen... but better to catch it here,389// if plug-ins are badly written390}391}392}393394395// 3: if that didn't work, try to find any non-mixing mixer396for(int i = 0; i < providers.size(); i++) {397MixerProvider provider = providers.get(i);398Mixer.Info[] infos = provider.getMixerInfo();399for (int j = 0; j < infos.length; j++) {400try {401Mixer mixer = provider.getMixer(infos[j]);402// see if this is an appropriate mixer which can mix403if (isAppropriateMixer(mixer, info, false)) {404return mixer.getLine(info);405}406} catch (LineUnavailableException e) {407lue = e;408} catch (IllegalArgumentException iae) {409// must not happen... but better to catch it here,410// if plug-ins are badly written411}412}413}414415// if this line was supported but was not available, throw the last416// LineUnavailableException we got (??).417if (lue != null) {418throw lue;419}420421// otherwise, the requested line was not supported, so throw422// an Illegal argument exception423throw new IllegalArgumentException("No line matching " +424info.toString() + " is supported.");425}426427/**428* Obtains a clip that can be used for playing back an audio file or an429* audio stream. The returned clip will be provided by the default system430* mixer, or, if not possible, by any other mixer installed in the system431* that supports a {@code Clip} object.432* <p>433* The returned clip must be opened with the {@code open(AudioFormat)} or434* {@code open(AudioInputStream)} method.435* <p>436* This is a high-level method that uses {@code getMixer} and437* {@code getLine} internally.438* <p>439* If the system property {@code javax.sound.sampled.Clip} is defined or it440* is defined in the file "sound.properties", it is used to retrieve the441* default clip. For details, refer to the442* {@link AudioSystem class description}.443*444* @return the desired clip object445* @throws LineUnavailableException if a clip object is not available due to446* resource restrictions447* @throws SecurityException if a clip object is not available due to448* security restrictions449* @throws IllegalArgumentException if the system does not support at least450* one clip instance through any installed mixer451* @see #getClip(Mixer.Info)452* @since 1.5453*/454public static Clip getClip() throws LineUnavailableException{455AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,456AudioSystem.NOT_SPECIFIED,45716, 2, 4,458AudioSystem.NOT_SPECIFIED, true);459DataLine.Info info = new DataLine.Info(Clip.class, format);460return (Clip) AudioSystem.getLine(info);461}462463/**464* Obtains a clip from the specified mixer that can be used for playing back465* an audio file or an audio stream.466* <p>467* The returned clip must be opened with the {@code open(AudioFormat)} or468* {@code open(AudioInputStream)} method.469* <p>470* This is a high-level method that uses {@code getMixer} and471* {@code getLine} internally.472*473* @param mixerInfo a {@code Mixer.Info} object representing the desired474* mixer, or {@code null} for the system default mixer475* @return a clip object from the specified mixer476* @throws LineUnavailableException if a clip is not available from this477* mixer due to resource restrictions478* @throws SecurityException if a clip is not available from this mixer due479* to security restrictions480* @throws IllegalArgumentException if the system does not support at least481* one clip through the specified mixer482* @see #getClip()483* @since 1.5484*/485public static Clip getClip(Mixer.Info mixerInfo) throws LineUnavailableException{486AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,487AudioSystem.NOT_SPECIFIED,48816, 2, 4,489AudioSystem.NOT_SPECIFIED, true);490DataLine.Info info = new DataLine.Info(Clip.class, format);491Mixer mixer = AudioSystem.getMixer(mixerInfo);492return (Clip) mixer.getLine(info);493}494495/**496* Obtains a source data line that can be used for playing back audio data497* in the format specified by the {@code AudioFormat} object. The returned498* line will be provided by the default system mixer, or, if not possible,499* by any other mixer installed in the system that supports a matching500* {@code SourceDataLine} object.501* <p>502* The returned line should be opened with the {@code open(AudioFormat)} or503* {@code open(AudioFormat, int)} method.504* <p>505* This is a high-level method that uses {@code getMixer} and506* {@code getLine} internally.507* <p>508* The returned {@code SourceDataLine}'s default audio format will be509* initialized with {@code format}.510* <p>511* If the system property {@code javax.sound.sampled.SourceDataLine} is512* defined or it is defined in the file "sound.properties", it is used to513* retrieve the default source data line. For details, refer to the514* {@link AudioSystem class description}.515*516* @param format an {@code AudioFormat} object specifying the supported517* audio format of the returned line, or {@code null} for any audio518* format519* @return the desired {@code SourceDataLine} object520* @throws LineUnavailableException if a matching source data line is not521* available due to resource restrictions522* @throws SecurityException if a matching source data line is not available523* due to security restrictions524* @throws IllegalArgumentException if the system does not support at least525* one source data line supporting the specified audio format526* through any installed mixer527* @see #getSourceDataLine(AudioFormat, Mixer.Info)528* @since 1.5529*/530public static SourceDataLine getSourceDataLine(AudioFormat format)531throws LineUnavailableException{532DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);533return (SourceDataLine) AudioSystem.getLine(info);534}535536/**537* Obtains a source data line that can be used for playing back audio data538* in the format specified by the {@code AudioFormat} object, provided by539* the mixer specified by the {@code Mixer.Info} object.540* <p>541* The returned line should be opened with the {@code open(AudioFormat)} or542* {@code open(AudioFormat, int)} method.543* <p>544* This is a high-level method that uses {@code getMixer} and545* {@code getLine} internally.546* <p>547* The returned {@code SourceDataLine}'s default audio format will be548* initialized with {@code format}.549*550* @param format an {@code AudioFormat} object specifying the supported551* audio format of the returned line, or {@code null} for any audio552* format553* @param mixerinfo a {@code Mixer.Info} object representing the desired554* mixer, or {@code null} for the system default mixer555* @return the desired {@code SourceDataLine} object556* @throws LineUnavailableException if a matching source data line is not557* available from the specified mixer due to resource restrictions558* @throws SecurityException if a matching source data line is not available559* from the specified mixer due to security restrictions560* @throws IllegalArgumentException if the specified mixer does not support561* at least one source data line supporting the specified audio562* format563* @see #getSourceDataLine(AudioFormat)564* @since 1.5565*/566public static SourceDataLine getSourceDataLine(AudioFormat format,567Mixer.Info mixerinfo)568throws LineUnavailableException{569DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);570Mixer mixer = AudioSystem.getMixer(mixerinfo);571return (SourceDataLine) mixer.getLine(info);572}573574/**575* Obtains a target data line that can be used for recording audio data in576* the format specified by the {@code AudioFormat} object. The returned line577* will be provided by the default system mixer, or, if not possible, by any578* other mixer installed in the system that supports a matching579* {@code TargetDataLine} object.580* <p>581* The returned line should be opened with the {@code open(AudioFormat)} or582* {@code open(AudioFormat, int)} method.583* <p>584* This is a high-level method that uses {@code getMixer} and585* {@code getLine} internally.586* <p>587* The returned {@code TargetDataLine}'s default audio format will be588* initialized with {@code format}.589* <p>590* If the system property {@code javax.sound.sampled.TargetDataLine} is591* defined or it is defined in the file "sound.properties", it is used to592* retrieve the default target data line. For details, refer to the593* {@link AudioSystem class description}.594*595* @param format an {@code AudioFormat} object specifying the supported596* audio format of the returned line, or {@code null} for any audio597* format598* @return the desired {@code TargetDataLine} object599* @throws LineUnavailableException if a matching target data line is not600* available due to resource restrictions601* @throws SecurityException if a matching target data line is not available602* due to security restrictions603* @throws IllegalArgumentException if the system does not support at least604* one target data line supporting the specified audio format605* through any installed mixer606* @see #getTargetDataLine(AudioFormat, Mixer.Info)607* @see AudioPermission608* @since 1.5609*/610public static TargetDataLine getTargetDataLine(AudioFormat format)611throws LineUnavailableException{612613DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);614return (TargetDataLine) AudioSystem.getLine(info);615}616617/**618* Obtains a target data line that can be used for recording audio data in619* the format specified by the {@code AudioFormat} object, provided by the620* mixer specified by the {@code Mixer.Info} object.621* <p>622* The returned line should be opened with the {@code open(AudioFormat)} or623* {@code open(AudioFormat, int)} method.624* <p>625* This is a high-level method that uses {@code getMixer} and626* {@code getLine} internally.627* <p>628* The returned {@code TargetDataLine}'s default audio format will be629* initialized with {@code format}.630*631* @param format an {@code AudioFormat} object specifying the supported632* audio format of the returned line, or {@code null} for any audio633* format634* @param mixerinfo a {@code Mixer.Info} object representing the desired635* mixer, or {@code null} for the system default mixer636* @return the desired {@code TargetDataLine} object637* @throws LineUnavailableException if a matching target data line is not638* available from the specified mixer due to resource restrictions639* @throws SecurityException if a matching target data line is not available640* from the specified mixer due to security restrictions641* @throws IllegalArgumentException if the specified mixer does not support642* at least one target data line supporting the specified audio643* format644* @see #getTargetDataLine(AudioFormat)645* @see AudioPermission646* @since 1.5647*/648public static TargetDataLine getTargetDataLine(AudioFormat format,649Mixer.Info mixerinfo)650throws LineUnavailableException {651652DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);653Mixer mixer = AudioSystem.getMixer(mixerinfo);654return (TargetDataLine) mixer.getLine(info);655}656657// $$fb 2002-04-12: fix for 4662082: behavior of AudioSystem.getTargetEncodings() methods doesn't match the spec658659/**660* Obtains the encodings that the system can obtain from an audio input661* stream with the specified encoding using the set of installed format662* converters.663*664* @param sourceEncoding the encoding for which conversion support is665* queried666* @return array of encodings. If {@code sourceEncoding} is not supported,667* an array of length 0 is returned. Otherwise, the array will have668* a length of at least 1, representing {@code sourceEncoding}669* (no conversion).670* @throws NullPointerException if {@code sourceEncoding} is {@code null}671*/672public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat.Encoding sourceEncoding) {673Objects.requireNonNull(sourceEncoding);674675List<FormatConversionProvider> codecs = getFormatConversionProviders();676Vector<AudioFormat.Encoding> encodings = new Vector<>();677678AudioFormat.Encoding[] encs = null;679680// gather from all the codecs681for(int i=0; i<codecs.size(); i++ ) {682FormatConversionProvider codec = codecs.get(i);683if( codec.isSourceEncodingSupported( sourceEncoding ) ) {684encs = codec.getTargetEncodings();685for (int j = 0; j < encs.length; j++) {686encodings.addElement( encs[j] );687}688}689}690if (!encodings.contains(sourceEncoding)) {691encodings.addElement(sourceEncoding);692}693694return encodings.toArray(new AudioFormat.Encoding[encodings.size()]);695}696697// $$fb 2002-04-12: fix for 4662082: behavior of AudioSystem.getTargetEncodings() methods doesn't match the spec698699/**700* Obtains the encodings that the system can obtain from an audio input701* stream with the specified format using the set of installed format702* converters.703*704* @param sourceFormat the audio format for which conversion is queried705* @return array of encodings. If {@code sourceFormat}is not supported, an706* array of length 0 is returned. Otherwise, the array will have a707* length of at least 1, representing the encoding of708* {@code sourceFormat} (no conversion).709* @throws NullPointerException if {@code sourceFormat} is {@code null}710*/711public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat) {712Objects.requireNonNull(sourceFormat);713714List<FormatConversionProvider> codecs = getFormatConversionProviders();715List<AudioFormat.Encoding> encs = new ArrayList<>();716717// gather from all the codecs718for (final FormatConversionProvider codec : codecs) {719Collections.addAll(encs, codec.getTargetEncodings(sourceFormat));720}721722if (!encs.contains(sourceFormat.getEncoding())) {723encs.add(sourceFormat.getEncoding());724}725726return encs.toArray(new AudioFormat.Encoding[encs.size()]);727}728729/**730* Indicates whether an audio input stream of the specified encoding can be731* obtained from an audio input stream that has the specified format.732*733* @param targetEncoding the desired encoding after conversion734* @param sourceFormat the audio format before conversion735* @return {@code true} if the conversion is supported, otherwise736* {@code false}737* @throws NullPointerException if {@code targetEncoding} or738* {@code sourceFormat} are {@code null}739*/740public static boolean isConversionSupported(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) {741Objects.requireNonNull(targetEncoding);742Objects.requireNonNull(sourceFormat);743if (sourceFormat.getEncoding().equals(targetEncoding)) {744return true;745}746747List<FormatConversionProvider> codecs = getFormatConversionProviders();748749for(int i=0; i<codecs.size(); i++ ) {750FormatConversionProvider codec = codecs.get(i);751if(codec.isConversionSupported(targetEncoding,sourceFormat) ) {752return true;753}754}755return false;756}757758/**759* Obtains an audio input stream of the indicated encoding, by converting760* the provided audio input stream.761*762* @param targetEncoding the desired encoding after conversion763* @param sourceStream the stream to be converted764* @return an audio input stream of the indicated encoding765* @throws IllegalArgumentException if the conversion is not supported766* @throws NullPointerException if {@code targetEncoding} or767* {@code sourceStream} are {@code null}768* @see #getTargetEncodings(AudioFormat.Encoding)769* @see #getTargetEncodings(AudioFormat)770* @see #isConversionSupported(AudioFormat.Encoding, AudioFormat)771* @see #getAudioInputStream(AudioFormat, AudioInputStream)772*/773public static AudioInputStream getAudioInputStream(AudioFormat.Encoding targetEncoding,774AudioInputStream sourceStream) {775Objects.requireNonNull(targetEncoding);776Objects.requireNonNull(sourceStream);777if (sourceStream.getFormat().getEncoding().equals(targetEncoding)) {778return sourceStream;779}780781List<FormatConversionProvider> codecs = getFormatConversionProviders();782783for(int i = 0; i < codecs.size(); i++) {784FormatConversionProvider codec = codecs.get(i);785if( codec.isConversionSupported( targetEncoding, sourceStream.getFormat() ) ) {786return codec.getAudioInputStream( targetEncoding, sourceStream );787}788}789// we ran out of options, throw an exception790throw new IllegalArgumentException("Unsupported conversion: " + targetEncoding + " from " + sourceStream.getFormat());791}792793/**794* Obtains the formats that have a particular encoding and that the system795* can obtain from a stream of the specified format using the set of796* installed format converters.797*798* @param targetEncoding the desired encoding after conversion799* @param sourceFormat the audio format before conversion800* @return array of formats. If no formats of the specified encoding are801* supported, an array of length 0 is returned.802* @throws NullPointerException if {@code targetEncoding} or803* {@code sourceFormat} are {@code null}804*/805public static AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) {806Objects.requireNonNull(targetEncoding);807Objects.requireNonNull(sourceFormat);808809List<FormatConversionProvider> codecs = getFormatConversionProviders();810List<AudioFormat> formats = new ArrayList<>();811812boolean matchFound = false;813// gather from all the codecs814for (final FormatConversionProvider codec : codecs) {815AudioFormat[] elements = codec816.getTargetFormats(targetEncoding, sourceFormat);817for (AudioFormat format : elements) {818formats.add(format);819if (sourceFormat.matches(format)) {820matchFound = true;821}822}823}824825if (targetEncoding.equals(sourceFormat.getEncoding())) {826if (!matchFound) {827formats.add(sourceFormat);828}829}830return formats.toArray(new AudioFormat[formats.size()]);831}832833/**834* Indicates whether an audio input stream of a specified format can be835* obtained from an audio input stream of another specified format.836*837* @param targetFormat the desired audio format after conversion838* @param sourceFormat the audio format before conversion839* @return {@code true} if the conversion is supported, otherwise840* {@code false}841* @throws NullPointerException if {@code targetFormat} or842* {@code sourceFormat} are {@code null}843*/844public static boolean isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat) {845Objects.requireNonNull(targetFormat);846Objects.requireNonNull(sourceFormat);847if (sourceFormat.matches(targetFormat)) {848return true;849}850851List<FormatConversionProvider> codecs = getFormatConversionProviders();852853for(int i=0; i<codecs.size(); i++ ) {854FormatConversionProvider codec = codecs.get(i);855if(codec.isConversionSupported(targetFormat, sourceFormat) ) {856return true;857}858}859return false;860}861862/**863* Obtains an audio input stream of the indicated format, by converting the864* provided audio input stream.865*866* @param targetFormat the desired audio format after conversion867* @param sourceStream the stream to be converted868* @return an audio input stream of the indicated format869* @throws IllegalArgumentException if the conversion is not supported870* @throws NullPointerException if {@code targetFormat} or871* {@code sourceStream} are {@code null}872* @see #getTargetEncodings(AudioFormat)873* @see #getTargetFormats(AudioFormat.Encoding, AudioFormat)874* @see #isConversionSupported(AudioFormat, AudioFormat)875* @see #getAudioInputStream(AudioFormat.Encoding, AudioInputStream)876*/877public static AudioInputStream getAudioInputStream(AudioFormat targetFormat,878AudioInputStream sourceStream) {879if (sourceStream.getFormat().matches(targetFormat)) {880return sourceStream;881}882883List<FormatConversionProvider> codecs = getFormatConversionProviders();884885for(int i = 0; i < codecs.size(); i++) {886FormatConversionProvider codec = codecs.get(i);887if(codec.isConversionSupported(targetFormat,sourceStream.getFormat()) ) {888return codec.getAudioInputStream(targetFormat,sourceStream);889}890}891892// we ran out of options...893throw new IllegalArgumentException("Unsupported conversion: " + targetFormat + " from " + sourceStream.getFormat());894}895896/**897* Obtains the audio file format of the provided input stream. The stream898* must point to valid audio file data. The implementation of this method899* may require multiple parsers to examine the stream to determine whether900* they support it. These parsers must be able to mark the stream, read901* enough data to determine whether they support the stream, and reset the902* stream's read pointer to its original position. If the input stream does903* not support these operations, this method may fail with an904* {@code IOException}.905*906* @param stream the input stream from which file format information should907* be extracted908* @return an {@code AudioFileFormat} object describing the stream's audio909* file format910* @throws UnsupportedAudioFileException if the stream does not point to911* valid audio file data recognized by the system912* @throws IOException if an input/output exception occurs913* @throws NullPointerException if {@code stream} is {@code null}914* @see InputStream#markSupported915* @see InputStream#mark916*/917public static AudioFileFormat getAudioFileFormat(final InputStream stream)918throws UnsupportedAudioFileException, IOException {919Objects.requireNonNull(stream);920921for (final AudioFileReader reader : getAudioFileReaders()) {922try {923return reader.getAudioFileFormat(stream);924} catch (final UnsupportedAudioFileException ignored) {925}926}927throw new UnsupportedAudioFileException("Stream of unsupported format");928}929930/**931* Obtains the audio file format of the specified {@code URL}. The932* {@code URL} must point to valid audio file data.933*934* @param url the {@code URL} from which file format information should be935* extracted936* @return an {@code AudioFileFormat} object describing the audio file937* format938* @throws UnsupportedAudioFileException if the {@code URL} does not point939* to valid audio file data recognized by the system940* @throws IOException if an input/output exception occurs941* @throws NullPointerException if {@code url} is {@code null}942*/943public static AudioFileFormat getAudioFileFormat(final URL url)944throws UnsupportedAudioFileException, IOException {945Objects.requireNonNull(url);946947for (final AudioFileReader reader : getAudioFileReaders()) {948try {949return reader.getAudioFileFormat(url);950} catch (final UnsupportedAudioFileException ignored) {951}952}953throw new UnsupportedAudioFileException("URL of unsupported format");954}955956/**957* Obtains the audio file format of the specified {@code File}. The958* {@code File} must point to valid audio file data.959*960* @param file the {@code File} from which file format information should961* be extracted962* @return an {@code AudioFileFormat} object describing the audio file963* format964* @throws UnsupportedAudioFileException if the {@code File} does not point965* to valid audio file data recognized by the system966* @throws IOException if an I/O exception occurs967* @throws NullPointerException if {@code file} is {@code null}968*/969public static AudioFileFormat getAudioFileFormat(final File file)970throws UnsupportedAudioFileException, IOException {971Objects.requireNonNull(file);972973for (final AudioFileReader reader : getAudioFileReaders()) {974try {975return reader.getAudioFileFormat(file);976} catch (final UnsupportedAudioFileException ignored) {977}978}979throw new UnsupportedAudioFileException("File of unsupported format");980}981982/**983* Obtains an audio input stream from the provided input stream. The stream984* must point to valid audio file data. The implementation of this method985* may require multiple parsers to examine the stream to determine whether986* they support it. These parsers must be able to mark the stream, read987* enough data to determine whether they support the stream, and reset the988* stream's read pointer to its original position. If the input stream does989* not support these operation, this method may fail with an990* {@code IOException}.991*992* @param stream the input stream from which the {@code AudioInputStream}993* should be constructed994* @return an {@code AudioInputStream} object based on the audio file data995* contained in the input stream996* @throws UnsupportedAudioFileException if the stream does not point to997* valid audio file data recognized by the system998* @throws IOException if an I/O exception occurs999* @throws NullPointerException if {@code stream} is {@code null}1000* @see InputStream#markSupported1001* @see InputStream#mark1002*/1003public static AudioInputStream getAudioInputStream(final InputStream stream)1004throws UnsupportedAudioFileException, IOException {1005Objects.requireNonNull(stream);10061007for (final AudioFileReader reader : getAudioFileReaders()) {1008try {1009return reader.getAudioInputStream(stream);1010} catch (final UnsupportedAudioFileException ignored) {1011}1012}1013throw new UnsupportedAudioFileException("Stream of unsupported format");1014}10151016/**1017* Obtains an audio input stream from the {@code URL} provided. The1018* {@code URL} must point to valid audio file data.1019*1020* @param url the {@code URL} for which the {@code AudioInputStream} should1021* be constructed1022* @return an {@code AudioInputStream} object based on the audio file data1023* pointed to by the {@code URL}1024* @throws UnsupportedAudioFileException if the {@code URL} does not point1025* to valid audio file data recognized by the system1026* @throws IOException if an I/O exception occurs1027* @throws NullPointerException if {@code url} is {@code null}1028*/1029public static AudioInputStream getAudioInputStream(final URL url)1030throws UnsupportedAudioFileException, IOException {1031Objects.requireNonNull(url);10321033for (final AudioFileReader reader : getAudioFileReaders()) {1034try {1035return reader.getAudioInputStream(url);1036} catch (final UnsupportedAudioFileException ignored) {1037}1038}1039throw new UnsupportedAudioFileException("URL of unsupported format");1040}10411042/**1043* Obtains an audio input stream from the provided {@code File}. The1044* {@code File} must point to valid audio file data.1045*1046* @param file the {@code File} for which the {@code AudioInputStream}1047* should be constructed1048* @return an {@code AudioInputStream} object based on the audio file data1049* pointed to by the {@code File}1050* @throws UnsupportedAudioFileException if the {@code File} does not point1051* to valid audio file data recognized by the system1052* @throws IOException if an I/O exception occurs1053* @throws NullPointerException if {@code file} is {@code null}1054*/1055public static AudioInputStream getAudioInputStream(final File file)1056throws UnsupportedAudioFileException, IOException {1057Objects.requireNonNull(file);10581059for (final AudioFileReader reader : getAudioFileReaders()) {1060try {1061return reader.getAudioInputStream(file);1062} catch (final UnsupportedAudioFileException ignored) {1063}1064}1065throw new UnsupportedAudioFileException("File of unsupported format");1066}10671068/**1069* Obtains the file types for which file writing support is provided by the1070* system.1071*1072* @return array of unique file types. If no file types are supported, an1073* array of length 0 is returned.1074*/1075public static AudioFileFormat.Type[] getAudioFileTypes() {1076List<AudioFileWriter> providers = getAudioFileWriters();1077Set<AudioFileFormat.Type> returnTypesSet = new HashSet<>();10781079for(int i=0; i < providers.size(); i++) {1080AudioFileWriter writer = providers.get(i);1081AudioFileFormat.Type[] fileTypes = writer.getAudioFileTypes();1082for(int j=0; j < fileTypes.length; j++) {1083returnTypesSet.add(fileTypes[j]);1084}1085}1086AudioFileFormat.Type[] returnTypes =1087returnTypesSet.toArray(new AudioFileFormat.Type[0]);1088return returnTypes;1089}10901091/**1092* Indicates whether file writing support for the specified file type is1093* provided by the system.1094*1095* @param fileType the file type for which write capabilities are queried1096* @return {@code true} if the file type is supported, otherwise1097* {@code false}1098* @throws NullPointerException if {@code fileType} is {@code null}1099*/1100public static boolean isFileTypeSupported(AudioFileFormat.Type fileType) {1101Objects.requireNonNull(fileType);1102List<AudioFileWriter> providers = getAudioFileWriters();11031104for(int i=0; i < providers.size(); i++) {1105AudioFileWriter writer = providers.get(i);1106if (writer.isFileTypeSupported(fileType)) {1107return true;1108}1109}1110return false;1111}11121113/**1114* Obtains the file types that the system can write from the audio input1115* stream specified.1116*1117* @param stream the audio input stream for which audio file type support1118* is queried1119* @return array of file types. If no file types are supported, an array of1120* length 0 is returned.1121* @throws NullPointerException if {@code stream} is {@code null}1122*/1123public static AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) {1124Objects.requireNonNull(stream);1125List<AudioFileWriter> providers = getAudioFileWriters();1126Set<AudioFileFormat.Type> returnTypesSet = new HashSet<>();11271128for(int i=0; i < providers.size(); i++) {1129AudioFileWriter writer = providers.get(i);1130AudioFileFormat.Type[] fileTypes = writer.getAudioFileTypes(stream);1131for(int j=0; j < fileTypes.length; j++) {1132returnTypesSet.add(fileTypes[j]);1133}1134}1135AudioFileFormat.Type[] returnTypes =1136returnTypesSet.toArray(new AudioFileFormat.Type[0]);1137return returnTypes;1138}11391140/**1141* Indicates whether an audio file of the specified file type can be written1142* from the indicated audio input stream.1143*1144* @param fileType the file type for which write capabilities are queried1145* @param stream the stream for which file-writing support is queried1146* @return {@code true} if the file type is supported for this audio input1147* stream, otherwise {@code false}1148* @throws NullPointerException if {@code fileType} or {@code stream} are1149* {@code null}1150*/1151public static boolean isFileTypeSupported(AudioFileFormat.Type fileType,1152AudioInputStream stream) {1153Objects.requireNonNull(fileType);1154Objects.requireNonNull(stream);1155List<AudioFileWriter> providers = getAudioFileWriters();11561157for(int i=0; i < providers.size(); i++) {1158AudioFileWriter writer = providers.get(i);1159if(writer.isFileTypeSupported(fileType, stream)) {1160return true;1161}1162}1163return false;1164}11651166/**1167* Writes a stream of bytes representing an audio file of the specified file1168* type to the output stream provided. Some file types require that the1169* length be written into the file header; such files cannot be written from1170* start to finish unless the length is known in advance. An attempt to1171* write a file of such a type will fail with an {@code IOException} if the1172* length in the audio file type is {@code AudioSystem.NOT_SPECIFIED}.1173*1174* @param stream the audio input stream containing audio data to be written1175* to the file1176* @param fileType the kind of audio file to write1177* @param out the stream to which the file data should be written1178* @return the number of bytes written to the output stream1179* @throws IOException if an input/output exception occurs1180* @throws IllegalArgumentException if the file type is not supported by the1181* system1182* @throws NullPointerException if {@code stream} or {@code fileType} or1183* {@code out} are {@code null}1184* @see #isFileTypeSupported1185* @see #getAudioFileTypes1186*/1187public static int write(final AudioInputStream stream,1188final AudioFileFormat.Type fileType,1189final OutputStream out) throws IOException {1190Objects.requireNonNull(stream);1191Objects.requireNonNull(fileType);1192Objects.requireNonNull(out);11931194for (final AudioFileWriter writer : getAudioFileWriters()) {1195try {1196return writer.write(stream, fileType, out);1197} catch (final IllegalArgumentException ignored) {1198// thrown if this provider cannot write the stream, try next1199}1200}1201// "File type " + type + " not supported."1202throw new IllegalArgumentException(1203"could not write audio file: file type not supported: "1204+ fileType);1205}12061207/**1208* Writes a stream of bytes representing an audio file of the specified file1209* type to the external file provided.1210*1211* @param stream the audio input stream containing audio data to be written1212* to the file1213* @param fileType the kind of audio file to write1214* @param out the external file to which the file data should be written1215* @return the number of bytes written to the file1216* @throws IOException if an I/O exception occurs1217* @throws IllegalArgumentException if the file type is not supported by the1218* system1219* @throws NullPointerException if {@code stream} or {@code fileType} or1220* {@code out} are {@code null}1221* @see #isFileTypeSupported1222* @see #getAudioFileTypes1223*/1224public static int write(final AudioInputStream stream,1225final AudioFileFormat.Type fileType,1226final File out) throws IOException {1227Objects.requireNonNull(stream);1228Objects.requireNonNull(fileType);1229Objects.requireNonNull(out);12301231for (final AudioFileWriter writer : getAudioFileWriters()) {1232try {1233return writer.write(stream, fileType, out);1234} catch (final IllegalArgumentException ignored) {1235// thrown if this provider cannot write the stream, try next1236}1237}1238throw new IllegalArgumentException(1239"could not write audio file: file type not supported: "1240+ fileType);1241}12421243// METHODS FOR INTERNAL IMPLEMENTATION USE12441245/**1246* Obtains the list of MixerProviders currently installed on the system.1247*1248* @return the list of MixerProviders currently installed on the system1249*/1250@SuppressWarnings("unchecked")1251private static List<MixerProvider> getMixerProviders() {1252return (List<MixerProvider>) getProviders(MixerProvider.class);1253}12541255/**1256* Obtains the set of format converters (codecs, transcoders, etc.) that are1257* currently installed on the system.1258*1259* @return an array of {@link FormatConversionProvider} objects representing1260* the available format converters. If no format converters readers1261* are available on the system, an array of length 0 is returned.1262*/1263@SuppressWarnings("unchecked")1264private static List<FormatConversionProvider> getFormatConversionProviders() {1265return (List<FormatConversionProvider>) getProviders(FormatConversionProvider.class);1266}12671268/**1269* Obtains the set of audio file readers that are currently installed on the1270* system.1271*1272* @return a List of {@link AudioFileReader} objects representing the1273* installed audio file readers. If no audio file readers are1274* available on the system, an empty List is returned.1275*/1276@SuppressWarnings("unchecked")1277private static List<AudioFileReader> getAudioFileReaders() {1278return (List<AudioFileReader>)getProviders(AudioFileReader.class);1279}12801281/**1282* Obtains the set of audio file writers that are currently installed on the1283* system.1284*1285* @return a List of {@link AudioFileWriter} objects representing the1286* available audio file writers. If no audio file writers are1287* available on the system, an empty List is returned.1288*/1289@SuppressWarnings("unchecked")1290private static List<AudioFileWriter> getAudioFileWriters() {1291return (List<AudioFileWriter>)getProviders(AudioFileWriter.class);1292}12931294/**1295* Attempts to locate and return a default Mixer that provides lines of the1296* specified type.1297*1298* @param providers the installed mixer providers1299* @param info The requested line type TargetDataLine.class, Clip.class or1300* Port.class1301* @return a Mixer that matches the requirements, or null if no default1302* mixer found1303*/1304private static Mixer getDefaultMixer(List<MixerProvider> providers, Line.Info info) {1305Class<?> lineClass = info.getLineClass();1306String providerClassName = JDK13Services.getDefaultProviderClassName(lineClass);1307String instanceName = JDK13Services.getDefaultInstanceName(lineClass);1308Mixer mixer;13091310if (providerClassName != null) {1311MixerProvider defaultProvider = getNamedProvider(providerClassName, providers);1312if (defaultProvider != null) {1313if (instanceName != null) {1314mixer = getNamedMixer(instanceName, defaultProvider, info);1315if (mixer != null) {1316return mixer;1317}1318} else {1319mixer = getFirstMixer(defaultProvider, info,1320false /* mixing not required*/);1321if (mixer != null) {1322return mixer;1323}1324}13251326}1327}13281329/*1330* - Provider class not specified, or1331* - provider class cannot be found, or1332* - provider class and instance specified and instance cannot be found1333* or is not appropriate1334*/1335if (instanceName != null) {1336mixer = getNamedMixer(instanceName, providers, info);1337if (mixer != null) {1338return mixer;1339}1340}134113421343/*1344* No defaults are specified, or if something is specified, everything1345* failed1346*/1347return null;1348}13491350/**1351* Return a MixerProvider of a given class from the list of MixerProviders.1352* This method never requires the returned Mixer to do mixing.1353*1354* @param providerClassName The class name of the provider to be returned1355* @param providers The list of MixerProviders that is searched1356* @return A MixerProvider of the requested class, or null if none is found1357*/1358private static MixerProvider getNamedProvider(String providerClassName,1359List<MixerProvider> providers) {1360for(int i = 0; i < providers.size(); i++) {1361MixerProvider provider = providers.get(i);1362if (provider.getClass().getName().equals(providerClassName)) {1363return provider;1364}1365}1366return null;1367}13681369/**1370* Return a Mixer with a given name from a given MixerProvider. This method1371* never requires the returned Mixer to do mixing.1372*1373* @param mixerName The name of the Mixer to be returned1374* @param provider The MixerProvider to check for Mixers1375* @param info The type of line the returned Mixer is required to support1376* @return A Mixer matching the requirements, or null if none is found1377*/1378private static Mixer getNamedMixer(String mixerName,1379MixerProvider provider,1380Line.Info info) {1381Mixer.Info[] infos = provider.getMixerInfo();1382for (int i = 0; i < infos.length; i++) {1383if (infos[i].getName().equals(mixerName)) {1384Mixer mixer = provider.getMixer(infos[i]);1385if (isAppropriateMixer(mixer, info, false)) {1386return mixer;1387}1388}1389}1390return null;1391}13921393/**1394* From a List of MixerProviders, return a Mixer with a given name. This1395* method never requires the returned Mixer to do mixing.1396*1397* @param mixerName The name of the Mixer to be returned1398* @param providers The List of MixerProviders to check for Mixers1399* @param info The type of line the returned Mixer is required to support1400* @return A Mixer matching the requirements, or null if none is found1401*/1402private static Mixer getNamedMixer(String mixerName,1403List<MixerProvider> providers,1404Line.Info info) {1405for(int i = 0; i < providers.size(); i++) {1406MixerProvider provider = providers.get(i);1407Mixer mixer = getNamedMixer(mixerName, provider, info);1408if (mixer != null) {1409return mixer;1410}1411}1412return null;1413}14141415/**1416* From a given MixerProvider, return the first appropriate Mixer.1417*1418* @param provider The MixerProvider to check for Mixers1419* @param info The type of line the returned Mixer is required to support1420* @param isMixingRequired If true, only Mixers that support mixing are1421* returned for line types of SourceDataLine and Clip1422* @return A Mixer that is considered appropriate, or null if none is found1423*/1424private static Mixer getFirstMixer(MixerProvider provider,1425Line.Info info,1426boolean isMixingRequired) {1427Mixer.Info[] infos = provider.getMixerInfo();1428for (int j = 0; j < infos.length; j++) {1429Mixer mixer = provider.getMixer(infos[j]);1430if (isAppropriateMixer(mixer, info, isMixingRequired)) {1431return mixer;1432}1433}1434return null;1435}14361437/**1438* Checks if a Mixer is appropriate. A Mixer is considered appropriate if it1439* support the given line type. If isMixingRequired is {@code true} and the1440* line type is an output one (SourceDataLine, Clip), the mixer is1441* appropriate if it supports at least 2 (concurrent) lines of the given1442* type.1443*1444* @param mixer The mixer to check1445* @param lineInfo The line to check1446* @param isMixingRequired Is the mixing required or not1447* @return {@code true} if the mixer is considered appropriate according to1448* the rules given above, {@code false} otherwise1449*/1450private static boolean isAppropriateMixer(Mixer mixer,1451Line.Info lineInfo,1452boolean isMixingRequired) {1453if (! mixer.isLineSupported(lineInfo)) {1454return false;1455}1456Class<?> lineClass = lineInfo.getLineClass();1457if (isMixingRequired1458&& (SourceDataLine.class.isAssignableFrom(lineClass) ||1459Clip.class.isAssignableFrom(lineClass))) {1460int maxLines = mixer.getMaxLines(lineInfo);1461return ((maxLines == NOT_SPECIFIED) || (maxLines > 1));1462}1463return true;1464}14651466/**1467* Like getMixerInfo, but return List.1468*1469* @return a List of info objects for the currently installed mixers. If no1470* mixers are available on the system, an empty List is returned.1471* @see #getMixerInfo()1472*/1473private static List<Mixer.Info> getMixerInfoList() {1474List<MixerProvider> providers = getMixerProviders();1475return getMixerInfoList(providers);1476}14771478/**1479* Like getMixerInfo, but return List.1480*1481* @param providers The list of MixerProviders1482* @return a List of info objects for the currently installed mixers. If no1483* mixers are available on the system, an empty List is returned.1484* @see #getMixerInfo()1485*/1486private static List<Mixer.Info> getMixerInfoList(List<MixerProvider> providers) {1487List<Mixer.Info> infos = new ArrayList<>();14881489Mixer.Info[] someInfos; // per-mixer1490Mixer.Info[] allInfos; // for all mixers14911492for(int i = 0; i < providers.size(); i++ ) {1493someInfos = providers.get(i).getMixerInfo();14941495for (int j = 0; j < someInfos.length; j++) {1496infos.add(someInfos[j]);1497}1498}14991500return infos;1501}15021503/**1504* Obtains the set of services currently installed on the system using the1505* SPI mechanism in 1.3.1506*1507* @param providerClass The type of providers requested. This should be one1508* of AudioFileReader.class, AudioFileWriter.class,1509* FormatConversionProvider.class, MixerProvider.class,1510* MidiDeviceProvider.class, MidiFileReader.class,1511* MidiFileWriter.class or SoundbankReader.class.1512* @return a List of instances of providers for the requested service. If no1513* providers are available, a vector of length 0 will be returned.1514*/1515private static List<?> getProviders(Class<?> providerClass) {1516return JDK13Services.getProviders(providerClass);1517}1518}151915201521