1 /* 2 * Copyright (c) 1999, 2019, 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.util.Objects; 30 import java.util.Vector; 31 32 import javax.sound.sampled.AudioFormat; 33 import javax.sound.sampled.AudioFormat.Encoding; 34 import javax.sound.sampled.AudioInputStream; 35 import javax.sound.sampled.AudioSystem; 36 import javax.sound.sampled.spi.FormatConversionProvider; 37 38 /** 39 * Converts among signed/unsigned and little/big endianness of sampled. 40 * 41 * @author Jan Borgersen 42 */ 43 public final class PCMtoPCMCodec extends FormatConversionProvider { 44 45 @Override getSourceEncodings()46 public AudioFormat.Encoding[] getSourceEncodings() { 47 return new Encoding[]{Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED}; 48 } 49 50 @Override getTargetEncodings()51 public AudioFormat.Encoding[] getTargetEncodings() { 52 return getSourceEncodings(); 53 } 54 55 @Override getTargetEncodings(AudioFormat sourceFormat)56 public AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat) { 57 58 final int sampleSize = sourceFormat.getSampleSizeInBits(); 59 AudioFormat.Encoding encoding = sourceFormat.getEncoding(); 60 if (sampleSize == 8) { 61 if (encoding.equals(AudioFormat.Encoding.PCM_SIGNED)) { 62 return new AudioFormat.Encoding[]{ 63 AudioFormat.Encoding.PCM_UNSIGNED 64 }; 65 } 66 if (encoding.equals(AudioFormat.Encoding.PCM_UNSIGNED)) { 67 return new AudioFormat.Encoding[]{ 68 AudioFormat.Encoding.PCM_SIGNED 69 }; 70 } 71 } else if (sampleSize == 16) { 72 if (encoding.equals(AudioFormat.Encoding.PCM_SIGNED) 73 || encoding.equals(AudioFormat.Encoding.PCM_UNSIGNED)) { 74 return new AudioFormat.Encoding[]{ 75 AudioFormat.Encoding.PCM_UNSIGNED, 76 AudioFormat.Encoding.PCM_SIGNED 77 }; 78 } 79 } 80 return new AudioFormat.Encoding[0]; 81 } 82 83 @Override getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat)84 public AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat){ 85 Objects.requireNonNull(targetEncoding); 86 87 // filter out targetEncoding from the old getOutputFormats( sourceFormat ) method 88 89 AudioFormat[] formats = getOutputFormats( sourceFormat ); 90 Vector<AudioFormat> newFormats = new Vector<>(); 91 for(int i=0; i<formats.length; i++ ) { 92 if( formats[i].getEncoding().equals( targetEncoding ) ) { 93 newFormats.addElement( formats[i] ); 94 } 95 } 96 97 AudioFormat[] formatArray = new AudioFormat[newFormats.size()]; 98 99 for (int i = 0; i < formatArray.length; i++) { 100 formatArray[i] = newFormats.elementAt(i); 101 } 102 103 return formatArray; 104 } 105 106 @Override getAudioInputStream(AudioFormat.Encoding targetEncoding, AudioInputStream sourceStream)107 public AudioInputStream getAudioInputStream(AudioFormat.Encoding targetEncoding, AudioInputStream sourceStream) { 108 109 if( isConversionSupported(targetEncoding, sourceStream.getFormat()) ) { 110 111 AudioFormat sourceFormat = sourceStream.getFormat(); 112 AudioFormat targetFormat = new AudioFormat( targetEncoding, 113 sourceFormat.getSampleRate(), 114 sourceFormat.getSampleSizeInBits(), 115 sourceFormat.getChannels(), 116 sourceFormat.getFrameSize(), 117 sourceFormat.getFrameRate(), 118 sourceFormat.isBigEndian() ); 119 120 return getConvertedStream(targetFormat, sourceStream); 121 122 } else { 123 throw new IllegalArgumentException("Unsupported conversion: " + sourceStream.getFormat().toString() + " to " + targetEncoding.toString() ); 124 } 125 126 } 127 128 @Override getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream)129 public AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream){ 130 if (!isConversionSupported(targetFormat, sourceStream.getFormat())) 131 throw new IllegalArgumentException("Unsupported conversion: " 132 + sourceStream.getFormat().toString() + " to " 133 + targetFormat.toString()); 134 return getConvertedStream( targetFormat, sourceStream ); 135 } 136 137 /** 138 * Opens the codec with the specified parameters. 139 * @param stream stream from which data to be processed should be read 140 * @param outputFormat desired data format of the stream after processing 141 * @return stream from which processed data may be read 142 * @throws IllegalArgumentException if the format combination supplied is 143 * not supported. 144 */ getConvertedStream(AudioFormat outputFormat, AudioInputStream stream)145 private AudioInputStream getConvertedStream(AudioFormat outputFormat, AudioInputStream stream) { 146 147 AudioInputStream cs = null; 148 149 AudioFormat inputFormat = stream.getFormat(); 150 151 if( inputFormat.matches(outputFormat) ) { 152 153 cs = stream; 154 } else { 155 156 cs = new PCMtoPCMCodecStream(stream, outputFormat); 157 } 158 return cs; 159 } 160 161 /** 162 * Obtains the set of output formats supported by the codec 163 * given a particular input format. 164 * If no output formats are supported for this input format, 165 * returns an array of length 0. 166 * @return array of supported output formats. 167 */ getOutputFormats(AudioFormat inputFormat)168 private AudioFormat[] getOutputFormats(AudioFormat inputFormat) { 169 170 Vector<AudioFormat> formats = new Vector<>(); 171 AudioFormat format; 172 173 int sampleSize = inputFormat.getSampleSizeInBits(); 174 boolean isBigEndian = inputFormat.isBigEndian(); 175 176 177 if ( sampleSize==8 ) { 178 if ( AudioFormat.Encoding.PCM_SIGNED.equals(inputFormat.getEncoding()) ) { 179 180 format = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 181 inputFormat.getSampleRate(), 182 inputFormat.getSampleSizeInBits(), 183 inputFormat.getChannels(), 184 inputFormat.getFrameSize(), 185 inputFormat.getFrameRate(), 186 false ); 187 formats.addElement(format); 188 } 189 190 if ( AudioFormat.Encoding.PCM_UNSIGNED.equals(inputFormat.getEncoding()) ) { 191 192 format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 193 inputFormat.getSampleRate(), 194 inputFormat.getSampleSizeInBits(), 195 inputFormat.getChannels(), 196 inputFormat.getFrameSize(), 197 inputFormat.getFrameRate(), 198 false ); 199 formats.addElement(format); 200 } 201 202 } else if ( sampleSize==16 ) { 203 204 if ( AudioFormat.Encoding.PCM_SIGNED.equals(inputFormat.getEncoding()) && isBigEndian ) { 205 206 format = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 207 inputFormat.getSampleRate(), 208 inputFormat.getSampleSizeInBits(), 209 inputFormat.getChannels(), 210 inputFormat.getFrameSize(), 211 inputFormat.getFrameRate(), 212 true ); 213 formats.addElement(format); 214 format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 215 inputFormat.getSampleRate(), 216 inputFormat.getSampleSizeInBits(), 217 inputFormat.getChannels(), 218 inputFormat.getFrameSize(), 219 inputFormat.getFrameRate(), 220 false ); 221 formats.addElement(format); 222 format = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 223 inputFormat.getSampleRate(), 224 inputFormat.getSampleSizeInBits(), 225 inputFormat.getChannels(), 226 inputFormat.getFrameSize(), 227 inputFormat.getFrameRate(), 228 false ); 229 formats.addElement(format); 230 } 231 232 if ( AudioFormat.Encoding.PCM_UNSIGNED.equals(inputFormat.getEncoding()) && isBigEndian ) { 233 234 format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 235 inputFormat.getSampleRate(), 236 inputFormat.getSampleSizeInBits(), 237 inputFormat.getChannels(), 238 inputFormat.getFrameSize(), 239 inputFormat.getFrameRate(), 240 true ); 241 formats.addElement(format); 242 format = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 243 inputFormat.getSampleRate(), 244 inputFormat.getSampleSizeInBits(), 245 inputFormat.getChannels(), 246 inputFormat.getFrameSize(), 247 inputFormat.getFrameRate(), 248 false ); 249 formats.addElement(format); 250 format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 251 inputFormat.getSampleRate(), 252 inputFormat.getSampleSizeInBits(), 253 inputFormat.getChannels(), 254 inputFormat.getFrameSize(), 255 inputFormat.getFrameRate(), 256 false ); 257 formats.addElement(format); 258 } 259 260 if ( AudioFormat.Encoding.PCM_SIGNED.equals(inputFormat.getEncoding()) && !isBigEndian ) { 261 262 format = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 263 inputFormat.getSampleRate(), 264 inputFormat.getSampleSizeInBits(), 265 inputFormat.getChannels(), 266 inputFormat.getFrameSize(), 267 inputFormat.getFrameRate(), 268 false ); 269 formats.addElement(format); 270 format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 271 inputFormat.getSampleRate(), 272 inputFormat.getSampleSizeInBits(), 273 inputFormat.getChannels(), 274 inputFormat.getFrameSize(), 275 inputFormat.getFrameRate(), 276 true ); 277 formats.addElement(format); 278 format = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 279 inputFormat.getSampleRate(), 280 inputFormat.getSampleSizeInBits(), 281 inputFormat.getChannels(), 282 inputFormat.getFrameSize(), 283 inputFormat.getFrameRate(), 284 true ); 285 formats.addElement(format); 286 } 287 288 if ( AudioFormat.Encoding.PCM_UNSIGNED.equals(inputFormat.getEncoding()) && !isBigEndian ) { 289 290 format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 291 inputFormat.getSampleRate(), 292 inputFormat.getSampleSizeInBits(), 293 inputFormat.getChannels(), 294 inputFormat.getFrameSize(), 295 inputFormat.getFrameRate(), 296 false ); 297 formats.addElement(format); 298 format = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 299 inputFormat.getSampleRate(), 300 inputFormat.getSampleSizeInBits(), 301 inputFormat.getChannels(), 302 inputFormat.getFrameSize(), 303 inputFormat.getFrameRate(), 304 true ); 305 formats.addElement(format); 306 format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 307 inputFormat.getSampleRate(), 308 inputFormat.getSampleSizeInBits(), 309 inputFormat.getChannels(), 310 inputFormat.getFrameSize(), 311 inputFormat.getFrameRate(), 312 true ); 313 formats.addElement(format); 314 } 315 } 316 AudioFormat[] formatArray; 317 318 synchronized(formats) { 319 320 formatArray = new AudioFormat[formats.size()]; 321 322 for (int i = 0; i < formatArray.length; i++) { 323 324 formatArray[i] = formats.elementAt(i); 325 } 326 } 327 328 return formatArray; 329 } 330 331 class PCMtoPCMCodecStream extends AudioInputStream { 332 333 private final int PCM_SWITCH_SIGNED_8BIT = 1; 334 private final int PCM_SWITCH_ENDIAN = 2; 335 private final int PCM_SWITCH_SIGNED_LE = 3; 336 private final int PCM_SWITCH_SIGNED_BE = 4; 337 private final int PCM_UNSIGNED_LE2SIGNED_BE = 5; 338 private final int PCM_SIGNED_LE2UNSIGNED_BE = 6; 339 private final int PCM_UNSIGNED_BE2SIGNED_LE = 7; 340 private final int PCM_SIGNED_BE2UNSIGNED_LE = 8; 341 342 private final int sampleSizeInBytes; 343 private int conversionType = 0; 344 345 PCMtoPCMCodecStream(AudioInputStream stream, AudioFormat outputFormat)346 PCMtoPCMCodecStream(AudioInputStream stream, AudioFormat outputFormat) { 347 348 super(stream, outputFormat, -1); 349 350 int sampleSizeInBits = 0; 351 AudioFormat.Encoding inputEncoding = null; 352 AudioFormat.Encoding outputEncoding = null; 353 boolean inputIsBigEndian; 354 boolean outputIsBigEndian; 355 356 AudioFormat inputFormat = stream.getFormat(); 357 358 // throw an IllegalArgumentException if not ok 359 if ( ! (isConversionSupported(inputFormat, outputFormat)) ) { 360 361 throw new IllegalArgumentException("Unsupported conversion: " + inputFormat.toString() + " to " + outputFormat.toString()); 362 } 363 364 inputEncoding = inputFormat.getEncoding(); 365 outputEncoding = outputFormat.getEncoding(); 366 inputIsBigEndian = inputFormat.isBigEndian(); 367 outputIsBigEndian = outputFormat.isBigEndian(); 368 sampleSizeInBits = inputFormat.getSampleSizeInBits(); 369 sampleSizeInBytes = sampleSizeInBits/8; 370 371 // determine conversion to perform 372 373 if( sampleSizeInBits==8 ) { 374 if( AudioFormat.Encoding.PCM_UNSIGNED.equals(inputEncoding) && 375 AudioFormat.Encoding.PCM_SIGNED.equals(outputEncoding) ) { 376 conversionType = PCM_SWITCH_SIGNED_8BIT; 377 } else if( AudioFormat.Encoding.PCM_SIGNED.equals(inputEncoding) && 378 AudioFormat.Encoding.PCM_UNSIGNED.equals(outputEncoding) ) { 379 conversionType = PCM_SWITCH_SIGNED_8BIT; 380 } 381 } else { 382 383 if( inputEncoding.equals(outputEncoding) && (inputIsBigEndian != outputIsBigEndian) ) { 384 385 conversionType = PCM_SWITCH_ENDIAN; 386 } else if (AudioFormat.Encoding.PCM_UNSIGNED.equals(inputEncoding) && !inputIsBigEndian && 387 AudioFormat.Encoding.PCM_SIGNED.equals(outputEncoding) && outputIsBigEndian) { 388 389 conversionType = PCM_UNSIGNED_LE2SIGNED_BE; 390 } else if (AudioFormat.Encoding.PCM_SIGNED.equals(inputEncoding) && !inputIsBigEndian && 391 AudioFormat.Encoding.PCM_UNSIGNED.equals(outputEncoding) && outputIsBigEndian) { 392 393 conversionType = PCM_SIGNED_LE2UNSIGNED_BE; 394 } else if (AudioFormat.Encoding.PCM_UNSIGNED.equals(inputEncoding) && inputIsBigEndian && 395 AudioFormat.Encoding.PCM_SIGNED.equals(outputEncoding) && !outputIsBigEndian) { 396 397 conversionType = PCM_UNSIGNED_BE2SIGNED_LE; 398 } else if (AudioFormat.Encoding.PCM_SIGNED.equals(inputEncoding) && inputIsBigEndian && 399 AudioFormat.Encoding.PCM_UNSIGNED.equals(outputEncoding) && !outputIsBigEndian) { 400 401 conversionType = PCM_SIGNED_BE2UNSIGNED_LE; 402 } 403 } 404 405 // set the audio stream length in frames if we know it 406 407 frameSize = inputFormat.getFrameSize(); 408 if( frameSize == AudioSystem.NOT_SPECIFIED ) { 409 frameSize=1; 410 } 411 if( stream instanceof AudioInputStream ) { 412 frameLength = stream.getFrameLength(); 413 } else { 414 frameLength = AudioSystem.NOT_SPECIFIED; 415 } 416 417 // set framePos to zero 418 framePos = 0; 419 420 } 421 422 /** 423 * Note that this only works for sign conversions. 424 * Other conversions require a read of at least 2 bytes. 425 */ 426 @Override read()427 public int read() throws IOException { 428 429 // $$jb: do we want to implement this function? 430 431 int temp; 432 byte tempbyte; 433 434 if( frameSize==1 ) { 435 if( conversionType == PCM_SWITCH_SIGNED_8BIT ) { 436 temp = super.read(); 437 438 if( temp < 0 ) return temp; // EOF or error 439 440 tempbyte = (byte) (temp & 0xf); 441 tempbyte = (tempbyte >= 0) ? (byte)(0x80 | tempbyte) : (byte)(0x7F & tempbyte); 442 temp = (int) tempbyte & 0xf; 443 444 return temp; 445 446 } else { 447 // $$jb: what to return here? 448 throw new IOException("cannot read a single byte if frame size > 1"); 449 } 450 } else { 451 throw new IOException("cannot read a single byte if frame size > 1"); 452 } 453 } 454 455 @Override read(byte[] b)456 public int read(byte[] b) throws IOException { 457 458 return read(b, 0, b.length); 459 } 460 461 @Override read(byte[] b, int off, int len)462 public int read(byte[] b, int off, int len) throws IOException { 463 464 465 int i; 466 467 // don't read fractional frames 468 if ( len%frameSize != 0 ) { 469 len -= (len%frameSize); 470 } 471 // don't read past our own set length 472 if( (frameLength!=AudioSystem.NOT_SPECIFIED) && ( (len/frameSize) >(frameLength-framePos)) ) { 473 len = (int)(frameLength-framePos) * frameSize; 474 } 475 476 int readCount = super.read(b, off, len); 477 byte tempByte; 478 479 if(readCount<0) { // EOF or error 480 return readCount; 481 } 482 483 // now do the conversions 484 485 switch( conversionType ) { 486 487 case PCM_SWITCH_SIGNED_8BIT: 488 switchSigned8bit(b,off,len,readCount); 489 break; 490 491 case PCM_SWITCH_ENDIAN: 492 switchEndian(b,off,len,readCount); 493 break; 494 495 case PCM_SWITCH_SIGNED_LE: 496 switchSignedLE(b,off,len,readCount); 497 break; 498 499 case PCM_SWITCH_SIGNED_BE: 500 switchSignedBE(b,off,len,readCount); 501 break; 502 503 case PCM_UNSIGNED_LE2SIGNED_BE: 504 case PCM_SIGNED_LE2UNSIGNED_BE: 505 switchSignedLE(b,off,len,readCount); 506 switchEndian(b,off,len,readCount); 507 break; 508 509 case PCM_UNSIGNED_BE2SIGNED_LE: 510 case PCM_SIGNED_BE2UNSIGNED_LE: 511 switchSignedBE(b,off,len,readCount); 512 switchEndian(b,off,len,readCount); 513 break; 514 515 default: 516 // do nothing 517 } 518 519 // we've done the conversion, just return the readCount 520 return readCount; 521 522 } 523 switchSigned8bit(byte[] b, int off, int len, int readCount)524 private void switchSigned8bit(byte[] b, int off, int len, int readCount) { 525 526 for(int i=off; i < (off+readCount); i++) { 527 b[i] = (b[i] >= 0) ? (byte)(0x80 | b[i]) : (byte)(0x7F & b[i]); 528 } 529 } 530 switchSignedBE(byte[] b, int off, int len, int readCount)531 private void switchSignedBE(byte[] b, int off, int len, int readCount) { 532 533 for(int i=off; i < (off+readCount); i+= sampleSizeInBytes ) { 534 b[i] = (b[i] >= 0) ? (byte)(0x80 | b[i]) : (byte)(0x7F & b[i]); 535 } 536 } 537 switchSignedLE(byte[] b, int off, int len, int readCount)538 private void switchSignedLE(byte[] b, int off, int len, int readCount) { 539 540 for(int i=(off+sampleSizeInBytes-1); i < (off+readCount); i+= sampleSizeInBytes ) { 541 b[i] = (b[i] >= 0) ? (byte)(0x80 | b[i]) : (byte)(0x7F & b[i]); 542 } 543 } 544 switchEndian(byte[] b, int off, int len, int readCount)545 private void switchEndian(byte[] b, int off, int len, int readCount) { 546 547 if(sampleSizeInBytes == 2) { 548 for(int i=off; i < (off+readCount); i += sampleSizeInBytes ) { 549 byte temp; 550 temp = b[i]; 551 b[i] = b[i+1]; 552 b[i+1] = temp; 553 } 554 } 555 } 556 } // end class PCMtoPCMCodecStream 557 } // end class PCMtoPCMCodec 558