1 /*
   2  * Copyright (c) 2008, 2015, 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.AudioInputStream;
  36 import javax.sound.sampled.AudioSystem;
  37 import javax.sound.sampled.AudioFormat.Encoding;
  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 
  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         public int read() throws IOException {
  67             byte[] b = new byte[1];
  68             int ret = read(b);
  69             if (ret < 0)
  70                 return ret;
  71             return b[0] & 0xFF;
  72         }
  73 
  74         public int read(byte[] b, int off, int len) throws IOException {
  75 
  76             int flen = len / fsize;
  77             if (readfloatbuffer == null || readfloatbuffer.length < flen)
  78                 readfloatbuffer = new float[flen];
  79             int ret = stream.read(readfloatbuffer, 0, flen);
  80             if (ret < 0)
  81                 return ret;
  82             converter.toByteArray(readfloatbuffer, 0, ret, b, off);
  83             return ret * fsize;
  84         }
  85 
  86         public int available() throws IOException {
  87             int ret = stream.available();
  88             if (ret < 0)
  89                 return ret;
  90             return ret * fsize;
  91         }
  92 
  93         public void close() throws IOException {
  94             stream.close();
  95         }
  96 
  97         public synchronized void mark(int readlimit) {
  98             stream.mark(readlimit * fsize);
  99         }
 100 
 101         public boolean markSupported() {
 102             return stream.markSupported();
 103         }
 104 
 105         public synchronized void reset() throws IOException {
 106             stream.reset();
 107         }
 108 
 109         public long skip(long n) throws IOException {
 110             long ret = stream.skip(n / fsize);
 111             if (ret < 0)
 112                 return ret;
 113             return ret * fsize;
 114         }
 115 
 116     }
 117 
 118     private static class AudioFloatInputStreamChannelMixer extends
 119             AudioFloatInputStream {
 120 
 121         private final int targetChannels;
 122 
 123         private final int sourceChannels;
 124 
 125         private final AudioFloatInputStream ais;
 126 
 127         private final AudioFormat targetFormat;
 128 
 129         private float[] conversion_buffer;
 130 
 131         AudioFloatInputStreamChannelMixer(AudioFloatInputStream ais,
 132                 int targetChannels) {
 133             this.sourceChannels = ais.getFormat().getChannels();
 134             this.targetChannels = targetChannels;
 135             this.ais = ais;
 136             AudioFormat format = ais.getFormat();
 137             targetFormat = new AudioFormat(format.getEncoding(), format
 138                     .getSampleRate(), format.getSampleSizeInBits(),
 139                     targetChannels, (format.getFrameSize() / sourceChannels)
 140                             * targetChannels, format.getFrameRate(), format
 141                             .isBigEndian());
 142         }
 143 
 144         public int available() throws IOException {
 145             return (ais.available() / sourceChannels) * targetChannels;
 146         }
 147 
 148         public void close() throws IOException {
 149             ais.close();
 150         }
 151 
 152         public AudioFormat getFormat() {
 153             return targetFormat;
 154         }
 155 
 156         public long getFrameLength() {
 157             return ais.getFrameLength();
 158         }
 159 
 160         public void mark(int readlimit) {
 161             ais.mark((readlimit / targetChannels) * sourceChannels);
 162         }
 163 
 164         public boolean markSupported() {
 165             return ais.markSupported();
 166         }
 167 
 168         public int read(float[] b, int off, int len) throws IOException {
 169             int len2 = (len / targetChannels) * sourceChannels;
 170             if (conversion_buffer == null || conversion_buffer.length < len2)
 171                 conversion_buffer = new float[len2];
 172             int ret = ais.read(conversion_buffer, 0, len2);
 173             if (ret < 0)
 174                 return ret;
 175             if (sourceChannels == 1) {
 176                 int cs = targetChannels;
 177                 for (int c = 0; c < targetChannels; c++) {
 178                     for (int i = 0, ix = off + c; i < len2; i++, ix += cs) {
 179                         b[ix] = conversion_buffer[i];
 180                     }
 181                 }
 182             } else if (targetChannels == 1) {
 183                 int cs = sourceChannels;
 184                 for (int i = 0, ix = off; i < len2; i += cs, ix++) {
 185                     b[ix] = conversion_buffer[i];
 186                 }
 187                 for (int c = 1; c < sourceChannels; c++) {
 188                     for (int i = c, ix = off; i < len2; i += cs, ix++) {
 189                         b[ix] += conversion_buffer[i];
 190                     }
 191                 }
 192                 float vol = 1f / ((float) sourceChannels);
 193                 for (int i = 0, ix = off; i < len2; i += cs, ix++) {
 194                     b[ix] *= vol;
 195                 }
 196             } else {
 197                 int minChannels = Math.min(sourceChannels, targetChannels);
 198                 int off_len = off + len;
 199                 int ct = targetChannels;
 200                 int cs = sourceChannels;
 201                 for (int c = 0; c < minChannels; c++) {
 202                     for (int i = off + c, ix = c; i < off_len; i += ct, ix += cs) {
 203                         b[i] = conversion_buffer[ix];
 204                     }
 205                 }
 206                 for (int c = minChannels; c < targetChannels; c++) {
 207                     for (int i = off + c; i < off_len; i += ct) {
 208                         b[i] = 0;
 209                     }
 210                 }
 211             }
 212             return (ret / sourceChannels) * targetChannels;
 213         }
 214 
 215         public void reset() throws IOException {
 216             ais.reset();
 217         }
 218 
 219         public long skip(long len) throws IOException {
 220             long ret = ais.skip((len / targetChannels) * sourceChannels);
 221             if (ret < 0)
 222                 return ret;
 223             return (ret / sourceChannels) * targetChannels;
 224         }
 225 
 226     }
 227 
 228     private static class AudioFloatInputStreamResampler extends
 229             AudioFloatInputStream {
 230 
 231         private final AudioFloatInputStream ais;
 232 
 233         private final AudioFormat targetFormat;
 234 
 235         private float[] skipbuffer;
 236 
 237         private SoftAbstractResampler resampler;
 238 
 239         private final float[] pitch = new float[1];
 240 
 241         private final float[] ibuffer2;
 242 
 243         private final float[][] ibuffer;
 244 
 245         private float ibuffer_index = 0;
 246 
 247         private int ibuffer_len = 0;
 248 
 249         private final int nrofchannels;
 250 
 251         private float[][] cbuffer;
 252 
 253         private final int buffer_len = 512;
 254 
 255         private final int pad;
 256 
 257         private final int pad2;
 258 
 259         private final float[] ix = new float[1];
 260 
 261         private final int[] ox = new int[1];
 262 
 263         private float[][] mark_ibuffer = null;
 264 
 265         private float mark_ibuffer_index = 0;
 266 
 267         private int mark_ibuffer_len = 0;
 268 
 269         AudioFloatInputStreamResampler(AudioFloatInputStream ais,
 270                 AudioFormat format) {
 271             this.ais = ais;
 272             AudioFormat sourceFormat = ais.getFormat();
 273             targetFormat = new AudioFormat(sourceFormat.getEncoding(), format
 274                     .getSampleRate(), sourceFormat.getSampleSizeInBits(),
 275                     sourceFormat.getChannels(), sourceFormat.getFrameSize(),
 276                     format.getSampleRate(), sourceFormat.isBigEndian());
 277             nrofchannels = targetFormat.getChannels();
 278             Object interpolation = format.getProperty("interpolation");
 279             if (interpolation != null && (interpolation instanceof String)) {
 280                 String resamplerType = (String) interpolation;
 281                 if (resamplerType.equalsIgnoreCase("point"))
 282                     this.resampler = new SoftPointResampler();
 283                 if (resamplerType.equalsIgnoreCase("linear"))
 284                     this.resampler = new SoftLinearResampler2();
 285                 if (resamplerType.equalsIgnoreCase("linear1"))
 286                     this.resampler = new SoftLinearResampler();
 287                 if (resamplerType.equalsIgnoreCase("linear2"))
 288                     this.resampler = new SoftLinearResampler2();
 289                 if (resamplerType.equalsIgnoreCase("cubic"))
 290                     this.resampler = new SoftCubicResampler();
 291                 if (resamplerType.equalsIgnoreCase("lanczos"))
 292                     this.resampler = new SoftLanczosResampler();
 293                 if (resamplerType.equalsIgnoreCase("sinc"))
 294                     this.resampler = new SoftSincResampler();
 295             }
 296             if (resampler == null)
 297                 resampler = new SoftLinearResampler2(); // new
 298                                                         // SoftLinearResampler2();
 299             pitch[0] = sourceFormat.getSampleRate() / format.getSampleRate();
 300             pad = resampler.getPadding();
 301             pad2 = pad * 2;
 302             ibuffer = new float[nrofchannels][buffer_len + pad2];
 303             ibuffer2 = new float[nrofchannels * buffer_len];
 304             ibuffer_index = buffer_len + pad;
 305             ibuffer_len = buffer_len;
 306         }
 307 
 308         public int available() throws IOException {
 309             return 0;
 310         }
 311 
 312         public void close() throws IOException {
 313             ais.close();
 314         }
 315 
 316         public AudioFormat getFormat() {
 317             return targetFormat;
 318         }
 319 
 320         public long getFrameLength() {
 321             return AudioSystem.NOT_SPECIFIED; // ais.getFrameLength();
 322         }
 323 
 324         public void mark(int readlimit) {
 325             ais.mark((int) (readlimit * pitch[0]));
 326             mark_ibuffer_index = ibuffer_index;
 327             mark_ibuffer_len = ibuffer_len;
 328             if (mark_ibuffer == null) {
 329                 mark_ibuffer = new float[ibuffer.length][ibuffer[0].length];
 330             }
 331             for (int c = 0; c < ibuffer.length; c++) {
 332                 float[] from = ibuffer[c];
 333                 float[] to = mark_ibuffer[c];
 334                 for (int i = 0; i < to.length; i++) {
 335                     to[i] = from[i];
 336                 }
 337             }
 338         }
 339 
 340         public boolean markSupported() {
 341             return ais.markSupported();
 342         }
 343 
 344         private void readNextBuffer() throws IOException {
 345 
 346             if (ibuffer_len == -1)
 347                 return;
 348 
 349             for (int c = 0; c < nrofchannels; c++) {
 350                 float[] buff = ibuffer[c];
 351                 int buffer_len_pad = ibuffer_len + pad2;
 352                 for (int i = ibuffer_len, ix = 0; i < buffer_len_pad; i++, ix++) {
 353                     buff[ix] = buff[i];
 354                 }
 355             }
 356 
 357             ibuffer_index -= (ibuffer_len);
 358 
 359             ibuffer_len = ais.read(ibuffer2);
 360             if (ibuffer_len >= 0) {
 361                 while (ibuffer_len < ibuffer2.length) {
 362                     int ret = ais.read(ibuffer2, ibuffer_len, ibuffer2.length
 363                             - ibuffer_len);
 364                     if (ret == -1)
 365                         break;
 366                     ibuffer_len += ret;
 367                 }
 368                 Arrays.fill(ibuffer2, ibuffer_len, ibuffer2.length, 0);
 369                 ibuffer_len /= nrofchannels;
 370             } else {
 371                 Arrays.fill(ibuffer2, 0, ibuffer2.length, 0);
 372             }
 373 
 374             int ibuffer2_len = ibuffer2.length;
 375             for (int c = 0; c < nrofchannels; c++) {
 376                 float[] buff = ibuffer[c];
 377                 for (int i = c, ix = pad2; i < ibuffer2_len; i += nrofchannels, ix++) {
 378                     buff[ix] = ibuffer2[i];
 379                 }
 380             }
 381 
 382         }
 383 
 384         public int read(float[] b, int off, int len) throws IOException {
 385 
 386             if (cbuffer == null || cbuffer[0].length < len / nrofchannels) {
 387                 cbuffer = new float[nrofchannels][len / nrofchannels];
 388             }
 389             if (ibuffer_len == -1)
 390                 return -1;
 391             if (len < 0)
 392                 return 0;
 393             int offlen = off + len;
 394             int remain = len / nrofchannels;
 395             int destPos = 0;
 396             int in_end = ibuffer_len;
 397             while (remain > 0) {
 398                 if (ibuffer_len >= 0) {
 399                     if (ibuffer_index >= (ibuffer_len + pad))
 400                         readNextBuffer();
 401                     in_end = ibuffer_len + pad;
 402                 }
 403 
 404                 if (ibuffer_len < 0) {
 405                     in_end = pad2;
 406                     if (ibuffer_index >= in_end)
 407                         break;
 408                 }
 409 
 410                 if (ibuffer_index < 0)
 411                     break;
 412                 int preDestPos = destPos;
 413                 for (int c = 0; c < nrofchannels; c++) {
 414                     ix[0] = ibuffer_index;
 415                     ox[0] = destPos;
 416                     float[] buff = ibuffer[c];
 417                     resampler.interpolate(buff, ix, in_end, pitch, 0,
 418                             cbuffer[c], ox, len / nrofchannels);
 419                 }
 420                 ibuffer_index = ix[0];
 421                 destPos = ox[0];
 422                 remain -= destPos - preDestPos;
 423             }
 424             for (int c = 0; c < nrofchannels; c++) {
 425                 int ix = 0;
 426                 float[] buff = cbuffer[c];
 427                 for (int i = c + off; i < offlen; i += nrofchannels) {
 428                     b[i] = buff[ix++];
 429                 }
 430             }
 431             return len - remain * nrofchannels;
 432         }
 433 
 434         public void reset() throws IOException {
 435             ais.reset();
 436             if (mark_ibuffer == null)
 437                 return;
 438             ibuffer_index = mark_ibuffer_index;
 439             ibuffer_len = mark_ibuffer_len;
 440             for (int c = 0; c < ibuffer.length; c++) {
 441                 float[] from = mark_ibuffer[c];
 442                 float[] to = ibuffer[c];
 443                 for (int i = 0; i < to.length; i++) {
 444                     to[i] = from[i];
 445                 }
 446             }
 447 
 448         }
 449 
 450         public long skip(long len) throws IOException {
 451             if (len < 0)
 452                 return 0;
 453             if (skipbuffer == null)
 454                 skipbuffer = new float[1024 * targetFormat.getFrameSize()];
 455             float[] l_skipbuffer = skipbuffer;
 456             long remain = len;
 457             while (remain > 0) {
 458                 int ret = read(l_skipbuffer, 0, (int) Math.min(remain,
 459                         skipbuffer.length));
 460                 if (ret < 0) {
 461                     if (remain == len)
 462                         return ret;
 463                     break;
 464                 }
 465                 remain -= ret;
 466             }
 467             return len - remain;
 468 
 469         }
 470 
 471     }
 472 
 473     private final Encoding[] formats = {Encoding.PCM_SIGNED,
 474                                         Encoding.PCM_UNSIGNED,
 475                                         Encoding.PCM_FLOAT};
 476 
 477     public AudioInputStream getAudioInputStream(Encoding targetEncoding,
 478             AudioInputStream sourceStream) {
 479         if (!isConversionSupported(targetEncoding, sourceStream.getFormat())) {
 480             throw new IllegalArgumentException(
 481                     "Unsupported conversion: " + sourceStream.getFormat()
 482                             .toString() + " to " + targetEncoding.toString());
 483         }
 484         if (sourceStream.getFormat().getEncoding().equals(targetEncoding))
 485             return sourceStream;
 486         AudioFormat format = sourceStream.getFormat();
 487         int channels = format.getChannels();
 488         Encoding encoding = targetEncoding;
 489         float samplerate = format.getSampleRate();
 490         int bits = format.getSampleSizeInBits();
 491         boolean bigendian = format.isBigEndian();
 492         if (targetEncoding.equals(Encoding.PCM_FLOAT))
 493             bits = 32;
 494         AudioFormat targetFormat = new AudioFormat(encoding, samplerate, bits,
 495                 channels, channels * bits / 8, samplerate, bigendian);
 496         return getAudioInputStream(targetFormat, sourceStream);
 497     }
 498 
 499     public AudioInputStream getAudioInputStream(AudioFormat targetFormat,
 500             AudioInputStream sourceStream) {
 501         if (!isConversionSupported(targetFormat, sourceStream.getFormat()))
 502             throw new IllegalArgumentException("Unsupported conversion: "
 503                     + sourceStream.getFormat().toString() + " to "
 504                     + targetFormat.toString());
 505         return getAudioInputStream(targetFormat, AudioFloatInputStream
 506                 .getInputStream(sourceStream));
 507     }
 508 
 509     public AudioInputStream getAudioInputStream(AudioFormat targetFormat,
 510             AudioFloatInputStream sourceStream) {
 511 
 512         if (!isConversionSupported(targetFormat, sourceStream.getFormat()))
 513             throw new IllegalArgumentException("Unsupported conversion: "
 514                     + sourceStream.getFormat().toString() + " to "
 515                     + targetFormat.toString());
 516         if (targetFormat.getChannels() != sourceStream.getFormat()
 517                 .getChannels())
 518             sourceStream = new AudioFloatInputStreamChannelMixer(sourceStream,
 519                     targetFormat.getChannels());
 520         if (Math.abs(targetFormat.getSampleRate()
 521                 - sourceStream.getFormat().getSampleRate()) > 0.000001)
 522             sourceStream = new AudioFloatInputStreamResampler(sourceStream,
 523                     targetFormat);
 524         return new AudioInputStream(new AudioFloatFormatConverterInputStream(
 525                 targetFormat, sourceStream), targetFormat, sourceStream
 526                 .getFrameLength());
 527     }
 528 
 529     public Encoding[] getSourceEncodings() {
 530         return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
 531                 Encoding.PCM_FLOAT };
 532     }
 533 
 534     public Encoding[] getTargetEncodings() {
 535         return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
 536                 Encoding.PCM_FLOAT };
 537     }
 538 
 539     public Encoding[] getTargetEncodings(AudioFormat sourceFormat) {
 540         if (AudioFloatConverter.getConverter(sourceFormat) == null)
 541             return new Encoding[0];
 542         return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
 543                 Encoding.PCM_FLOAT };
 544     }
 545 
 546     public AudioFormat[] getTargetFormats(Encoding targetEncoding,
 547             AudioFormat sourceFormat) {
 548         Objects.requireNonNull(targetEncoding);
 549         if (AudioFloatConverter.getConverter(sourceFormat) == null)
 550             return new AudioFormat[0];
 551         int channels = sourceFormat.getChannels();
 552 
 553         ArrayList<AudioFormat> formats = new ArrayList<AudioFormat>();
 554 
 555         if (targetEncoding.equals(Encoding.PCM_SIGNED))
 556             formats.add(new AudioFormat(Encoding.PCM_SIGNED,
 557                     AudioSystem.NOT_SPECIFIED, 8, channels, channels,
 558                     AudioSystem.NOT_SPECIFIED, false));
 559         if (targetEncoding.equals(Encoding.PCM_UNSIGNED))
 560             formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
 561                     AudioSystem.NOT_SPECIFIED, 8, channels, channels,
 562                     AudioSystem.NOT_SPECIFIED, false));
 563 
 564         for (int bits = 16; bits < 32; bits += 8) {
 565             if (targetEncoding.equals(Encoding.PCM_SIGNED)) {
 566                 formats.add(new AudioFormat(Encoding.PCM_SIGNED,
 567                         AudioSystem.NOT_SPECIFIED, bits, channels, channels
 568                                 * bits / 8, AudioSystem.NOT_SPECIFIED, false));
 569                 formats.add(new AudioFormat(Encoding.PCM_SIGNED,
 570                         AudioSystem.NOT_SPECIFIED, bits, channels, channels
 571                                 * bits / 8, AudioSystem.NOT_SPECIFIED, true));
 572             }
 573             if (targetEncoding.equals(Encoding.PCM_UNSIGNED)) {
 574                 formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
 575                         AudioSystem.NOT_SPECIFIED, bits, channels, channels
 576                                 * bits / 8, AudioSystem.NOT_SPECIFIED, true));
 577                 formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
 578                         AudioSystem.NOT_SPECIFIED, bits, channels, channels
 579                                 * bits / 8, AudioSystem.NOT_SPECIFIED, false));
 580             }
 581         }
 582 
 583         if (targetEncoding.equals(Encoding.PCM_FLOAT)) {
 584             formats.add(new AudioFormat(Encoding.PCM_FLOAT,
 585                     AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4,
 586                     AudioSystem.NOT_SPECIFIED, false));
 587             formats.add(new AudioFormat(Encoding.PCM_FLOAT,
 588                     AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4,
 589                     AudioSystem.NOT_SPECIFIED, true));
 590             formats.add(new AudioFormat(Encoding.PCM_FLOAT,
 591                     AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8,
 592                     AudioSystem.NOT_SPECIFIED, false));
 593             formats.add(new AudioFormat(Encoding.PCM_FLOAT,
 594                     AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8,
 595                     AudioSystem.NOT_SPECIFIED, true));
 596         }
 597 
 598         return formats.toArray(new AudioFormat[formats.size()]);
 599     }
 600 
 601     public boolean isConversionSupported(AudioFormat targetFormat,
 602             AudioFormat sourceFormat) {
 603         Objects.requireNonNull(targetFormat);
 604         if (AudioFloatConverter.getConverter(sourceFormat) == null)
 605             return false;
 606         if (AudioFloatConverter.getConverter(targetFormat) == null)
 607             return false;
 608         if (sourceFormat.getChannels() <= 0)
 609             return false;
 610         if (targetFormat.getChannels() <= 0)
 611             return false;
 612         return true;
 613     }
 614 
 615     public boolean isConversionSupported(Encoding targetEncoding,
 616             AudioFormat sourceFormat) {
 617         Objects.requireNonNull(targetEncoding);
 618         if (AudioFloatConverter.getConverter(sourceFormat) == null)
 619             return false;
 620         for (int i = 0; i < formats.length; i++) {
 621             if (targetEncoding.equals(formats[i]))
 622                 return true;
 623         }
 624         return false;
 625     }
 626 
 627 }