我正在打开一个targetdataline来接受给定格式的音频输入。 我启动并打开该行,我有一个填充字节的缓冲区。它在一个恒定循环上运行,直到外部参数发生变化。
现在对于固定的采样率和缓冲区大小,我希望这总是花费相同的时间来填充,即如果我的缓冲区大小为8000流的48000,并且我的采样率是48kHz,我会期望我的缓冲区总是需要1秒才能填满。但是我发现这种情况差异很大。
以下是我使用的代码:
DataLine.Info info1 = new DataLine.Info(TargetDataLine.class, format1);
try (TargetDataLine line = (TargetDataLine) m1.getLine(info1)) {
line.open(format1);
line.start();
while (!pauseInput){
long time1 = System.currentTimeMillis();
int numBytesRead1 = line.read(buffer1, 0, buffer1.length);
//chan1double = deinterleaveAudio(buffer1, chan1selectedchannel, chan1totalchannels);
long time2 = System.currentTimeMillis();
System.out.println(threadName + " Capture time = " + (time2-time1));
}
line.stop();
}
注释行是我想在每次缓冲区已满时运行的进程。我意识到我不能把它放在这里,因为它会中断流,所以我需要找到一种不同的方式来调用它,因此我已经注释掉了。
出于测试目的,我的缓冲区大小为4096.我的音频格式是48位16位,所以我希望我的字节缓冲区可以在42.6ms内填充。 ((1/48000)* 2048)。 (这是乘以缓冲区大小的一半,因为每个样本是两个字节)。然而,使用currentTimeMillies来测量每个传递,它将返回123ms和250ms,并在这些时间之间变化。
我在这里错过了一些我没有做过的事情吗?
编辑:我已经将代码复制到一个全新的应用程序中,该应用程序甚至没有GUI或附加任何内容。纯粹输出到控制台并查看发生了什么,确保没有后台线程干扰,并且确定相同的情况发生。预计填充时间为250ms的缓冲区在95%的时间内填充255-259ms。然而,偶尔这将下降到127毫秒(除非有一些奇怪的缓冲区事情发生,这在物理上是不可能的。这是java中某个地方的错误吗?
答案 0 :(得分:2)
我认为以这种方式调整时机并不是一个好主意。它取决于很多东西,例如bufferSize,混音器等。此外,您的应用程序正在使用混音器共享线路的缓冲区。如果您有实时处理,请将数据存储在循环缓冲区中,其长度足以容纳您需要的数据量。在另一个线程中,从循环缓冲区中读取所需数量的数据,并以恒定的时间间隔进行处理。因此,有时,您可能会在两次连续处理之间重叠或遗漏某些字节,但始终具有预期的字节数。
当您打开该行时,您可以使用open(format, bufferSize)
指定行的缓冲区大小,或者您可以通过以下方式检查实际的缓冲区大小
致电DataLine.getBufferSize()
。然后,您需要指定在通过TargetDataLine.read()
检索数据时提供的短缓冲区的大小。您的短缓冲区大小必须小于行的缓冲区大小。我认为短缓冲区大小为行缓冲区大小的1/4,1 / 8,1 / 16左右。另一个想法是在调用read()
之前检查可用字节DataLine.available()
。请注意,read()
是一个阻塞调用(但它不会阻塞行的缓冲区),即它将被卡住,直到读取了所请求的字节数。
对于应用程序和音频接口之间的低延迟直接通信,您可以考虑ASIO。
答案 1 :(得分:0)
对于任何看过同一问题的人,我都会得到一个答案,其中一半解释了正在发生的事情。
线程调度程序决定代码何时可以运行,这可能导致此变化10-20ms。在早些时候,这是多达70毫秒。 这并不意味着流丢失了样本,而只是该缓冲区不会提供连续流。因此,任何应用程序都会实时处理此数据并将其传递以写入音频输出流,需要了解这种额外的潜在延迟。
我仍在查看缓冲区填充时间短的原因,每隔四到五次。我被告知这可能与targetDataLine缓冲区大小不同,我的缓冲区大小不同,只是该缓冲区的剩余部分正在写入,但是我已将此更改为完全相同但仍然没有运气。