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