3

I encountered a very strange problem. I am writing portion of the code below.

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 is the TargetDataLine from which I am recording sound in to byteArrayOutputStream. it works normally until 40 - 48 seconds very well in my test , but when it reaches 49 seconds every time it throws an exception below :

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)

I am using this technique because I need the recorded bytes and from those bytes I am drawing a wave form succesfully on the UI.

During test I come to know that an exception raised when the size of the byteArrayOutputStream is only 7.3 mb.

Can I write this byteArrayOutputStream to random access file and then reset this byteArrayOutputStream everytime it reaches to its limit?

How do I know in advance the limits of the byteArrayOutputStream?

I checked with Integer.MAX_VALUE but as I said it just raised an exception at 7.3 mb so I can not reach to Integer.MAX_VALUE.

This applet runs on the internet, so setting memory size will not help me. How can I set it to my client's computer?

ctesniere
  • 131
  • 8
Mihir
  • 2,480
  • 7
  • 38
  • 57
  • possible duplicate of [What is an OutOfMemoryError and how do I debug and fix it](http://stackoverflow.com/questions/24510188/what-is-an-outofmemoryerror-and-how-do-i-debug-and-fix-it) – Raedwald Apr 30 '15 at 11:42

5 Answers5

6

Your problem is, that the JVM is running out of memory because you keep all the data in memory. Extending the heap space helps temporarily until you need to work with even longer audios.

One idea might be to exchange the ByteArrayOutputStream by an FileOutputStream to a temporary File. This wouldn't require all audio data to be kept in memory. An even nicer solution is Guavas FileBackedOutputStream which stores the data only in a temporary file if it surpasses a certain threshold.

Another solution would be to update the waveform directly which each buffer. By creating a method which just receives the buffer with the audio data in it and applying it to the current state of the waveform. This way you wouldn't need to store the audio data at all.

Stephan
  • 7,360
  • 37
  • 46
  • unfortunately FileBackedOutputStreams doesn't let you specify the folder to create the temp file, but off to roll my own! – rogerdpack Oct 02 '12 at 20:37
2

This problem occurs because a Hotspot JVM's heap has a fixed upper bound on its size. The default limit depends on your platform, but it could be as low as 40Mb.

What you are doing is reading large amounts of data from your audio stream and buffering them in memory. Given that the heap has an upper limit, your application is eventually going to run into that limit, and the result will be an OutOfMemoryError exception.

The obvious solution is to increase the maximum heap space for the JVM using the -Xmx option. (Refer to the java command manual entry for more information on this option.) However there is an ultimate limit to how big you can make the JVMs heap ... depending on your hardware, the OS and whether or not you are using a 64 bit JVM.

You could also save the audio data to a file or attempt to compress it or reduce it, but these will all make the graphing more complicated.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
1

Others have discussed the source of the problem.

To solve it, either:

  1. Draw the bytes a little at a time, or..
  2. Store one single BufferedImage, grab the bytes in small chunks, and draw them immediately to the image.

The first technique might result in something like seen in this You Tube Video

Sound trace

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • Hello andrew can you give me any working example of you r talking about ? Thanks – Mihir Aug 22 '12 at 15:39
  • Can you spell *all 3* letters of 'long' words like 'are'? You are not sending text messages. – Andrew Thompson Aug 22 '12 at 22:32
  • Google 'this+define' You will need to lift your effort if you expect further help from me. Be specific about what you do not understand, since I do not intend explaining every word in my original comment. – Andrew Thompson Aug 23 '12 at 04:10
0

You can try to increase JVM heap space or try to compress your sound data to consume less memory.

gkuzmin
  • 2,414
  • 17
  • 24
0

It will definitely fill your memory and its behavior will be different on different computers. What you can do is use some FileOutputStream and write data onto it and once your loop ends pull data from there and convert it to wav. Also write your data in small packets of bytes (preferably 4096).. ensure your length of tempBuffer doesn't increases more than 4096.

Hope this helps..

pratikabu
  • 1,672
  • 14
  • 17