3

我遇到了一个很奇怪的问题。我正在编写下面的部分代码。

try {
    while (!stopCapture) {
        // Read data from the internal buffer of the data line.
        int cnt = this.recLine.read(tempBuffer, 0, tempBuffer.length);
        if (cnt > 0) {
            // Save data in output stream object.
            byteArrayOutputStream.write(tempBuffer, 0, cnt);
            // System.out.println(" bytes " + tempBuffer[0]);
        }// end if
    }// ends while

    // AudioSystem.write(myAIS, targetType, outputFile);
    byteToWave(byteArrayOutputStream);
    byteArrayOutputStream.close();
} catch (IOException e) {
    // TODO provide runtime exception to reach it to presentation layer
    e.printStackTrace();
} catch (Exception ex) {
    ex.printStackTrace();
}

recLine 是我将声音录制到 byteArrayOutputStream 的 TargetDataLine。在我的测试中,它可以正常工作到 40-48 秒,但是当它每次达到 49 秒时,它都会抛出以下异常:

Exception in thread "Thread-5" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2786)
    at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:94)
    at com.actura.app.capture.ActuraRecorder.run(ActuraRecorder.java:109)
    at java.lang.Thread.run(Thread.java:619)

我使用这种技术是因为我需要记录的字节,并且从这些字节中我成功地在 UI 上绘制了一个波形。

在测试期间,我知道当 byteArrayOutputStream 的大小仅为 7.3 mb 时会引发异常。

我可以将此 byteArrayOutputStream 写入随机访问文件,然后每次达到其限制时重置此 byteArrayOutputStream 吗?

我如何提前知道 byteArrayOutputStream 的限制?

我检查了 Integer.MAX_VALUE,但正如我所说,它只是在 7.3 mb 处引发了一个异常,所以我无法访问 Integer.MAX_VALUE。

这个小程序在互联网上运行,所以设置内存大小对我没有帮助。如何将其设置到我客户的计算机上?

4

5 回答 5

6

您的问题是,JVM 内存不足,因为您将所有数据都保存在内存中。扩展堆空间暂时有帮助,直到您需要处理更长的音频。

一个想法可能是将ByteArrayOutputStreamby交换FileOutputStream为临时File。这不需要将所有音频数据都保存在内存中。一个更好的解决方案是Guavas FileBackedOutputStream,它仅在数据超过某个阈值时将数据存储在临时文件中。

另一种解决方案是直接更新每个缓冲区的波形。通过创建一个方法,该方法只接收包含音频数据的缓冲区并将其应用于波形的当前状态。这样,您根本不需要存储音频数据。

于 2012-08-22T12:35:23.220 回答
2

出现此问题是因为 Hotspot JVM 的堆在其大小上有一个固定的上限。默认限制取决于您的平台,但可能低至 40Mb。

您正在做的是从音频流中读取大量数据并将它们缓冲在内存中。鉴于堆有一个上限,您的应用程序最终会遇到该限制,结果将是OutOfMemoryError异常。

显而易见的解决方案是使用该-Xmx选项增加 JVM 的最大堆空间。(有关此选项的更多信息,请参阅 java 命令手册条目。)但是,您可以使 JVM 堆的大小有一个最终限制……取决于您的硬件、操作系统以及您是否使用 64位JVM。

您还可以将音频数据保存到文件中或尝试对其进行压缩或缩减,但这些都会使绘图变得更加复杂。

于 2012-08-22T12:45:00.573 回答
1

其他人讨论了问题的根源。

要解决它,要么:

  1. 一次绘制一点字节,或者..
  2. 存储一个BufferedImage,以小块的形式抓取字节,然后立即将它们绘制到图像上。

第一种技术可能会导致类似You Tube Video中看到的内容

声迹

于 2012-08-22T12:48:31.223 回答
0

您可以尝试增加JVM堆空间或尝试压缩声音数据以消耗更少的内存。

于 2012-08-22T12:29:33.207 回答
0

它肯定会填满你的记忆,并且在不同的计算机上它的行为会有所不同。您可以做的是使用一些FileOutputStream并将数据写入其中,一旦您的循环结束,从那里提取数据并将其转换为wav. 还将您的数据写入小字节包(最好是 4096).. 确保您的长度tempBuffer不会增加超过 4096。

希望这可以帮助..

于 2012-08-22T12:35:43.063 回答