/*
* LawDecoder.java
*
* This file is part of Tritonus: http://www.tritonus.org/
*/
/*
* Copyright (c) 2007 by Florian Bomers <http://www.bomers.de>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
/*
|<--- this code is formatted to fit into 80 columns --->|
*/
package org.tritonus.sampled.convert;
import java.util.Arrays;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import static javax.sound.sampled.AudioFormat.Encoding.*;
import static org.tritonus.sampled.convert.LawEncoder.*;
import org.tritonus.share.sampled.AudioFormats;
import org.tritonus.share.sampled.TConversionTool;
import org.tritonus.share.sampled.convert.TEncodingFormatConversionProvider;
import org.tritonus.share.sampled.convert.TSynchronousFilteredAudioInputStream;
/**
* This provider supports these conversions:
* <ul>
* <li>alaw,ulaw -> PCM 8 Signed
* <li>alaw,ulaw -> PCM 8 Unsigned -> alaw
* <li>alaw,ulaw -> PCM 16 signed big endian -> alaw
* <li>alaw,ulaw -> PCM 16 signed little endian -> alaw
* </ul>
* <p>
* FrameRate, SampleRate, Channels CANNOT be converted.
* <p>
* This new provider replaces UlawFormatConversionProvider and AlawFormatConversionProvider.
*
* @author Florian Bomers
*/
public class LawDecoder extends TEncodingFormatConversionProvider {
private static final AudioFormat[] INPUT_FORMATS = LAW_FORMATS;
private static final AudioFormat[] OUTPUT_FORMATS = {
new AudioFormat(PCM_SIGNED, ALL, 8, ALL, ALL, ALL, false),
new AudioFormat(PCM_SIGNED, ALL, 8, ALL, ALL, ALL, true),
new AudioFormat(PCM_UNSIGNED, ALL, 8, ALL, ALL, ALL, false),
new AudioFormat(PCM_UNSIGNED, ALL, 8, ALL, ALL, ALL, true),
new AudioFormat(PCM_SIGNED, ALL, 16, ALL, ALL, ALL, false),
new AudioFormat(PCM_SIGNED, ALL, 16, ALL, ALL, ALL, true),
};
/**
* Constructor.
*/
public LawDecoder() {
super(Arrays.asList(INPUT_FORMATS), Arrays.asList(OUTPUT_FORMATS));
}
@Override
public AudioInputStream getAudioInputStream(AudioFormat targetFormat,
AudioInputStream sourceStream) {
AudioFormat sourceFormat = sourceStream.getFormat();
// the non-conversion case
// TODO: does this work OK when some fields are
// AudioSystem.NOT_SPECIFIED ?
if (AudioFormats.matches(sourceFormat, targetFormat)) {
return sourceStream;
}
if (doMatch(targetFormat.getFrameRate(), sourceFormat.getFrameRate())
&& doMatch(targetFormat.getChannels(),
sourceFormat.getChannels())) {
if (doMatch(sourceFormat.getSampleSizeInBits(), 8)) {
if (sourceFormat.getEncoding().equals(ULAW)) {
// convert ULAW to the target format
return new FromUlawStream(sourceStream, targetFormat);
} else if (sourceFormat.getEncoding().equals(ALAW)) {
// convert ALAW to the target format
return new FromAlawStream(sourceStream, targetFormat);
}
}
}
throw new IllegalArgumentException("format conversion not supported");
}
// protected boolean isSupportedFormat(AudioFormat format) {
// return getConvertType(format)!=0;
// }
private static final AudioFormat createTargetFormat(AudioFormat src, AudioFormat dst) {
return new AudioFormat(dst.getEncoding(),
src.getSampleRate(),
dst.getSampleSizeInBits(),
src.getChannels(),
dst.getSampleSizeInBits() * src.getChannels() / 8,
src.getFrameRate(),
dst.isBigEndian());
}
class FromUlawStream extends TSynchronousFilteredAudioInputStream {
private int convertType;
public FromUlawStream(AudioInputStream sourceStream,
AudioFormat targetFormat) {
// transform the targetFormat so that
// FrameRate, SampleRate, and Channels match the sourceFormat
// we only retain encoding, samplesize and endian of targetFormat.
super(sourceStream, createTargetFormat(sourceStream.getFormat(), targetFormat));
convertType = getConvertType(getFormat(), ULAW8);
if (convertType == 0) {
throw new IllegalArgumentException("format conversion not supported");
}
if (targetFormat.getSampleSizeInBits() == 8) {
enableConvertInPlace();
}
}
@Override
protected int convert(byte[] inBuffer, byte[] outBuffer,
int outByteOffset, int inFrameCount) {
int sampleCount = inFrameCount * getFormat().getChannels();
switch (convertType) {
case UNSIGNED8:
TConversionTool.ulaw2pcm8(inBuffer, 0, outBuffer,
outByteOffset, sampleCount, false);
break;
case SIGNED8:
TConversionTool.ulaw2pcm8(inBuffer, 0, outBuffer,
outByteOffset, sampleCount, true);
break;
case BIG_ENDIAN16:
TConversionTool.ulaw2pcm16(inBuffer, 0, outBuffer,
outByteOffset, sampleCount, true);
break;
case LITTLE_ENDIAN16:
TConversionTool.ulaw2pcm16(inBuffer, 0, outBuffer,
outByteOffset, sampleCount, false);
break;
case ALAW8:
TConversionTool.ulaw2alaw(inBuffer, 0, outBuffer,
outByteOffset, sampleCount);
break;
}
return inFrameCount;
}
@Override
protected void convertInPlace(byte[] buffer, int byteOffset,
int frameCount) {
int sampleCount = frameCount * format.getChannels();
switch (convertType) {
case UNSIGNED8:
TConversionTool.ulaw2pcm8(buffer, byteOffset, sampleCount,
false);
break;
case SIGNED8:
TConversionTool.ulaw2pcm8(buffer, byteOffset, sampleCount, true);
break;
case ALAW8:
TConversionTool.ulaw2alaw(buffer, byteOffset, sampleCount);
break;
default:
throw new RuntimeException("FromUlawStream: Call to convertInPlace, "
+"but it cannot convert in place. (convertType=" + convertType + ")");
}
}
}
class FromAlawStream extends TSynchronousFilteredAudioInputStream {
private int convertType;
public FromAlawStream(AudioInputStream sourceStream,
AudioFormat targetFormat) {
// transform the targetFormat so that
// FrameRate, SampleRate, and Channels match the sourceFormat
// we only retain encoding, samplesize and endian of targetFormat.
super(sourceStream, createTargetFormat(sourceStream.getFormat(), targetFormat));
convertType = getConvertType(getFormat(), ALAW8);
if (convertType == 0) {
throw new IllegalArgumentException(
"format conversion not supported");
}
if (targetFormat.getSampleSizeInBits() == 8) {
enableConvertInPlace();
}
}
@Override
protected int convert(byte[] inBuffer, byte[] outBuffer,
int outByteOffset, int inFrameCount) {
int sampleCount = inFrameCount * getFormat().getChannels();
switch (convertType) {
case UNSIGNED8:
TConversionTool.alaw2pcm8(inBuffer, 0, outBuffer,
outByteOffset, sampleCount, false);
break;
case SIGNED8:
TConversionTool.alaw2pcm8(inBuffer, 0, outBuffer,
outByteOffset, sampleCount, true);
break;
case BIG_ENDIAN16:
TConversionTool.alaw2pcm16(inBuffer, 0, outBuffer,
outByteOffset, sampleCount, true);
break;
case LITTLE_ENDIAN16:
TConversionTool.alaw2pcm16(inBuffer, 0, outBuffer,
outByteOffset, sampleCount, false);
break;
case ULAW8:
TConversionTool.alaw2ulaw(inBuffer, 0, outBuffer,
outByteOffset, sampleCount);
break;
}
return inFrameCount;
}
@Override
protected void convertInPlace(byte[] buffer, int byteOffset,
int frameCount) {
int sampleCount = frameCount * format.getChannels();
switch (convertType) {
case UNSIGNED8:
TConversionTool.alaw2pcm8(buffer, byteOffset, sampleCount,
false);
break;
case SIGNED8:
TConversionTool.alaw2pcm8(buffer, byteOffset, sampleCount, true);
break;
case ULAW8:
TConversionTool.alaw2ulaw(buffer, byteOffset, sampleCount);
break;
default:
throw new RuntimeException("FromAlawStream: Call to convertInPlace, "
+"but it cannot convert in place. (convertType=" + convertType + ")");
}
}
}
}