1 /* 2 * Copyright (c) 1999, 2007, 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.InputStream; 29 import java.io.IOException; 30 31 import java.util.Vector; 32 33 import javax.sound.sampled.AudioFormat; 34 import javax.sound.sampled.AudioSystem; 35 import javax.sound.sampled.AudioInputStream; 36 37 38 /** 39 * A-law encodes linear data, and decodes a-law data to linear data. 40 * 41 * @author Kara Kytle 42 */ 43 public class AlawCodec extends SunCodec { 44 45 /* Tables used for A-law decoding */ 46 47 final static byte ALAW_TABH[] = new byte[256]; 48 final static byte ALAW_TABL[] = new byte[256]; 49 50 private static final AudioFormat.Encoding[] alawEncodings = { AudioFormat.Encoding.ALAW, AudioFormat.Encoding.PCM_SIGNED }; 51 52 private static final short seg_end [] = {0xFF, 0x1FF, 0x3FF, 53 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF}; 54 55 /** 56 * Initializes the decode tables 57 */ 58 static { 59 for (int i=0;i<256;i++) { 60 int input = i ^ 0x55; 61 int mantissa = (input & 0xf ) << 4; 62 int segment = (input & 0x70) >> 4; 63 int value = mantissa+8; 64 65 if(segment>=1) 66 value+=0x100; 67 if(segment>1) 68 value <<= (segment -1); 69 70 if( (input & 0x80)==0 ) 71 value = -value; 72 73 ALAW_TABL[i] = (byte)value; 74 ALAW_TABH[i] = (byte)(value>>8); 75 } 76 } 77 78 79 /** 80 * Constructs a new ALAW codec object. 81 */ 82 public AlawCodec() { 83 84 super(alawEncodings, alawEncodings); 85 } 86 87 // NEW CODE 88 89 /** 90 */ 91 public AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat){ 92 93 if( sourceFormat.getEncoding().equals( AudioFormat.Encoding.PCM_SIGNED )) { 94 95 if( sourceFormat.getSampleSizeInBits() == 16 ) { 96 97 AudioFormat.Encoding enc[] = new AudioFormat.Encoding[1]; 98 enc[0] = AudioFormat.Encoding.ALAW; 99 return enc; 100 101 } else { 102 return new AudioFormat.Encoding[0]; 103 } 104 } else if( sourceFormat.getEncoding().equals( AudioFormat.Encoding.ALAW ) ) { 105 106 if( sourceFormat.getSampleSizeInBits() == 8 ) { 107 108 AudioFormat.Encoding enc[] = new AudioFormat.Encoding[1]; 109 enc[0] = AudioFormat.Encoding.PCM_SIGNED; 110 return enc; 111 112 } else { 113 return new AudioFormat.Encoding[0]; 114 } 115 116 } else { 117 return new AudioFormat.Encoding[0]; 118 } 119 } 120 121 /** 122 */ 123 public AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat){ 124 if( (targetEncoding.equals( AudioFormat.Encoding.PCM_SIGNED ) && sourceFormat.getEncoding().equals( AudioFormat.Encoding.ALAW)) || 125 (targetEncoding.equals( AudioFormat.Encoding.ALAW) && sourceFormat.getEncoding().equals( AudioFormat.Encoding.PCM_SIGNED)) ) { 126 return getOutputFormats( sourceFormat ); 127 } else { 128 return new AudioFormat[0]; 129 } 130 } 131 132 /** 133 */ 134 public AudioInputStream getAudioInputStream(AudioFormat.Encoding targetEncoding, AudioInputStream sourceStream){ 135 AudioFormat sourceFormat = sourceStream.getFormat(); 136 AudioFormat.Encoding sourceEncoding = sourceFormat.getEncoding(); 137 138 if( sourceEncoding.equals( targetEncoding ) ) { 139 return sourceStream; 140 } else { 141 AudioFormat targetFormat = null; 142 if( !isConversionSupported(targetEncoding,sourceStream.getFormat()) ) { 143 throw new IllegalArgumentException("Unsupported conversion: " + sourceStream.getFormat().toString() + " to " + targetEncoding.toString()); 144 } 145 if( sourceEncoding.equals( AudioFormat.Encoding.ALAW ) && 146 targetEncoding.equals( AudioFormat.Encoding.PCM_SIGNED ) ) { 147 148 targetFormat = new AudioFormat( targetEncoding, 149 sourceFormat.getSampleRate(), 150 16, 151 sourceFormat.getChannels(), 152 2*sourceFormat.getChannels(), 153 sourceFormat.getSampleRate(), 154 sourceFormat.isBigEndian()); 155 156 } else if( sourceEncoding.equals( AudioFormat.Encoding.PCM_SIGNED ) && 157 targetEncoding.equals( AudioFormat.Encoding.ALAW ) ) { 158 159 targetFormat = new AudioFormat( targetEncoding, 160 sourceFormat.getSampleRate(), 161 8, 162 sourceFormat.getChannels(), 163 sourceFormat.getChannels(), 164 sourceFormat.getSampleRate(), 165 false); 166 } else { 167 throw new IllegalArgumentException("Unsupported conversion: " + sourceStream.getFormat().toString() + " to " + targetEncoding.toString()); 168 } 169 return getAudioInputStream( targetFormat, sourceStream ); 170 } 171 } 172 173 /** 174 * use old code... 175 */ 176 public AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream){ 177 return getConvertedStream( targetFormat, sourceStream ); 178 } 179 180 181 // OLD CODE 182 183 184 /** 185 * Opens the codec with the specified parameters. 186 * @param stream stream from which data to be processed should be read 187 * @param outputFormat desired data format of the stream after processing 188 * @return stream from which processed data may be read 189 * @throws IllegalArgumentException if the format combination supplied is 190 * not supported. 191 */ 192 /* public AudioInputStream getConvertedStream(AudioFormat outputFormat, AudioInputStream stream) { */ 193 private AudioInputStream getConvertedStream(AudioFormat outputFormat, AudioInputStream stream) { 194 195 AudioInputStream cs = null; 196 AudioFormat inputFormat = stream.getFormat(); 197 198 if( inputFormat.matches(outputFormat) ) { 199 cs = stream; 200 } else { 201 cs = (AudioInputStream) (new AlawCodecStream(stream, outputFormat)); 202 } 203 204 return cs; 205 } 206 207 /** 208 * Obtains the set of output formats supported by the codec 209 * given a particular input format. 210 * If no output formats are supported for this input format, 211 * returns an array of length 0. 212 * @return array of supported output formats. 213 */ 214 /* public AudioFormat[] getOutputFormats(AudioFormat inputFormat) { */ 215 private AudioFormat[] getOutputFormats(AudioFormat inputFormat) { 216 217 218 Vector formats = new Vector(); 219 AudioFormat format; 220 221 if ( AudioFormat.Encoding.PCM_SIGNED.equals(inputFormat.getEncoding())) { 222 format = new AudioFormat(AudioFormat.Encoding.ALAW, 223 inputFormat.getSampleRate(), 224 8, 225 inputFormat.getChannels(), 226 inputFormat.getChannels(), 227 inputFormat.getSampleRate(), 228 false ); 229 formats.addElement(format); 230 } 231 232 if (AudioFormat.Encoding.ALAW.equals(inputFormat.getEncoding())) { 233 format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 234 inputFormat.getSampleRate(), 235 16, 236 inputFormat.getChannels(), 237 inputFormat.getChannels()*2, 238 inputFormat.getSampleRate(), 239 false ); 240 formats.addElement(format); 241 format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 242 inputFormat.getSampleRate(), 243 16, 244 inputFormat.getChannels(), 245 inputFormat.getChannels()*2, 246 inputFormat.getSampleRate(), 247 true ); 248 formats.addElement(format); 249 } 250 251 AudioFormat[] formatArray = new AudioFormat[formats.size()]; 252 for (int i = 0; i < formatArray.length; i++) { 253 formatArray[i] = (AudioFormat)(formats.elementAt(i)); 254 } 255 return formatArray; 256 } 257 258 259 class AlawCodecStream extends AudioInputStream { 260 261 // tempBuffer required only for encoding (when encode is true) 262 private static final int tempBufferSize = 64; 263 private byte tempBuffer [] = null; 264 265 /** 266 * True to encode to a-law, false to decode to linear 267 */ 268 boolean encode = false; 269 270 AudioFormat encodeFormat; 271 AudioFormat decodeFormat; 272 273 byte tabByte1[] = null; 274 byte tabByte2[] = null; 275 int highByte = 0; 276 int lowByte = 1; 277 278 AlawCodecStream(AudioInputStream stream, AudioFormat outputFormat) { 279 280 super(stream, outputFormat, -1); 281 282 AudioFormat inputFormat = stream.getFormat(); 283 284 // throw an IllegalArgumentException if not ok 285 if ( ! (isConversionSupported(outputFormat, inputFormat)) ) { 286 287 throw new IllegalArgumentException("Unsupported conversion: " + inputFormat.toString() + " to " + outputFormat.toString()); 288 } 289 290 //$$fb 2002-07-18: fix for 4714846: JavaSound ULAW (8-bit) encoder erroneously depends on endian-ness 291 boolean PCMIsBigEndian; 292 293 // determine whether we are encoding or decoding 294 if (AudioFormat.Encoding.ALAW.equals(inputFormat.getEncoding())) { 295 encode = false; 296 encodeFormat = inputFormat; 297 decodeFormat = outputFormat; 298 PCMIsBigEndian = outputFormat.isBigEndian(); 299 } else { 300 encode = true; 301 encodeFormat = outputFormat; 302 decodeFormat = inputFormat; 303 PCMIsBigEndian = inputFormat.isBigEndian(); 304 tempBuffer = new byte[tempBufferSize]; 305 } 306 307 if (PCMIsBigEndian) { 308 tabByte1 = ALAW_TABH; 309 tabByte2 = ALAW_TABL; 310 highByte = 0; 311 lowByte = 1; 312 } else { 313 tabByte1 = ALAW_TABL; 314 tabByte2 = ALAW_TABH; 315 highByte = 1; 316 lowByte = 0; 317 } 318 319 // set the AudioInputStream length in frames if we know it 320 if (stream instanceof AudioInputStream) { 321 frameLength = ((AudioInputStream)stream).getFrameLength(); 322 } 323 324 // set framePos to zero 325 framePos = 0; 326 frameSize = inputFormat.getFrameSize(); 327 if( frameSize==AudioSystem.NOT_SPECIFIED ) { 328 frameSize=1; 329 } 330 } 331 332 333 /* 334 * $$jb 2/23/99 335 * Used to determine segment number in aLaw encoding 336 */ 337 private short search(short val, short table[], short size) { 338 for(short i = 0; i < size; i++) { 339 if (val <= table[i]) { return i; } 340 } 341 return size; 342 } 343 344 /** 345 * Note that this won't actually read anything; must read in 346 * two-byte units. 347 */ 348 public int read() throws IOException { 349 350 byte[] b = new byte[1]; 351 return (int)read(b, 0, b.length); 352 } 353 354 355 public int read(byte[] b) throws IOException { 356 357 return read(b, 0, b.length); 358 } 359 360 public int read(byte[] b, int off, int len) throws IOException { 361 362 // don't read fractional frames 363 if( len%frameSize != 0 ) { 364 len -= (len%frameSize); 365 } 366 367 if (encode) { 368 369 short QUANT_MASK = 0xF; 370 short SEG_SHIFT = 4; 371 short mask; 372 short seg; 373 int adj; 374 int i; 375 376 short sample; 377 byte enc; 378 379 int readCount = 0; 380 int currentPos = off; 381 int readLeft = len*2; 382 int readLen = ( (readLeft>tempBufferSize) ? tempBufferSize : readLeft ); 383 384 while ((readCount = super.read(tempBuffer,0,readLen))>0) { 385 386 for (i = 0; i < readCount; i+=2) { 387 388 /* Get the sample from the tempBuffer */ 389 sample = (short)(( (tempBuffer[i + highByte]) << 8) & 0xFF00); 390 sample |= (short)( (tempBuffer[i + lowByte]) & 0xFF); 391 392 if(sample >= 0) { 393 mask = 0xD5; 394 } else { 395 mask = 0x55; 396 sample = (short)(-sample - 8); 397 } 398 /* Convert the scaled magnitude to segment number. */ 399 seg = search(sample, seg_end, (short) 8); 400 /* 401 * Combine the sign, segment, quantization bits 402 */ 403 if (seg >= 8) { /* out of range, return maximum value. */ 404 enc = (byte) (0x7F ^ mask); 405 } else { 406 enc = (byte) (seg << SEG_SHIFT); 407 if(seg < 2) { 408 enc |= (byte) ( (sample >> 4) & QUANT_MASK); 409 } else { 410 enc |= (byte) ( (sample >> (seg + 3)) & QUANT_MASK ); 411 } 412 enc ^= mask; 413 } 414 /* Now put the encoded sample where it belongs */ 415 b[currentPos] = enc; 416 currentPos++; 417 } 418 /* And update pointers and counters for next iteration */ 419 readLeft -= readCount; 420 readLen = ( (readLeft>tempBufferSize) ? tempBufferSize : readLeft ); 421 } 422 423 if( currentPos==off && readCount<0 ) { // EOF or error 424 return readCount; 425 } 426 427 return (currentPos - off); /* Number of bytes written to new buffer */ 428 429 } else { 430 431 int i; 432 int readLen = len/2; 433 int readOffset = off + len/2; 434 int readCount = super.read(b, readOffset, readLen); 435 436 for (i = off; i < (off + (readCount*2)); i+=2) { 437 b[i] = (byte)tabByte1[b[readOffset] & 0xFF]; 438 b[i+1] = (byte)tabByte2[b[readOffset] & 0xFF]; 439 readOffset++; 440 } 441 442 if( readCount<0 ) { // EOF or error 443 return readCount; 444 } 445 446 return (i - off); 447 } 448 } 449 } // end class AlawCodecStream 450 } // end class ALAW