我正在从事一个涉及音频处理的项目。
我正在从文件中提取一段音频,然后想要对其进行一些处理。问题是我将音频数据作为字节数组获取,而我的处理是在 double 数组上(后来也在复杂数组上...)。
我的问题是如何正确地将接收到的字节数组转换为 double 数组?
这是我的输入代码:
AudioFormat format = new AudioFormat(8000, 16, 1, true, true);
AudioInputStream in = AudioSystem.getAudioInputStream(WAVfile);
AudioInputStream din = null;
AudioFormat decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
8000,
16,
1,
2,
8000,
true);
din = AudioSystem.getAudioInputStream(decodedFormat, in);
TargetDataLine fileLine = AudioSystem.getTargetDataLine(decodedFormat);
fileLine .open(format);
fileLine .start();
int numBytesRead;
byte[] targetData = new byte[256]; // (samplingRate / 1000) * 32ms
while (true) {
numBytesRead = din.read(targetData, 0, targetData.length);
if (numBytesRead == -1) {
break;
}
double[] convertedData;
// Conversion code goes here...
processAudio(convertedData);
}
到目前为止,我已经针对该站点和其他站点对不同问题的不同答案进行了研究。我尝试使用ByteBuffer和位转换,但是它们都没有给我看似正确的结果(我中的另一个成员对Python中的同一文件执行了相同的操作,因此我可以引用结果应为大概是...
我想念什么?如何正确将字节转换为 double ?如果我只想在targetData中捕获文件的32ms,那么targerData的长度应该是多少?那么,convertedData的长度将是多少?
提前致谢。
最佳答案
使用NIO缓冲区进行的转换不应该那么难。您所要做的就是应用一个因数从16位范围到[-1.0…1.0]
范围进行规范化。
好吧,it isn’t so easy,但是出于大多数实际目的,决定一个因素就足够了:
AudioFormat decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
8000, 16, 1, 2, 8000, true);
try(AudioInputStream in = AudioSystem.getAudioInputStream(WAVfile);
AudioInputStream din = AudioSystem.getAudioInputStream(decodedFormat, in);
ReadableByteChannel inCh = Channels.newChannel(din)) {
ByteBuffer inBuf=ByteBuffer.allocate(256);
final double factor=2.0/(1<<16);
while(inCh.read(inBuf) != -1) {
inBuf.flip();
double[] convertedData=new double[inBuf.remaining()/2];
DoubleBuffer outBuf=DoubleBuffer.wrap(convertedData);
while(inBuf.remaining()>=2) {
outBuf.put(inBuf.getShort()*factor);
}
assert !outBuf.hasRemaining();
inBuf.compact();
processAudio(convertedData);
}
}
上面的解决方案有效地使用了
…/(double)0x8000
变体。由于我不知道processAudio
对提供的缓冲区有什么作用,例如无论是否保留对它的引用,循环都会在每次迭代中分配一个新的缓冲区,但是将其更改为可重用的缓冲区应该很容易。使用预分配的缓冲区时,您只需要注意读/转换后的double的实际数量即可。