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