package org.jcodec.common;
import static org.jcodec.common.tools.MathUtil.clip;
import org.jcodec.api.NotSupportedException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* @author The JCodec project
*
*/
public class AudioUtil {
private static final float f24 = 8388607f;
private static final float f16 = 32767f;
public final static float r16 = 1f / 32768f;
public final static float r24 = 1f / 8388608f;
/**
* Converts PCM samples stored in buf and described with format to float
* array representation
*
* @param format
* Supported formats - *_*_S16_LE, *_*_S24_LE, *_*_S16_BE,
* *_*_S24_LE
* @param buf
* @param floatBuf
* @return Total number of samples read from the buffer
*/
public static void toFloat(AudioFormat format, ByteBuffer buf, FloatBuffer floatBuf) {
if (!format.isSigned())
throw new NotSupportedException("Unsigned PCM is not supported ( yet? ).");
if (format.getSampleSizeInBits() != 16 && format.getSampleSizeInBits() != 24)
throw new NotSupportedException(format.getSampleSizeInBits() + " bit PCM is not supported ( yet? ).");
if (format.isBigEndian()) {
if (format.getSampleSizeInBits() == 16) {
toFloat16BE(buf, floatBuf);
} else {
toFloat24BE(buf, floatBuf);
}
} else {
if (format.getSampleSizeInBits() == 16) {
toFloat16LE(buf, floatBuf);
} else {
toFloat24LE(buf, floatBuf);
}
}
}
/**
* Converts float PCM samples stored in floatBuf to integer representation
* according to format and stores them in buf
*
* @param format
* Supported formats - *_*_S16_LE, *_*_S24_LE, *_*_S16_BE,
* *_*_S24_LE
* @param buf
* @param floatBuf
* @return Total number of samples written to the buffer
*/
public static void fromFloat(FloatBuffer floatBuf, AudioFormat format, ByteBuffer buf) {
if (!format.isSigned())
throw new NotSupportedException("Unsigned PCM is not supported ( yet? ).");
if (format.getSampleSizeInBits() != 16 && format.getSampleSizeInBits() != 24)
throw new NotSupportedException(format.getSampleSizeInBits() + " bit PCM is not supported ( yet? ).");
if (format.isBigEndian()) {
if (format.getSampleSizeInBits() == 16) {
fromFloat16BE(buf, floatBuf);
} else {
fromFloat24BE(buf, floatBuf);
}
} else {
if (format.getSampleSizeInBits() == 16) {
fromFloat16LE(buf, floatBuf);
} else {
fromFloat24LE(buf, floatBuf);
}
}
}
private static void toFloat24LE(ByteBuffer buf, FloatBuffer out) {
while (buf.remaining() >= 3 && out.hasRemaining()) {
out.put(r24 * ((((buf.get() & 0xff) << 8) | ((buf.get() & 0xff) << 16) | ((buf.get() & 0xff) << 24)) >> 8));
}
}
private static void toFloat16LE(ByteBuffer buf, FloatBuffer out) {
while (buf.remaining() >= 2 && out.hasRemaining()) {
out.put(r16 * (short) ((buf.get() & 0xff) | ((buf.get() & 0xff) << 8)));
}
}
private static void toFloat24BE(ByteBuffer buf, FloatBuffer out) {
while (buf.remaining() >= 3 && out.hasRemaining()) {
out.put(r24 * ((((buf.get() & 0xff) << 24) | ((buf.get() & 0xff) << 16) | ((buf.get() & 0xff) << 8)) >> 8));
}
}
private static void toFloat16BE(ByteBuffer buf, FloatBuffer out) {
while (buf.remaining() >= 2 && out.hasRemaining()) {
out.put(r16 * (short) (((buf.get() & 0xff) << 8) | (buf.get() & 0xff)));
}
}
private static void fromFloat24LE(ByteBuffer buf, FloatBuffer _in) {
while (buf.remaining() >= 3 && _in.hasRemaining()) {
int val = clip((int) (_in.get() * f24), -0x800000, 0x7fffff) & 0xffffff;
buf.put((byte) val);
buf.put((byte) (val >> 8));
buf.put((byte) (val >> 16));
}
}
private static void fromFloat16LE(ByteBuffer buf, FloatBuffer _in) {
while (buf.remaining() >= 2 && _in.hasRemaining()) {
int val = clip((int) (_in.get() * f16), -0x8000, 0x7fff) & 0xffff;
buf.put((byte) val);
buf.put((byte) (val >> 8));
}
}
private static void fromFloat24BE(ByteBuffer buf, FloatBuffer _in) {
while (buf.remaining() >= 3 && _in.hasRemaining()) {
int val = clip((int) (_in.get() * f24), -0x800000, 0x7fffff) & 0xffffff;
buf.put((byte) (val >> 16));
buf.put((byte) (val >> 8));
buf.put((byte) val);
}
}
private static void fromFloat16BE(ByteBuffer buf, FloatBuffer _in) {
while (buf.remaining() >= 2 && _in.hasRemaining()) {
int val = clip((int) (_in.get() * f16), -0x8000, 0x7fff) & 0xffff;
buf.put((byte) (val >> 8));
buf.put((byte) val);
}
}
public static int fromInt(int[] data, int len, AudioFormat format, ByteBuffer buf) {
if (!format.isSigned())
throw new NotSupportedException("Unsigned PCM is not supported ( yet? ).");
if (format.getSampleSizeInBits() != 16 && format.getSampleSizeInBits() != 24)
throw new NotSupportedException(format.getSampleSizeInBits() + " bit PCM is not supported ( yet? ).");
if (format.isBigEndian()) {
if (format.getSampleSizeInBits() == 16) {
return fromInt16BE(buf, data, len);
} else {
return fromInt24BE(buf, data, len);
}
} else {
if (format.getSampleSizeInBits() == 16) {
return fromInt16LE(buf, data, len);
} else {
return fromInt24LE(buf, data, len);
}
}
}
private static int fromInt24LE(ByteBuffer buf, int[] out, int len) {
int samples = 0;
while (buf.remaining() >= 3 && samples < len) {
int val = out[samples++];
buf.put((byte) val);
buf.put((byte) (val >> 8));
buf.put((byte) (val >> 16));
}
return samples;
}
private static int fromInt16LE(ByteBuffer buf, int[] out, int len) {
int samples = 0;
while (buf.remaining() >= 2 && samples < len) {
int val = out[samples++];
buf.put((byte) val);
buf.put((byte) (val >> 8));
}
return samples;
}
private static int fromInt24BE(ByteBuffer buf, int[] out, int len) {
int samples = 0;
while (buf.remaining() >= 3 && samples < len) {
int val = out[samples++];
buf.put((byte) (val >> 16));
buf.put((byte) (val >> 8));
buf.put((byte) val);
}
return samples;
}
private static int fromInt16BE(ByteBuffer buf, int[] out, int len) {
int samples = 0;
while (buf.remaining() >= 2 && samples < len) {
int val = out[samples++];
buf.put((byte) (val >> 8));
buf.put((byte) val);
}
return samples;
}
public static int toInt(AudioFormat format, ByteBuffer buf, int[] samples) {
if (!format.isSigned())
throw new NotSupportedException("Unsigned PCM is not supported ( yet? ).");
if (format.getSampleSizeInBits() != 16 && format.getSampleSizeInBits() != 24)
throw new NotSupportedException(format.getSampleSizeInBits() + " bit PCM is not supported ( yet? ).");
if (format.isBigEndian()) {
if (format.getSampleSizeInBits() == 16) {
return toInt16BE(buf, samples);
} else {
return toInt24BE(buf, samples);
}
} else {
if (format.getSampleSizeInBits() == 16) {
return toInt16LE(buf, samples);
} else {
return toInt24LE(buf, samples);
}
}
}
private static int toInt24LE(ByteBuffer buf, int[] out) {
int samples = 0;
while (buf.remaining() >= 3 && samples < out.length) {
out[samples++] = (((buf.get() & 0xff) << 8) | ((buf.get() & 0xff) << 16) | ((buf.get() & 0xff) << 24)) >> 8;
}
return samples;
}
private static int toInt16LE(ByteBuffer buf, int[] out) {
int samples = 0;
while (buf.remaining() >= 2 && samples < out.length) {
out[samples++] = (short) ((buf.get() & 0xff) | ((buf.get() & 0xff) << 8));
}
return samples;
}
private static int toInt24BE(ByteBuffer buf, int[] out) {
int samples = 0;
while (buf.remaining() >= 3 && samples < out.length) {
out[samples++] = (((buf.get() & 0xff) << 24) | ((buf.get() & 0xff) << 16) | ((buf.get() & 0xff) << 8)) >> 8;
}
return samples;
}
private static int toInt16BE(ByteBuffer buf, int[] out) {
int samples = 0;
while (buf.remaining() >= 2 && samples < out.length) {
out[samples++] = (short) (((buf.get() & 0xff) << 8) | (buf.get() & 0xff));
}
return samples;
}
/**
* Interleaves audio samples in ins into outb using sample size from the
* format
*
* @param format
* @param ins
* @param outb
*/
public static void interleave(AudioFormat format, ByteBuffer[] ins, ByteBuffer outb) {
int bytesPerSample = format.getSampleSizeInBits() >> 3;
int bytesPerFrame = bytesPerSample * ins.length;
int max = 0;
for (int i = 0; i < ins.length; i++)
if (ins[i].remaining() > max)
max = ins[i].remaining();
for (int frames = 0; frames < max && outb.remaining() >= bytesPerFrame; frames++) {
for (int j = 0; j < ins.length; j++) {
if (ins[j].remaining() < bytesPerSample) {
for (int i = 0; i < bytesPerSample; i++)
outb.put((byte) 0);
} else {
for (int i = 0; i < bytesPerSample; i++) {
outb.put(ins[j].get());
}
}
}
}
}
/**
* Deinterleaves audio samples from inb into outs using sample size from
* format
*
* @param format
* @param inb
* @param outs
*/
public static void deinterleave(AudioFormat format, ByteBuffer inb, ByteBuffer[] outs) {
int bytesPerSample = format.getSampleSizeInBits() >> 3;
int bytesPerFrame = bytesPerSample * outs.length;
while (inb.remaining() >= bytesPerFrame) {
for (int j = 0; j < outs.length; j++) {
for (int i = 0; i < bytesPerSample; i++) {
outs[j].put(inb.get());
}
}
}
}
}