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