/*
AudioUtils.java
Copyright (c) 2015 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.deviceplugin.webrtc.util;
import android.util.Log;
import org.deviceconnect.android.deviceplugin.webrtc.BuildConfig;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author NTT DOCOMO, INC.
*/
public final class AudioUtils {
/**
* Tag for debugging.
*/
private static final String TAG = "AUDIO";
/**
* Defined the monaural.
*/
public static final int MONO = 1;
/**
* Defined the stereo.
*/
public static final int STEREO = 2;
/**
* Defined the 8bit depth.
*/
public static final int BIT_DEPTH_8BYTE = 1;
/**
* Defined the 16bit depth.
*/
public static final int BIT_DEPTH_16SHORT = 2;
/**
* Defined the 32bit depth.
*/
public static final int BIT_DEPTH_32FLOAT = 3;
/**
* Defined the sample rate.
*/
private static final int[] SAMPLE_RATE = {
48000,
44100,
32000,
22050
};
/**
* Defined the channels.
*/
private static final int[] CHANNELS = {
MONO, STEREO
};
private static final int[] BIT_DEPTH = {
BIT_DEPTH_8BYTE,
BIT_DEPTH_16SHORT,
BIT_DEPTH_32FLOAT
};
/**
* Convert from array of byte to array of short.
* @param byteArray byte array
* @return short array
*/
public static short[] byteToShort(final byte[] byteArray) {
short[] shortOut = new short[byteArray.length / 2];
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shortOut);
return shortOut;
}
/**
* Convert from array of short to array of float.
* @param shortArray short array
* @return float array
*/
public static float[] shortToFloat(final short[] shortArray) {
float[] floatOut = new float[shortArray.length];
for (int i = 0; i < shortArray.length; i++) {
floatOut[i] = shortArray[i];
}
return floatOut;
}
/**
* Convert from singed 16-bit PCM to 32-bit float PCM.
* @param byteArray byte array
* @return byte array
*/
public static byte[] convert16BitTo32Bit(final byte[] byteArray) {
float[] audioDataF = shortToFloat(byteToShort(byteArray));
for (int i = 0; i < audioDataF.length; i++) {
audioDataF[i] /= 32768.0;
}
FloatBuffer fb = FloatBuffer.wrap(audioDataF);
ByteBuffer byteBuffer = ByteBuffer.allocate(fb.capacity() * 4);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
byteBuffer.asFloatBuffer().put(fb);
return byteBuffer.array();
}
/**
* Gets the list of AudioFormat.
* @return list of AudioFormat
*/
public static List<AudioFormat> getSupportedFormats() {
List<AudioFormat> list = new ArrayList<>();
for (int channel : CHANNELS) {
for (int rate : SAMPLE_RATE) {
AudioFormat format = new AudioFormat(rate, channel, BIT_DEPTH_32FLOAT, false);
list.add(format);
}
}
return list;
}
/**
* Gets the default AudioFormat.
* @return AudioFormat
*/
public static AudioFormat getDefaultFormat() {
return new AudioFormat(SAMPLE_RATE[0], CHANNELS[0], BIT_DEPTH_32FLOAT, false);
}
/**
* Converts the AudioFormat to String.
* @param format AudioFormat
* @return String
*/
public static String formatToText(final AudioFormat format) {
StringBuilder builder = new StringBuilder();
builder.append("SampleRate:[" + format.getSampleRate() + "]");
builder.append(",");
builder.append("Channels:[" + format.getChannels() + "]");
builder.append(",");
builder.append("BitDepth:[" + format.getBitDepth() + "]");
builder.append(",");
builder.append("Processing:[" + format.isNoAudioProcessing() + "]");
return builder.toString();
}
/**
* Converts the String to AudioFormat.
* @param text String
* @return AudioFormat
*/
public static AudioFormat textToFormat(final String text) {
if (text == null || text.length() == 0) {
return null;
}
String[] txt = text.split(",");
if (txt.length != 4) {
if (BuildConfig.DEBUG) {
Log.e(TAG, "text format is invalid.");
}
return null;
}
try {
String sampleRateStr = txt[0].substring("SampleRate:[".length(), txt[0].length() - 1);
String channelsStr = txt[1].substring("Channels:[".length(), txt[1].length() - 1);
String depthStr = txt[2].substring("BitDepth:[".length(), txt[2].length() - 1);
String noAudioProcessingStr = txt[3].substring("Processing:[".length(), txt[3].length() - 1);
int sampleRate = Integer.parseInt(sampleRateStr);
int channels = Integer.parseInt(channelsStr);
int depth = Integer.parseInt(depthStr);
boolean noAudioProcessing = noAudioProcessingStr.equals("true");
return new AudioFormat(sampleRate, channels, depth, noAudioProcessing);
} catch (Exception e) {
if (BuildConfig.DEBUG) {
Log.e(TAG, "", e);
}
return null;
}
}
/**
* AudioFormat.
*
* @author NTT DOCOMO, INC.
*/
public static class AudioFormat {
/**
* The Sample Rate.
*/
private int mSampleRate;
/**
* The number of channels.
*/
private int mChannels;
/**
* The bit depth.
*/
private int mBitDepth;
/**
* The Audio processing flag.
*/
private boolean mNoAudioProcessing;
public AudioFormat(final int sampleRate, final int channels, final int bitDepth, final boolean noAudioProcessing) {
mSampleRate = sampleRate;
mChannels = channels;
mBitDepth = bitDepth;
mNoAudioProcessing = noAudioProcessing;
}
/**
* Gets the sample rate.
* @return sample rate
*/
public int getSampleRate() {
return mSampleRate;
}
/**
* Gets the number of channels.
* @return the number of channels
*/
public int getChannels() {
return mChannels;
}
/**
* Gets the bit depth.
* @return the bit depth.
*/
public int getBitDepth() {
return mBitDepth;
}
public boolean isNoAudioProcessing() {
return mNoAudioProcessing;
}
public void setNoAudioProcessing(boolean noAudioProcessing) {
mNoAudioProcessing = noAudioProcessing;
}
@Override
public String toString() {
return formatToText(this);
}
}
}