我正在实现一个使用JDK Version 8 Update 201实时(或至少尽可能接近实时)记录和分析音频的应用程序。在执行模拟该应用程序典型用例的测试时,我注意到在连续录制了几个小时的音频后,突然延迟了一到两秒钟。到目前为止,还没有明显的延迟。直到这个关键的记录点持续了几个小时才开始出现这种延迟。
要检查用于计时音频样本的代码是否错误,我注释掉了与计时有关的所有内容。这基本上使我离开了这个更新循环,该循环可以在准备好音频样本后立即提取它们(注意:Kotlin代码):
while (!isInterrupted) {
val audioData = read(sampleSize, false)
listener.audioFrameCaptured(audioData)
}
这是我的读取方法:
fun read(samples: Int, buffered: Boolean = true): AudioData {
//Allocate a byte array in which the read audio samples will be stored.
val bytesToRead = samples * format.frameSize
val data = ByteArray(bytesToRead)
//Calculate the maximum amount of bytes to read during each iteration.
val bufferSize = (line.bufferSize / BUFFER_SIZE_DIVIDEND / format.frameSize).roundToInt() * format.frameSize
val maxBytesPerCycle = if (buffered) bufferSize else bytesToRead
//Read the audio data in one or multiple iterations.
var bytesRead = 0
while (bytesRead < bytesToRead) {
bytesRead += (line as TargetDataLine).read(data, bytesRead, min(maxBytesPerCycle, bytesToRead - bytesRead))
}
return AudioData(data, format)
}
但是,即使没有我的帮助,问题也没有解决。因此,我继续进行一些实验,并让应用程序使用不同的音频格式运行,这导致了非常令人困惑的结果(我将使用PCM签名的16位立体声音频格式,其字节序少,采样率44100.0 Hz默认情况下,除非另有说明):
这些结果使我得出这样的结论:在此问题开始发生之前,我可以记录音频的时间取决于运行应用程序的机器以及字节率(即帧大小和采样率) )的音频格式。这个似乎成立(尽管到目前为止我还不能完全确认),因为如果我将2和3中所做的更改结合起来,我会假设我可以录制四次音频样本。长时间(大约在26到27小时之间),就像在延迟开始出现之前使用“默认”音频格式时一样。由于我还没有足够的时间让应用程序运行这么长时间,因此我只能说,由于时间限制,在不得不停止它之前,它可以正常运行约15个小时。因此,这一假设仍有待确认或否认。
根据项目要点13的结果,似乎整个问题仅在使用Windows时出现。因此,我认为它可能可能是javax.sound.sampled API特定于平台的部分中的错误。
即使我认为当这个问题开始发生时我可能已经找到了改变的方法,但我对结果并不满意。我可以定期关闭并重新打开该行,以免该问题开始出现。但是,这样做将导致我花费很少的时间,而我无法捕获音频样本。此外,Javadoc指出某些行在关闭后根本无法重新打开。因此,对于我而言,这不是一个好的解决方案。
理想情况下,整个问题都不应该发生。我是否完全缺少某些东西,或者我在使用javax.sound.sampled API可能遇到的限制?我该如何摆脱这个问题?
编辑:通过Xtreme Biker和gidds的建议,我创建了一个小示例应用程序。您可以在此Github repository中找到它。
答案 0 :(得分:6)
我在Java音频接口方面有(相当)丰富的经验。 以下几点可能会有助于指导您找到适当的解决方案: