0

So my following code snippet (the concat method) mainly tries to do two things:

  1. Concatenate multiple AudioInputStream, read from a list of source files, into one final long stream
  2. Write that stream into a destination WAV file

    public static void concat(ArrayList<String> sourceFiles, String targetFile) {
        AudioInputStream targetBuilder = null;
        String file;
        AudioInputStream nextSource;
        for (int i = 0; i < sourceFiles.size(); i++)
        {
            file = sourceFiles.get(i);
            if(targetBuilder == null)
            {
                targetBuilder = AudioSystem.getAudioInputStream(new File(file));
                continue;
            }
            nextSource = AudioSystem.getAudioInputStream(new File(file));
            AudioInputStream appendedFiles = new AudioInputStream(
                    new SequenceInputStream(targetBuilder, nextSource),     
                    targetBuilder.getFormat(), 
                    targetBuilder.getFrameLength() + nextSource.getFrameLength());
            targetBuilder = appendedFiles;
            nextSource.close();
        }
        AudioSystem.write(targetBuilder, AudioFileFormat.Type.WAVE, new File(targetFile));
        targetBuilder.close();
    }
    

This idea is borrowed from this thread: Join two WAV files from Java. Now the problem is after executing this snippet consecutively several times, eventually it will throw IO Exception on "stream closed" on the line of AudioSystem.write(). The stack trace looks like this:

java.io.IOException: Stream Closed
at java.io.FileInputStream.readBytes(Native Method)
at java.io.FileInputStream.read(FileInputStream.java:272)
at javax.sound.sampled.AudioInputStream.read(AudioInputStream.java:292)
at java.io.SequenceInputStream.read(SequenceInputStream.java:208)
at java.io.SequenceInputStream.read(SequenceInputStream.java:211)
at javax.sound.sampled.AudioInputStream.read(AudioInputStream.java:292)
at java.io.SequenceInputStream.read(SequenceInputStream.java:208)
at javax.sound.sampled.AudioInputStream.read(AudioInputStream.java:292)
......//with the exact same two lines repeating lots of times
at java.io.SequenceInputStream.read(SequenceInputStream.java:208)
at javax.sound.sampled.AudioInputStream.read(AudioInputStream.java:292)
at com.sun.media.sound.SunFileWriter$NoCloseInputStream.read(SunFileWriter.java:198)
at java.io.SequenceInputStream.read(SequenceInputStream.java:208)
at java.io.InputStream.read(InputStream.java:101)
at com.sun.media.sound.WaveFileWriter.writeWaveFile(WaveFileWriter.java:239)
at com.sun.media.sound.WaveFileWriter.write(WaveFileWriter.java:137)
at javax.sound.sampled.AudioSystem.write(AudioSystem.java:1354)
at com.mycompany.mypackage.AudioUtils.concat(AudioUtils.java:324)
......

So looks to me it's when the program still attempts to read more bytes from the AudioInputStream to the file but at some point the stream (targetBuilder) just closes itself. Which doesn't make sense to me because I only close this stream AFTER my AudioSystem.write() method.

Now if I actually comment on this line inside the for loop:

//nextSource.close();

This stream close exception seems going away, with the replace of "FilesNotFoundException: too many open files", which makes sense to me because in every iteration of the loop I am creating a new AudioInputStream and assign reference to nextSource, but if I never close it eventually I run out of open file descriptors.

However what I don't understand is how come closing this nextSource stream would affect in any way how targetBuilder stream behaves? Why would this stream abruptly getting closed if I close nextSource stream? (I am assuming there is a connection between the two, please correct me if I am wrong).

Thanks!

Community
  • 1
  • 1
Superziyi
  • 609
  • 1
  • 9
  • 30

1 Answers1

0

SequenceInputStream doesn't copy yours streams in memory, it's just remembers links... here is code of read method

public int read() throws IOException
{
  int ch = -1;

  while (in != null && (ch = in.read()) < 0)
    {
      in.close();
      in = getNextStream();
    }

  return ch;
}

so if you will close one of streams added to SequenceInputStream - you will get exception

Puh
  • 305
  • 1
  • 10
  • Thanks for this! You mean all the contents are just put as some reference in some stream place and only gets consumed when it actually comes down to AudioSystem.write()? This doesn't happen every time though. The first several executions of this method typically succeeds. Also, what would you recommend as the workaround in this case then? Apparently I cannot just leave the stream open cos it's gonna hit open file descriptor limit, and I am concatenating quite a bunch of streams... – Superziyi Oct 23 '14 at 01:49
  • You need to close those nextSource after the write to close the opened files. – DiogoSantana Oct 23 '14 at 01:51
  • @DiogoSantana What do you mean by that? nextSource only has the scope inside the for loop, and write has to happen when the for loop is done. Even if I close it after write, I am only closing the last stream referenced by nextSource. And I did that, it will still spit out "too many open files" exception... – Superziyi Oct 23 '14 at 01:57
  • AudioInputStream uses internally FileInputStream. Every FileInputStream created opens the inner file on the OS. So you need to change the logic of your code to be able to close all after the write. – DiogoSantana Oct 23 '14 at 02:02
  • Try to add all to a List. Then iterate the list closing each one. – DiogoSantana Oct 23 '14 at 02:02
  • easiest way is merge files by pairs, first and second, then result with third, and so on.. but it's really bad idea)) also you can make a wrapper around AudioInputStream that constructor get list of files and override read method in similar way as it done in SequenceInputStream but getNextStream method should open next file... – Puh Oct 23 '14 at 02:21
  • You are right about why exceptions get thrown. While the root cause of getting "too many open files" lies in somewhere else. Adding them to list doesn't solve the problem. Neither does writing to temp files sound like a good solution. I will open another question specifically for that. Thanks for both of your answers though. – Superziyi Oct 23 '14 at 22:30