javasphinx4

How to use middle of AudioInputStream to record to file for x seconds, and continue stream?


I have a Java desktop program (Sphinx4) that continuously listens on a microphone for a keyword. I then want it to record to a file (WAVE, bigEndian) with the audio following the keyword and send it off to an external web api for processing. The good news is that I have the keyword working properly and I created a method that starts recording to a file at the time I need it too, but I am unable to stop the output when it's in the middle of its audio inputstream without cutting off the entire stream.

I can output the file successfully using:

AudioSystem.write(inputStream, AudioFileFormat.Type.WAVE, new File("test3.wav"));

However that is thread blocking and would continue forever (seeing as this is continuous listening). After looking at this for hours, I was hoping someone would have a simple solution that hopefully I'd missed.

At first, I thought all I needed to do was treat it like a normal inputstream, but without setting meta information the file is unplayable:

        byte[] buffer = new byte[1024];
        FileOutputStream out = new FileOutputStream("test.wav");
        while ((read = inputStream.read(buffer)) != -1) {
            out.write(buffer, 0, read);             
        }
        out.close();

Basically, my plan was to add a boolean variable in the while loop to detect when to stop and then close it out, and while it did create a file, it was unplayable.

The audio stream is initiated with the following:

    AudioFormat format =
        new AudioFormat(sampleRate, sampleSize, 1, signed, bigEndian);
    try {
        line = AudioSystem.getTargetDataLine(format);
        line.open();
    } catch (LineUnavailableException e) {
        throw new IllegalStateException(e);
    }
    inputStream = new AudioInputStream(line);

So what can I do to get an audio file in the middle of the stream and record for "x" seconds?


Solution

  • After trying various methods, I finally found the solution. My goal was to record an audio file from the AudioInputStream, yet calling "close()" on it basically broke the entire application because various other classes needed the inputstream as well. Closing and starting a new stream just wasn't practical in my use case.

    I decided to look into cloning an inputstream and it was exactly what I needed. In summary, all that was really needed was to create a ByteArrayOutputStream from the AudioInputStream then write out the bytes for as long as necessary. Once done, close out the ByteArrayOutputStream and do whatever processing is needed. By doing so, the original inputstream does not have to be touched or interrupted in any way. Hopefully this helps anyone else that stumbles around with the same issue:

        int numBytesRead;
        byte[] data = new byte[line.getBufferSize()];
    
        while (isrecording) {
            numBytesRead = line.read(data, 0, data.length);     
            baos.write(data, 0, numBytesRead);
        }
    
        try {
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(baos.toByteArray());   
            AudioInputStream stream = new AudioInputStream(byteArrayInputStream, format, baos.toByteArray().length);
            AudioSystem.write(stream, AudioFileFormat.Type.WAVE, new File("test.wav"));
    
            baos.close();
            byteArrayInputStream.close();
    
        } catch (Exception e) {
            e.printStackTrace();
        }