1 /*
2  * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.media.sound;
27 
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Objects;
33 
34 import javax.sound.sampled.AudioFormat;
35 import javax.sound.sampled.AudioFormat.Encoding;
36 import javax.sound.sampled.AudioInputStream;
37 import javax.sound.sampled.AudioSystem;
38 import javax.sound.sampled.spi.FormatConversionProvider;
39 
40 /**
41  * This class is used to convert between 8,16,24,32 bit signed/unsigned
42  * big/litle endian fixed/floating stereo/mono/multi-channel audio streams and
43  * perform sample-rate conversion if needed.
44  *
45  * @author Karl Helgason
46  */
47 public final class AudioFloatFormatConverter extends FormatConversionProvider {
48 
49     private static class AudioFloatFormatConverterInputStream extends
50             InputStream {
51         private final AudioFloatConverter converter;
52 
53         private final AudioFloatInputStream stream;
54 
55         private float[] readfloatbuffer;
56 
57         private final int fsize;
58 
AudioFloatFormatConverterInputStream(AudioFormat targetFormat, AudioFloatInputStream stream)59         AudioFloatFormatConverterInputStream(AudioFormat targetFormat,
60                 AudioFloatInputStream stream) {
61             this.stream = stream;
62             converter = AudioFloatConverter.getConverter(targetFormat);
63             fsize = ((targetFormat.getSampleSizeInBits() + 7) / 8);
64         }
65 
66         @Override
read()67         public int read() throws IOException {
68             byte[] b = new byte[1];
69             int ret = read(b);
70             if (ret < 0)
71                 return ret;
72             return b[0] & 0xFF;
73         }
74 
75         @Override
read(byte[] b, int off, int len)76         public int read(byte[] b, int off, int len) throws IOException {
77 
78             int flen = len / fsize;
79             if (readfloatbuffer == null || readfloatbuffer.length < flen)
80                 readfloatbuffer = new float[flen];
81             int ret = stream.read(readfloatbuffer, 0, flen);
82             if (ret < 0)
83                 return ret;
84             converter.toByteArray(readfloatbuffer, 0, ret, b, off);
85             return ret * fsize;
86         }
87 
88         @Override
available()89         public int available() throws IOException {
90             int ret = stream.available();
91             if (ret < 0)
92                 return ret;
93             return ret * fsize;
94         }
95 
96         @Override
close()97         public void close() throws IOException {
98             stream.close();
99         }
100 
101         @Override
mark(int readlimit)102         public synchronized void mark(int readlimit) {
103             stream.mark(readlimit * fsize);
104         }
105 
106         @Override
markSupported()107         public boolean markSupported() {
108             return stream.markSupported();
109         }
110 
111         @Override
reset()112         public synchronized void reset() throws IOException {
113             stream.reset();
114         }
115 
116         @Override
skip(long n)117         public long skip(long n) throws IOException {
118             long ret = stream.skip(n / fsize);
119             if (ret < 0)
120                 return ret;
121             return ret * fsize;
122         }
123 
124     }
125 
126     private static class AudioFloatInputStreamChannelMixer extends
127             AudioFloatInputStream {
128 
129         private final int targetChannels;
130 
131         private final int sourceChannels;
132 
133         private final AudioFloatInputStream ais;
134 
135         private final AudioFormat targetFormat;
136 
137         private float[] conversion_buffer;
138 
AudioFloatInputStreamChannelMixer(AudioFloatInputStream ais, int targetChannels)139         AudioFloatInputStreamChannelMixer(AudioFloatInputStream ais,
140                 int targetChannels) {
141             this.sourceChannels = ais.getFormat().getChannels();
142             this.targetChannels = targetChannels;
143             this.ais = ais;
144             AudioFormat format = ais.getFormat();
145             targetFormat = new AudioFormat(format.getEncoding(), format
146                     .getSampleRate(), format.getSampleSizeInBits(),
147                     targetChannels, (format.getFrameSize() / sourceChannels)
148                             * targetChannels, format.getFrameRate(), format
149                             .isBigEndian());
150         }
151 
152         @Override
available()153         public int available() throws IOException {
154             return (ais.available() / sourceChannels) * targetChannels;
155         }
156 
157         @Override
close()158         public void close() throws IOException {
159             ais.close();
160         }
161 
162         @Override
getFormat()163         public AudioFormat getFormat() {
164             return targetFormat;
165         }
166 
167         @Override
getFrameLength()168         public long getFrameLength() {
169             return ais.getFrameLength();
170         }
171 
172         @Override
mark(int readlimit)173         public void mark(int readlimit) {
174             ais.mark((readlimit / targetChannels) * sourceChannels);
175         }
176 
177         @Override
markSupported()178         public boolean markSupported() {
179             return ais.markSupported();
180         }
181 
182         @Override
read(float[] b, int off, int len)183         public int read(float[] b, int off, int len) throws IOException {
184             int len2 = (len / targetChannels) * sourceChannels;
185             if (conversion_buffer == null || conversion_buffer.length < len2)
186                 conversion_buffer = new float[len2];
187             int ret = ais.read(conversion_buffer, 0, len2);
188             if (ret < 0)
189                 return ret;
190             if (sourceChannels == 1) {
191                 int cs = targetChannels;
192                 for (int c = 0; c < targetChannels; c++) {
193                     for (int i = 0, ix = off + c; i < len2; i++, ix += cs) {
194                         b[ix] = conversion_buffer[i];
195                     }
196                 }
197             } else if (targetChannels == 1) {
198                 int cs = sourceChannels;
199                 for (int i = 0, ix = off; i < len2; i += cs, ix++) {
200                     b[ix] = conversion_buffer[i];
201                 }
202                 for (int c = 1; c < sourceChannels; c++) {
203                     for (int i = c, ix = off; i < len2; i += cs, ix++) {
204                         b[ix] += conversion_buffer[i];
205                     }
206                 }
207                 float vol = 1f / ((float) sourceChannels);
208                 for (int i = 0, ix = off; i < len2; i += cs, ix++) {
209                     b[ix] *= vol;
210                 }
211             } else {
212                 int minChannels = Math.min(sourceChannels, targetChannels);
213                 int off_len = off + len;
214                 int ct = targetChannels;
215                 int cs = sourceChannels;
216                 for (int c = 0; c < minChannels; c++) {
217                     for (int i = off + c, ix = c; i < off_len; i += ct, ix += cs) {
218                         b[i] = conversion_buffer[ix];
219                     }
220                 }
221                 for (int c = minChannels; c < targetChannels; c++) {
222                     for (int i = off + c; i < off_len; i += ct) {
223                         b[i] = 0;
224                     }
225                 }
226             }
227             return (ret / sourceChannels) * targetChannels;
228         }
229 
230         @Override
reset()231         public void reset() throws IOException {
232             ais.reset();
233         }
234 
235         @Override
skip(long len)236         public long skip(long len) throws IOException {
237             long ret = ais.skip((len / targetChannels) * sourceChannels);
238             if (ret < 0)
239                 return ret;
240             return (ret / sourceChannels) * targetChannels;
241         }
242 
243     }
244 
245     private static class AudioFloatInputStreamResampler extends
246             AudioFloatInputStream {
247 
248         private final AudioFloatInputStream ais;
249 
250         private final AudioFormat targetFormat;
251 
252         private float[] skipbuffer;
253 
254         private SoftAbstractResampler resampler;
255 
256         private final float[] pitch = new float[1];
257 
258         private final float[] ibuffer2;
259 
260         private final float[][] ibuffer;
261 
262         private float ibuffer_index = 0;
263 
264         private int ibuffer_len = 0;
265 
266         private final int nrofchannels;
267 
268         private float[][] cbuffer;
269 
270         private final int buffer_len = 512;
271 
272         private final int pad;
273 
274         private final int pad2;
275 
276         private final float[] ix = new float[1];
277 
278         private final int[] ox = new int[1];
279 
280         private float[][] mark_ibuffer = null;
281 
282         private float mark_ibuffer_index = 0;
283 
284         private int mark_ibuffer_len = 0;
285 
AudioFloatInputStreamResampler(AudioFloatInputStream ais, AudioFormat format)286         AudioFloatInputStreamResampler(AudioFloatInputStream ais,
287                 AudioFormat format) {
288             this.ais = ais;
289             AudioFormat sourceFormat = ais.getFormat();
290             targetFormat = new AudioFormat(sourceFormat.getEncoding(), format
291                     .getSampleRate(), sourceFormat.getSampleSizeInBits(),
292                     sourceFormat.getChannels(), sourceFormat.getFrameSize(),
293                     format.getSampleRate(), sourceFormat.isBigEndian());
294             nrofchannels = targetFormat.getChannels();
295             Object interpolation = format.getProperty("interpolation");
296             if (interpolation != null && (interpolation instanceof String)) {
297                 String resamplerType = (String) interpolation;
298                 if (resamplerType.equalsIgnoreCase("point"))
299                     this.resampler = new SoftPointResampler();
300                 if (resamplerType.equalsIgnoreCase("linear"))
301                     this.resampler = new SoftLinearResampler2();
302                 if (resamplerType.equalsIgnoreCase("linear1"))
303                     this.resampler = new SoftLinearResampler();
304                 if (resamplerType.equalsIgnoreCase("linear2"))
305                     this.resampler = new SoftLinearResampler2();
306                 if (resamplerType.equalsIgnoreCase("cubic"))
307                     this.resampler = new SoftCubicResampler();
308                 if (resamplerType.equalsIgnoreCase("lanczos"))
309                     this.resampler = new SoftLanczosResampler();
310                 if (resamplerType.equalsIgnoreCase("sinc"))
311                     this.resampler = new SoftSincResampler();
312             }
313             if (resampler == null)
314                 resampler = new SoftLinearResampler2(); // new
315                                                         // SoftLinearResampler2();
316             pitch[0] = sourceFormat.getSampleRate() / format.getSampleRate();
317             pad = resampler.getPadding();
318             pad2 = pad * 2;
319             ibuffer = new float[nrofchannels][buffer_len + pad2];
320             ibuffer2 = new float[nrofchannels * buffer_len];
321             ibuffer_index = buffer_len + pad;
322             ibuffer_len = buffer_len;
323         }
324 
325         @Override
available()326         public int available() throws IOException {
327             return 0;
328         }
329 
330         @Override
close()331         public void close() throws IOException {
332             ais.close();
333         }
334 
335         @Override
getFormat()336         public AudioFormat getFormat() {
337             return targetFormat;
338         }
339 
340         @Override
getFrameLength()341         public long getFrameLength() {
342             return AudioSystem.NOT_SPECIFIED; // ais.getFrameLength();
343         }
344 
345         @Override
mark(int readlimit)346         public void mark(int readlimit) {
347             ais.mark((int) (readlimit * pitch[0]));
348             mark_ibuffer_index = ibuffer_index;
349             mark_ibuffer_len = ibuffer_len;
350             if (mark_ibuffer == null) {
351                 mark_ibuffer = new float[ibuffer.length][ibuffer[0].length];
352             }
353             for (int c = 0; c < ibuffer.length; c++) {
354                 float[] from = ibuffer[c];
355                 float[] to = mark_ibuffer[c];
356                 for (int i = 0; i < to.length; i++) {
357                     to[i] = from[i];
358                 }
359             }
360         }
361 
362         @Override
markSupported()363         public boolean markSupported() {
364             return ais.markSupported();
365         }
366 
readNextBuffer()367         private void readNextBuffer() throws IOException {
368 
369             if (ibuffer_len == -1)
370                 return;
371 
372             for (int c = 0; c < nrofchannels; c++) {
373                 float[] buff = ibuffer[c];
374                 int buffer_len_pad = ibuffer_len + pad2;
375                 for (int i = ibuffer_len, ix = 0; i < buffer_len_pad; i++, ix++) {
376                     buff[ix] = buff[i];
377                 }
378             }
379 
380             ibuffer_index -= (ibuffer_len);
381 
382             ibuffer_len = ais.read(ibuffer2);
383             if (ibuffer_len >= 0) {
384                 while (ibuffer_len < ibuffer2.length) {
385                     int ret = ais.read(ibuffer2, ibuffer_len, ibuffer2.length
386                             - ibuffer_len);
387                     if (ret == -1)
388                         break;
389                     ibuffer_len += ret;
390                 }
391                 Arrays.fill(ibuffer2, ibuffer_len, ibuffer2.length, 0);
392                 ibuffer_len /= nrofchannels;
393             } else {
394                 Arrays.fill(ibuffer2, 0, ibuffer2.length, 0);
395             }
396 
397             int ibuffer2_len = ibuffer2.length;
398             for (int c = 0; c < nrofchannels; c++) {
399                 float[] buff = ibuffer[c];
400                 for (int i = c, ix = pad2; i < ibuffer2_len; i += nrofchannels, ix++) {
401                     buff[ix] = ibuffer2[i];
402                 }
403             }
404 
405         }
406 
407         @Override
read(float[] b, int off, int len)408         public int read(float[] b, int off, int len) throws IOException {
409 
410             if (cbuffer == null || cbuffer[0].length < len / nrofchannels) {
411                 cbuffer = new float[nrofchannels][len / nrofchannels];
412             }
413             if (ibuffer_len == -1)
414                 return -1;
415             if (len < 0)
416                 return 0;
417             int offlen = off + len;
418             int remain = len / nrofchannels;
419             int destPos = 0;
420             int in_end = ibuffer_len;
421             while (remain > 0) {
422                 if (ibuffer_len >= 0) {
423                     if (ibuffer_index >= (ibuffer_len + pad))
424                         readNextBuffer();
425                     in_end = ibuffer_len + pad;
426                 }
427 
428                 if (ibuffer_len < 0) {
429                     in_end = pad2;
430                     if (ibuffer_index >= in_end)
431                         break;
432                 }
433 
434                 if (ibuffer_index < 0)
435                     break;
436                 int preDestPos = destPos;
437                 for (int c = 0; c < nrofchannels; c++) {
438                     ix[0] = ibuffer_index;
439                     ox[0] = destPos;
440                     float[] buff = ibuffer[c];
441                     resampler.interpolate(buff, ix, in_end, pitch, 0,
442                             cbuffer[c], ox, len / nrofchannels);
443                 }
444                 ibuffer_index = ix[0];
445                 destPos = ox[0];
446                 remain -= destPos - preDestPos;
447             }
448             for (int c = 0; c < nrofchannels; c++) {
449                 int ix = 0;
450                 float[] buff = cbuffer[c];
451                 for (int i = c + off; i < offlen; i += nrofchannels) {
452                     b[i] = buff[ix++];
453                 }
454             }
455             return len - remain * nrofchannels;
456         }
457 
458         @Override
reset()459         public void reset() throws IOException {
460             ais.reset();
461             if (mark_ibuffer == null)
462                 return;
463             ibuffer_index = mark_ibuffer_index;
464             ibuffer_len = mark_ibuffer_len;
465             for (int c = 0; c < ibuffer.length; c++) {
466                 float[] from = mark_ibuffer[c];
467                 float[] to = ibuffer[c];
468                 for (int i = 0; i < to.length; i++) {
469                     to[i] = from[i];
470                 }
471             }
472 
473         }
474 
475         @Override
skip(long len)476         public long skip(long len) throws IOException {
477             if (len < 0)
478                 return 0;
479             if (skipbuffer == null)
480                 skipbuffer = new float[1024 * targetFormat.getFrameSize()];
481             float[] l_skipbuffer = skipbuffer;
482             long remain = len;
483             while (remain > 0) {
484                 int ret = read(l_skipbuffer, 0, (int) Math.min(remain,
485                         skipbuffer.length));
486                 if (ret < 0) {
487                     if (remain == len)
488                         return ret;
489                     break;
490                 }
491                 remain -= ret;
492             }
493             return len - remain;
494 
495         }
496 
497     }
498 
499     private final Encoding[] formats = {Encoding.PCM_SIGNED,
500                                         Encoding.PCM_UNSIGNED,
501                                         Encoding.PCM_FLOAT};
502 
503     @Override
getAudioInputStream(Encoding targetEncoding, AudioInputStream sourceStream)504     public AudioInputStream getAudioInputStream(Encoding targetEncoding,
505                                                 AudioInputStream sourceStream) {
506         if (!isConversionSupported(targetEncoding, sourceStream.getFormat())) {
507             throw new IllegalArgumentException(
508                     "Unsupported conversion: " + sourceStream.getFormat()
509                             .toString() + " to " + targetEncoding.toString());
510         }
511         if (sourceStream.getFormat().getEncoding().equals(targetEncoding))
512             return sourceStream;
513         AudioFormat format = sourceStream.getFormat();
514         int channels = format.getChannels();
515         Encoding encoding = targetEncoding;
516         float samplerate = format.getSampleRate();
517         int bits = format.getSampleSizeInBits();
518         boolean bigendian = format.isBigEndian();
519         if (targetEncoding.equals(Encoding.PCM_FLOAT))
520             bits = 32;
521         AudioFormat targetFormat = new AudioFormat(encoding, samplerate, bits,
522                 channels, channels * bits / 8, samplerate, bigendian);
523         return getAudioInputStream(targetFormat, sourceStream);
524     }
525 
526     @Override
getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream)527     public AudioInputStream getAudioInputStream(AudioFormat targetFormat,
528                                                 AudioInputStream sourceStream) {
529         if (!isConversionSupported(targetFormat, sourceStream.getFormat()))
530             throw new IllegalArgumentException("Unsupported conversion: "
531                     + sourceStream.getFormat().toString() + " to "
532                     + targetFormat.toString());
533         return getAudioInputStream(targetFormat, AudioFloatInputStream
534                 .getInputStream(sourceStream));
535     }
536 
getAudioInputStream(AudioFormat targetFormat, AudioFloatInputStream sourceStream)537     public AudioInputStream getAudioInputStream(AudioFormat targetFormat,
538             AudioFloatInputStream sourceStream) {
539 
540         if (!isConversionSupported(targetFormat, sourceStream.getFormat()))
541             throw new IllegalArgumentException("Unsupported conversion: "
542                     + sourceStream.getFormat().toString() + " to "
543                     + targetFormat.toString());
544         if (targetFormat.getChannels() != sourceStream.getFormat()
545                 .getChannels())
546             sourceStream = new AudioFloatInputStreamChannelMixer(sourceStream,
547                     targetFormat.getChannels());
548         if (Math.abs(targetFormat.getSampleRate()
549                 - sourceStream.getFormat().getSampleRate()) > 0.000001)
550             sourceStream = new AudioFloatInputStreamResampler(sourceStream,
551                     targetFormat);
552         return new AudioInputStream(new AudioFloatFormatConverterInputStream(
553                 targetFormat, sourceStream), targetFormat, sourceStream
554                 .getFrameLength());
555     }
556 
557     @Override
getSourceEncodings()558     public Encoding[] getSourceEncodings() {
559         return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
560                 Encoding.PCM_FLOAT };
561     }
562 
563     @Override
getTargetEncodings()564     public Encoding[] getTargetEncodings() {
565         return getSourceEncodings();
566     }
567 
568     @Override
getTargetEncodings(AudioFormat sourceFormat)569     public Encoding[] getTargetEncodings(AudioFormat sourceFormat) {
570         if (AudioFloatConverter.getConverter(sourceFormat) == null)
571             return new Encoding[0];
572         return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
573                 Encoding.PCM_FLOAT };
574     }
575 
576     @Override
getTargetFormats(Encoding targetEncoding, AudioFormat sourceFormat)577     public AudioFormat[] getTargetFormats(Encoding targetEncoding,
578                                           AudioFormat sourceFormat) {
579         Objects.requireNonNull(targetEncoding);
580         if (AudioFloatConverter.getConverter(sourceFormat) == null)
581             return new AudioFormat[0];
582         int channels = sourceFormat.getChannels();
583 
584         ArrayList<AudioFormat> formats = new ArrayList<>();
585 
586         if (targetEncoding.equals(Encoding.PCM_SIGNED))
587             formats.add(new AudioFormat(Encoding.PCM_SIGNED,
588                     AudioSystem.NOT_SPECIFIED, 8, channels, channels,
589                     AudioSystem.NOT_SPECIFIED, false));
590         if (targetEncoding.equals(Encoding.PCM_UNSIGNED))
591             formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
592                     AudioSystem.NOT_SPECIFIED, 8, channels, channels,
593                     AudioSystem.NOT_SPECIFIED, false));
594 
595         for (int bits = 16; bits < 32; bits += 8) {
596             if (targetEncoding.equals(Encoding.PCM_SIGNED)) {
597                 formats.add(new AudioFormat(Encoding.PCM_SIGNED,
598                         AudioSystem.NOT_SPECIFIED, bits, channels, channels
599                                 * bits / 8, AudioSystem.NOT_SPECIFIED, false));
600                 formats.add(new AudioFormat(Encoding.PCM_SIGNED,
601                         AudioSystem.NOT_SPECIFIED, bits, channels, channels
602                                 * bits / 8, AudioSystem.NOT_SPECIFIED, true));
603             }
604             if (targetEncoding.equals(Encoding.PCM_UNSIGNED)) {
605                 formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
606                         AudioSystem.NOT_SPECIFIED, bits, channels, channels
607                                 * bits / 8, AudioSystem.NOT_SPECIFIED, true));
608                 formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
609                         AudioSystem.NOT_SPECIFIED, bits, channels, channels
610                                 * bits / 8, AudioSystem.NOT_SPECIFIED, false));
611             }
612         }
613 
614         if (targetEncoding.equals(Encoding.PCM_FLOAT)) {
615             formats.add(new AudioFormat(Encoding.PCM_FLOAT,
616                     AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4,
617                     AudioSystem.NOT_SPECIFIED, false));
618             formats.add(new AudioFormat(Encoding.PCM_FLOAT,
619                     AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4,
620                     AudioSystem.NOT_SPECIFIED, true));
621             formats.add(new AudioFormat(Encoding.PCM_FLOAT,
622                     AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8,
623                     AudioSystem.NOT_SPECIFIED, false));
624             formats.add(new AudioFormat(Encoding.PCM_FLOAT,
625                     AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8,
626                     AudioSystem.NOT_SPECIFIED, true));
627         }
628 
629         return formats.toArray(new AudioFormat[formats.size()]);
630     }
631 
632     @Override
isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat)633     public boolean isConversionSupported(AudioFormat targetFormat,
634                                          AudioFormat sourceFormat) {
635         Objects.requireNonNull(targetFormat);
636         if (AudioFloatConverter.getConverter(sourceFormat) == null)
637             return false;
638         if (AudioFloatConverter.getConverter(targetFormat) == null)
639             return false;
640         if (sourceFormat.getChannels() <= 0)
641             return false;
642         if (targetFormat.getChannels() <= 0)
643             return false;
644         return true;
645     }
646 
647     @Override
isConversionSupported(Encoding targetEncoding, AudioFormat sourceFormat)648     public boolean isConversionSupported(Encoding targetEncoding,
649                                          AudioFormat sourceFormat) {
650         Objects.requireNonNull(targetEncoding);
651         if (AudioFloatConverter.getConverter(sourceFormat) == null)
652             return false;
653         for (int i = 0; i < formats.length; i++) {
654             if (targetEncoding.equals(formats[i]))
655                 return true;
656         }
657         return false;
658     }
659 
660 }
661