Java 如何转换使用Android创建的16位音频';通过位移位将音频记录转换为12位音频?
我正在尝试将16位音频转换为12位音频。然而,我对这种转换非常缺乏经验,并且相信我的方法可能不正确或有缺陷 作为下面代码片段的上下文,该用例是一个Android应用程序,用户可以对其说话,音频被传输到IoT设备以立即播放。IoT设备要求音频为单声道12位、8k采样率、小尾端、无符号,存储在前12位(0-11)和最后4位(12-15)中的数据为零。音频数据需要以1000字节的数据包形式接收 通过使用AudioRecord在Android应用程序中创建音频。其实例化如下:Java 如何转换使用Android创建的16位音频';通过位移位将音频记录转换为12位音频?,java,android,audio,bit-manipulation,audiorecord,Java,Android,Audio,Bit Manipulation,Audiorecord,我正在尝试将16位音频转换为12位音频。然而,我对这种转换非常缺乏经验,并且相信我的方法可能不正确或有缺陷 作为下面代码片段的上下文,该用例是一个Android应用程序,用户可以对其说话,音频被传输到IoT设备以立即播放。IoT设备要求音频为单声道12位、8k采样率、小尾端、无符号,存储在前12位(0-11)和最后4位(12-15)中的数据为零。音频数据需要以1000字节的数据包形式接收 通过使用AudioRecord在Android应用程序中创建音频。其实例化如下: int bufferSiz
int bufferSize = 1000;
this.audioRecord = new AudioRecord(
MediaRecorder.AudioSource.MIC,
8000,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize
);
在while循环中,音频记录由1000字节的数据包读取,并根据用例中的规范进行修改。不确定该部分是否相关,但为了完整性:
byte[] buffer = new byte[1000];
audioRecord.read(buffer, 0, buffer.length);
byte[] modifiedBytes = convert16BitTo12Bit(buffer);
然后修改后的字节被发送到设备
下面是修改字节的方法。基本上,为了符合规范,我移动每个16位集合中的位(抛出最低有效值4),并在最后四个点上添加零。我是通过位集来实现的
/**
* Takes a byte array presented as 16 bit audio and converts it to 12 bit audio through bit
* manipulation. Packets must be of 1000 bytes or no manipulation will occur and the input
* will be immediately returned.
*/
private byte[] convert16BitTo12Bit(byte[] input) {
if (input.length == 1000) {
for (int i = 0; i < input.length; i += 2) {
Log.d(TAG, "convert16BitTo12Bit: pass #" + (i / 2));
byte[] chunk = new byte[2];
System.arraycopy(input, i, chunk, 0, 2);
if (!isEmptyByteArray(chunk)) {
byte[] modifiedBytes = convertChunk(chunk);
System.arraycopy(
modifiedBytes,
0,
input,
i,
modifiedBytes.length
);
}
}
return input;
}
Log.d(TAG, "convert16BitTo12Bit: Failed - input is not 1000 in length; it is " + input.length);
return input;
}
/**
* Converts 2 bytes 16 bit audio into 12 bit audio. If the input is not 2 bytes, the input
* will be returned without manipulation.
*/
private byte[] convertChunk(byte[] chunk) {
if (chunk.length == 2) {
BitSet bitSet = BitSet.valueOf(chunk);
Log.d(TAG, "convertChunk: bitSet starts as " + bitSet.toString());
modifyBitSet(bitSet);
Log.d(TAG, "convertChunk: bitSet ends as " + bitSet.toString());
return bitSet.toByteArray();
}
Log.d(TAG, "convertChunk: Failed = chunk is not 2 in length; it is " + chunk.length);
return chunk;
}
/**
* Removes the first four bits and shifts the rest to leave the final four bits as 0.
*/
private void modifyBitSet(BitSet bitSet) {
for (int i = 4; i < bitSet.length(); i++) {
bitSet.set(i - 4, bitSet.get(i));
}
if (bitSet.length() > 8) {
bitSet.clear(12, 16);
} else {
bitSet.clear(4, 8);
}
}
/**
* Returns true if the byte array input contains all zero bits.
*/
private boolean isEmptyByteArray(byte[] input) {
BitSet bitSet = BitSet.valueOf(input);
return bitSet.isEmpty();
}
然而,当通过设备播放时,听起来更糟,我甚至不认为我能辨认出任何单词
显然,我的方法在这里行不通中心问题是,如果最后四位必须为零,如何将16位块转换为12位音频并保持音频质量?此外,考虑到我使用AudioRecord获取音频的更大方法,上述问题的解决方案是否适合此用例?
请让我知道,如果有什么我可以提供澄清这些问题和我的意图
考虑到音频是16位,但必须更改为12位,结尾有四个零,因此必须在某个地方抛出四位
是的,当然,没有别的办法了,是吗
这是我现在可以迅速解决的问题。当然还没有完全测试过。仅在输入2和4字节时测试。我把它留给你去测试
//Reminder :: Convert as many as possible.
//Reminder :: To calculate the required size for store:
//if((bytes.length & 1) == 0) Math.round((bytes.length * 6) / 8F) : Math.round(((bytes.length - 1) * 6) / 8F).
//Return :: Amount of converted bytes.
public static final int convert16BitTo12Bit(final byte[] bytes, final byte[] store)
{
final int size = bytes.length;
int storeIndex = 0;
//Copy the first 2 bytes into store.
store[storeIndex++] = bytes[0]; store[storeIndex] = bytes[1];
if(size < 4) {
store[storeIndex] = (byte)(store[storeIndex] & 0xF0);
return 2;
}
final int result;
final byte tmp;
// 11111111 11110000 00000000 00000000
//+ 11111111 11110000 (<< 12)
//= 11111111 11111111 11111111 00000000 (1)
//-----------------------------------------
// 11111111 00000000 00000000 00000000 (1)
//+ 11111111 11110000 (<< 16)
//= 11111111 11111111 11110000 00000000 (2)
//-----------------------------------------
// 11110000 00000000 00000000 00000000 (2)
//+ 1111 11111111 0000 (<< 20)
//= 11111111 11111111 00000000 00000000 (3)
//-----------------------------------------
// 00000000 00000000 00000000 00000000 (3)
//+ 11111111 11110000 (<< 24)
//= 11111111 11110000 00000000 00000000
for(int i=2, shiftBits = 12; i < size; i += 2) {
if(shiftBits == 24) {
//Copy 2 bytes from bytes[] into store[] and move on.
store[storeIndex] = bytes[i];
//Never store byte 0 (Garbage).
tmp = (byte)(bytes[i + 1] & 0xF0); //Bit order: 11110000.
if(tmp != 0) store[++storeIndex] = tmp;
shiftBits = 12; //Reset
} else if(shiftBits == 20) {
result = ((store[storeIndex - 1] << 24) | ((store[storeIndex] & 0xFF) << 16))
| (((bytes[i] & 0xFF) << 20) | ((bytes[i + 1] & 0xFF) << 12));
store[storeIndex] = (byte)((result >> 24) & 0xFF);
tmp = (byte)((result >> 16) & 0xFF);
//Never store byte 0 (Garbage).
if(tmp != 0) store[++storeIndex] = tmp;
shiftBits = 24;
} else if(shiftBits == 16) {
result = ((store[storeIndex - 1] << 24) | ((store[storeIndex] & 0xFF) << 16))
| (((bytes[i] & 0xFF) << 16) | ((bytes[i + 1] & 0xFF) << 8));
store[storeIndex] = (byte)((result >> 16) & 0xFF);
tmp = (byte)((result >> 8) & 0xF0);
//Never store byte 0 (Garbage).
if(tmp != 0) store[++storeIndex] = tmp;
shiftBits = 20;
} else {
result = ((store[storeIndex - 1] << 24) | ((store[storeIndex] & 0xFF) << 16))
| (((bytes[i] & 0xFF) << 12) | ((bytes[i + 1] & 0xFF) << 4));
store[storeIndex] = (byte)((result >> 16) & 0xFF);
tmp = (byte)((result >> 8) & 0xFF);
//Never store byte 0 (Garbage).
if(tmp != 0) store[++storeIndex] = tmp;
shiftBits = 16;
}
}
return ++storeIndex;
}
- 中间的管道或竖条是将我们刚刚创建的两个整数合并为一个
byte[] buffer = new byte[1000];
byte[] store;
if((buffer.length & 1) == 0) { //Even.
store = new byte[Math.round((bytes.length * 6) / 8F)];
} else { //Odd.
store = new byte[Math.round(((bytes.length - 1) * 6) / 8F)];
}
audioRecord.read(buffer, 0, buffer.length);
int convertedByteSize = convert16BitTo12Bit(buffer, store);
System.out.println("size: " + convertedByteSize);
你确定你只需清除前4位,而不需要将12位彼此靠近
1111111110000 1111111111110000
-->11111111111111111111111111110000000
?@Darkman位修改对我来说都是很新的;我以前从来没有做过。。。所以我真的不确定什么才是理想的。我的经营理念是,最低有效的四位应该被删除,以保持最佳的音频质量。只要我不移动任何东西,这实际上是有效的。因此,换档过程中出现了一些问题。你能在这里阐明你的方法吗?您如何连接示例中的位?考虑到音频是16位的,但必须更改为12位,最后是4个零,因此必须在某个地方抛出4位;这声音听不懂。一个关于存储大小的问题来澄清意图:在“用法”代码块中,存储总是相同的大小,因为缓冲区总是在前一行的1000处开始。在该位置调用代码是故意的还是错误的?存储区的长度始终为750字节,而且可能数组的最后一个块(100+字节)总是完全为零。实际上,它不是从1000开始的。1000是名为buffer
的数组的大小,您希望传递给audioRecord
。它将始终从索引0处的缓冲区
转换字节。750字节-是的,1000个16位字节到12位字节需要多少空间。例如:完整的4字节16位需要4个字节[11111111 11111111][11111111 111111111]
,而不是12位[11111111 1111][11111111]
需要3个字节。这类似于24位与8位深度的图像,后者的大小更大。我建议您尝试更改bitSet.clear(12,16)代码>到位集。清除(8,12)代码>和位集。清除(4,8)代码>到位集。清除(0,4)
看看会发生什么。如果我现在理解正确,这个答案遗漏了问题中的零填充。这种方法是将四个字节中的两组16位合并为三个字节中的两组12位。因此,至少转换正在发生。但是,数据需要表示为16位中的12位(最后4位为0,因此4个字节中的12位[11111111 11110000][11111111 11110000]
。这就是为什么我对750的大小感到困惑的原因。我们该怎么做呢?750字节的结果是否应该通过其他方法进行零填充和移位?我不知道如何编辑注释,但我的意思是“但是,[在您的三字节示例中]数据需要表示为16位中的[两组]12位(最后四位为0,因此4个字节中有12位…”
result = ((store[storeIndex - 1] << 24) | ((store[storeIndex] & 0xFF) << 16))
| (((bytes[i] & 0xFF) << 20) | ((bytes[i + 1] & 0xFF) << 12));
((store[storeIndex - 1] << 24) | ((store[storeIndex] & 0xFF) << 16))
(((bytes[i] & 0xFF) << 20) | ((bytes[i + 1] & 0xFF) << 12));
(...) | (...)
byte[] buffer = new byte[1000];
byte[] store;
if((buffer.length & 1) == 0) { //Even.
store = new byte[Math.round((bytes.length * 6) / 8F)];
} else { //Odd.
store = new byte[Math.round(((bytes.length - 1) * 6) / 8F)];
}
audioRecord.read(buffer, 0, buffer.length);
int convertedByteSize = convert16BitTo12Bit(buffer, store);
System.out.println("size: " + convertedByteSize);