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 
  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
  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
  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
  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
  97         public void close() throws IOException {
  98             stream.close();
  99         }
 100 
 101         @Override
 102         public synchronized void mark(int readlimit) {
 103             stream.mark(readlimit * fsize);
 104         }
 105 
 106         @Override
 107         public boolean markSupported() {
 108             return stream.markSupported();
 109         }
 110 
 111         @Override
 112         public synchronized void reset() throws IOException {
 113             stream.reset();
 114         }
 115 
 116         @Override
 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 
 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
 153         public int available() throws IOException {
 154             return (ais.available() / sourceChannels) * targetChannels;
 155         }
 156 
 157         @Override
 158         public void close() throws IOException {
 159             ais.close();
 160         }
 161 
 162         @Override
 163         public AudioFormat getFormat() {
 164             return targetFormat;
 165         }
 166 
 167         @Override
 168         public long getFrameLength() {
 169             return ais.getFrameLength();
 170         }
 171 
 172         @Override
 173         public void mark(int readlimit) {
 174             ais.mark((readlimit / targetChannels) * sourceChannels);
 175         }
 176 
 177         @Override
 178         public boolean markSupported() {
 179             return ais.markSupported();
 180         }
 181 
 182         @Override
 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
 231         public void reset() throws IOException {
 232             ais.reset();
 233         }
 234 
 235         @Override
 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 
 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
 326         public int available() throws IOException {
 327             return 0;
 328         }
 329 
 330         @Override
 331         public void close() throws IOException {
 332             ais.close();
 333         }
 334 
 335         @Override
 336         public AudioFormat getFormat() {
 337             return targetFormat;
 338         }
 339 
 340         @Override
 341         public long getFrameLength() {
 342             return AudioSystem.NOT_SPECIFIED; // ais.getFrameLength();
 343         }
 344 
 345         @Override
 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
 363         public boolean markSupported() {
 364             return ais.markSupported();
 365         }
 366 
 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
 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
 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
 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
 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
 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 
 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
 558     public Encoding[] getSourceEncodings() {
 559         return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
 560                 Encoding.PCM_FLOAT };
 561     }
 562 
 563     @Override
 564     public Encoding[] getTargetEncodings() {
 565         return getSourceEncodings();
 566     }
 567 
 568     @Override
 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
 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
 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
 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 }