Path: blob/master/src/java.desktop/share/classes/com/sun/media/sound/AlawCodec.java
41161 views
/*1* Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.media.sound;2627import java.io.IOException;28import java.util.Objects;29import java.util.Vector;3031import javax.sound.sampled.AudioFormat;32import javax.sound.sampled.AudioFormat.Encoding;33import javax.sound.sampled.AudioInputStream;34import javax.sound.sampled.AudioSystem;35import javax.sound.sampled.spi.FormatConversionProvider;3637/**38* A-law encodes linear data, and decodes a-law data to linear data.39*40* @author Kara Kytle41*/42public final class AlawCodec extends FormatConversionProvider {4344/* Tables used for A-law decoding */4546private static final byte[] ALAW_TABH = new byte[256];47private static final byte[] ALAW_TABL = new byte[256];4849private static final short[] seg_end = {500xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF51};5253/**54* Initializes the decode tables.55*/56static {57for (int i=0;i<256;i++) {58int input = i ^ 0x55;59int mantissa = (input & 0xf ) << 4;60int segment = (input & 0x70) >> 4;61int value = mantissa+8;6263if(segment>=1)64value+=0x100;65if(segment>1)66value <<= (segment -1);6768if( (input & 0x80)==0 )69value = -value;7071ALAW_TABL[i] = (byte)value;72ALAW_TABH[i] = (byte)(value>>8);73}74}7576@Override77public AudioFormat.Encoding[] getSourceEncodings() {78return new Encoding[]{Encoding.ALAW, Encoding.PCM_SIGNED};79}8081@Override82public AudioFormat.Encoding[] getTargetEncodings() {83return getSourceEncodings();84}8586@Override87public AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat){8889if( sourceFormat.getEncoding().equals( AudioFormat.Encoding.PCM_SIGNED )) {9091if( sourceFormat.getSampleSizeInBits() == 16 ) {9293AudioFormat.Encoding[] enc = new AudioFormat.Encoding[1];94enc[0] = AudioFormat.Encoding.ALAW;95return enc;9697} else {98return new AudioFormat.Encoding[0];99}100} else if( sourceFormat.getEncoding().equals( AudioFormat.Encoding.ALAW ) ) {101102if( sourceFormat.getSampleSizeInBits() == 8 ) {103104AudioFormat.Encoding[] enc = new AudioFormat.Encoding[1];105enc[0] = AudioFormat.Encoding.PCM_SIGNED;106return enc;107108} else {109return new AudioFormat.Encoding[0];110}111112} else {113return new AudioFormat.Encoding[0];114}115}116117@Override118public AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat){119Objects.requireNonNull(sourceFormat);120if( (targetEncoding.equals( AudioFormat.Encoding.PCM_SIGNED ) && sourceFormat.getEncoding().equals( AudioFormat.Encoding.ALAW)) ||121(targetEncoding.equals( AudioFormat.Encoding.ALAW) && sourceFormat.getEncoding().equals( AudioFormat.Encoding.PCM_SIGNED)) ) {122return getOutputFormats( sourceFormat );123} else {124return new AudioFormat[0];125}126}127128@Override129public AudioInputStream getAudioInputStream(AudioFormat.Encoding targetEncoding, AudioInputStream sourceStream){130AudioFormat sourceFormat = sourceStream.getFormat();131AudioFormat.Encoding sourceEncoding = sourceFormat.getEncoding();132133if( !isConversionSupported(targetEncoding,sourceStream.getFormat()) ) {134throw new IllegalArgumentException("Unsupported conversion: " + sourceStream.getFormat().toString() + " to " + targetEncoding.toString());135}136if( sourceEncoding.equals( targetEncoding ) ) {137return sourceStream;138}139AudioFormat targetFormat = null;140if( sourceEncoding.equals( AudioFormat.Encoding.ALAW ) &&141targetEncoding.equals( AudioFormat.Encoding.PCM_SIGNED ) ) {142143targetFormat = new AudioFormat( targetEncoding,144sourceFormat.getSampleRate(),14516,146sourceFormat.getChannels(),1472*sourceFormat.getChannels(),148sourceFormat.getSampleRate(),149sourceFormat.isBigEndian());150151} else if( sourceEncoding.equals( AudioFormat.Encoding.PCM_SIGNED ) &&152targetEncoding.equals( AudioFormat.Encoding.ALAW ) ) {153154targetFormat = new AudioFormat( targetEncoding,155sourceFormat.getSampleRate(),1568,157sourceFormat.getChannels(),158sourceFormat.getChannels(),159sourceFormat.getSampleRate(),160false);161} else {162throw new IllegalArgumentException("Unsupported conversion: " + sourceStream.getFormat().toString() + " to " + targetEncoding.toString());163}164return getConvertedStream(targetFormat, sourceStream);165}166167@Override168public AudioInputStream getAudioInputStream(AudioFormat targetFormat, AudioInputStream sourceStream){169if (!isConversionSupported(targetFormat, sourceStream.getFormat()))170throw new IllegalArgumentException("Unsupported conversion: "171+ sourceStream.getFormat().toString() + " to "172+ targetFormat.toString());173return getConvertedStream( targetFormat, sourceStream );174}175176/**177* Opens the codec with the specified parameters.178* @param stream stream from which data to be processed should be read179* @param outputFormat desired data format of the stream after processing180* @return stream from which processed data may be read181* @throws IllegalArgumentException if the format combination supplied is182* not supported.183*/184private AudioInputStream getConvertedStream(AudioFormat outputFormat, AudioInputStream stream) {185186AudioInputStream cs = null;187AudioFormat inputFormat = stream.getFormat();188189if( inputFormat.matches(outputFormat) ) {190cs = stream;191} else {192cs = new AlawCodecStream(stream, outputFormat);193}194195return cs;196}197198/**199* Obtains the set of output formats supported by the codec200* given a particular input format.201* If no output formats are supported for this input format,202* returns an array of length 0.203* @return array of supported output formats.204*/205private AudioFormat[] getOutputFormats(AudioFormat inputFormat) {206207208Vector<AudioFormat> formats = new Vector<>();209AudioFormat format;210211if (inputFormat.getSampleSizeInBits() == 16212&& AudioFormat.Encoding.PCM_SIGNED.equals(inputFormat.getEncoding())) {213format = new AudioFormat(AudioFormat.Encoding.ALAW,214inputFormat.getSampleRate(), 8,215inputFormat.getChannels(),216inputFormat.getChannels(),217inputFormat.getSampleRate(), false);218formats.addElement(format);219}220if (inputFormat.getSampleSizeInBits() == 8221&& AudioFormat.Encoding.ALAW.equals(inputFormat.getEncoding())) {222format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,223inputFormat.getSampleRate(), 16,224inputFormat.getChannels(),225inputFormat.getChannels() * 2,226inputFormat.getSampleRate(), false);227formats.addElement(format);228format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,229inputFormat.getSampleRate(), 16,230inputFormat.getChannels(),231inputFormat.getChannels() * 2,232inputFormat.getSampleRate(), true);233formats.addElement(format);234}235236AudioFormat[] formatArray = new AudioFormat[formats.size()];237for (int i = 0; i < formatArray.length; i++) {238formatArray[i] = formats.elementAt(i);239}240return formatArray;241}242243244private final class AlawCodecStream extends AudioInputStream {245246// tempBuffer required only for encoding (when encode is true)247private static final int tempBufferSize = 64;248private byte[] tempBuffer = null;249250/**251* True to encode to a-law, false to decode to linear252*/253boolean encode = false;254255AudioFormat encodeFormat;256AudioFormat decodeFormat;257258byte[] tabByte1 = null;259byte[] tabByte2 = null;260int highByte = 0;261int lowByte = 1;262263AlawCodecStream(AudioInputStream stream, AudioFormat outputFormat) {264265super(stream, outputFormat, -1);266267AudioFormat inputFormat = stream.getFormat();268269// throw an IllegalArgumentException if not ok270if ( ! (isConversionSupported(outputFormat, inputFormat)) ) {271272throw new IllegalArgumentException("Unsupported conversion: " + inputFormat.toString() + " to " + outputFormat.toString());273}274275//$$fb 2002-07-18: fix for 4714846: JavaSound ULAW (8-bit) encoder erroneously depends on endian-ness276boolean PCMIsBigEndian;277278// determine whether we are encoding or decoding279if (AudioFormat.Encoding.ALAW.equals(inputFormat.getEncoding())) {280encode = false;281encodeFormat = inputFormat;282decodeFormat = outputFormat;283PCMIsBigEndian = outputFormat.isBigEndian();284} else {285encode = true;286encodeFormat = outputFormat;287decodeFormat = inputFormat;288PCMIsBigEndian = inputFormat.isBigEndian();289tempBuffer = new byte[tempBufferSize];290}291292if (PCMIsBigEndian) {293tabByte1 = ALAW_TABH;294tabByte2 = ALAW_TABL;295highByte = 0;296lowByte = 1;297} else {298tabByte1 = ALAW_TABL;299tabByte2 = ALAW_TABH;300highByte = 1;301lowByte = 0;302}303304// set the AudioInputStream length in frames if we know it305if (stream instanceof AudioInputStream) {306frameLength = stream.getFrameLength();307}308309// set framePos to zero310framePos = 0;311frameSize = inputFormat.getFrameSize();312if( frameSize==AudioSystem.NOT_SPECIFIED ) {313frameSize=1;314}315}316317318/*319* $$jb 2/23/99320* Used to determine segment number in aLaw encoding321*/322private short search(short val, short[] table, short size) {323for(short i = 0; i < size; i++) {324if (val <= table[i]) { return i; }325}326return size;327}328329/**330* Note that this won't actually read anything; must read in331* two-byte units.332*/333@Override334public int read() throws IOException {335336byte[] b = new byte[1];337return read(b, 0, b.length);338}339340@Override341public int read(byte[] b) throws IOException {342343return read(b, 0, b.length);344}345346@Override347public int read(byte[] b, int off, int len) throws IOException {348349// don't read fractional frames350if( len%frameSize != 0 ) {351len -= (len%frameSize);352}353354if (encode) {355356short QUANT_MASK = 0xF;357short SEG_SHIFT = 4;358short mask;359short seg;360int adj;361int i;362363short sample;364byte enc;365366int readCount = 0;367int currentPos = off;368int readLeft = len*2;369int readLen = ( (readLeft>tempBufferSize) ? tempBufferSize : readLeft );370371while ((readCount = super.read(tempBuffer,0,readLen))>0) {372373for (i = 0; i < readCount; i+=2) {374375/* Get the sample from the tempBuffer */376sample = (short)(( (tempBuffer[i + highByte]) << 8) & 0xFF00);377sample |= (short)( (tempBuffer[i + lowByte]) & 0xFF);378379if(sample >= 0) {380mask = 0xD5;381} else {382mask = 0x55;383sample = (short)(-sample - 8);384}385/* Convert the scaled magnitude to segment number. */386seg = search(sample, seg_end, (short) 8);387/*388* Combine the sign, segment, quantization bits389*/390if (seg >= 8) { /* out of range, return maximum value. */391enc = (byte) (0x7F ^ mask);392} else {393enc = (byte) (seg << SEG_SHIFT);394if(seg < 2) {395enc |= (byte) ( (sample >> 4) & QUANT_MASK);396} else {397enc |= (byte) ( (sample >> (seg + 3)) & QUANT_MASK );398}399enc ^= mask;400}401/* Now put the encoded sample where it belongs */402b[currentPos] = enc;403currentPos++;404}405/* And update pointers and counters for next iteration */406readLeft -= readCount;407readLen = ( (readLeft>tempBufferSize) ? tempBufferSize : readLeft );408}409410if( currentPos==off && readCount<0 ) { // EOF or error411return readCount;412}413414return (currentPos - off); /* Number of bytes written to new buffer */415416} else {417418int i;419int readLen = len/2;420int readOffset = off + len/2;421int readCount = super.read(b, readOffset, readLen);422423for (i = off; i < (off + (readCount*2)); i+=2) {424b[i] = tabByte1[b[readOffset] & 0xFF];425b[i+1] = tabByte2[b[readOffset] & 0xFF];426readOffset++;427}428429if( readCount<0 ) { // EOF or error430return readCount;431}432433return (i - off);434}435}436437@Override438public long skip(final long n) throws IOException {439// Implementation of this method assumes that we support440// encoding/decoding from/to 8/16 bits only441return encode ? super.skip(n * 2) / 2 : super.skip(n / 2) * 2;442}443} // end class AlawCodecStream444} // end class ALAW445446447