This discussion is archived
11 Replies Latest reply: Jul 6, 2011 2:29 PM by sabre150 RSS

sanity check, converting ByteArrayInputStream to an AudioInputStream

810200 Newbie
Currently Being Moderated
I'm in the process of rewriting some tutorial code that plays back OggVorbis files. Instead of playing back the file, I my goal is to save the data as a .WAV file.

I progressively read the decoded file into a ByteArrayOutputStream baos. Then, I use boas to make a ByteArrayInputStream bais. Then, I try to use bais in the constructor for an AudioInputStream. The plan is to then use the AudioInputStream in an AudioSystem.write() method.
ByteArrayInputStream bais = new ByteArrayInputStream(boas.toByteArray());
AudioInputStream ais = AudioSystem.getAudioInputStream(bais);
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, destinationFile);
The second statement above throws this exception: "UnsupportedAudioFileException: could not get audio input stream from input stream at javax.sound.sampled.AudioSystem.getAudioInputStream(Unknown Source)"

So, here is the question: is the plan itself reasonable? In the tutorial, the OggVorbis file is progressively read into a SourceDataLine that was generated as follows, where the variable audioFormat is a typical .WAVE format:
DataLine.Info datalineInfo = new DataLine.Info(SourceDataLine.class, audioFormat, AudioSystem.NOT_SPECIFIED);
SourceDataLine outputLine = (SourceDataLine) AudioSystem.getLine(datalineInfo);
outputLine.open(audioFormat);
I am assuming that it is valid to read into a ByteArrayOutputStream instead. Perhaps something is lost when doing so?

The next thing I might try is to take the ByteArrayOutputStream data and convert that to a TargetDataLine, as I have had success making AudioInputStreams from TargetDataLines in the past.

Thanks for any feedback!
  • 1. Re: sanity check, converting ByteArrayInputStream to an AudioInputStream
    EJP Guru
    Currently Being Moderated
    I progressively read the decoded file into a ByteArrayOutputStream baos.
    Why?

    Why not just wrap the AudioInputStream around the orignal input stream?

    Loading it all into memory adds a lot of latency and uses up tons of memory.
    I am assuming that it is valid to read into a ByteArrayOutputStream instead.
    Valid but pointless.
    Perhaps something is lost when doing so?
    Not unless your code is wrong.
  • 2. Re: sanity check, converting ByteArrayInputStream to an AudioInputStream
    810200 Newbie
    Currently Being Moderated
    You ask why I don't wrap an AudioInputStream around the original input stream.

    The answer is that the original input stream is encoded as OggVorbis, and unusable until it is decoded.

    The innermost loop where the decoding occurs reads the data one frame channel at a time from a multidimensional array, converting it to WAV format (16-bit, little endian, etc.) and places this data in a byte buffer. The buffer is then progressively read into a SourceDataLine (in the original code).

    There is no way to wrap a SourceDataLine with an AudioInputStream, is there? Or to extract or generate an AudioInputStream from a SourceDataLine? So, I thought I'd try to read into a ByteArrayOutputStream and work from there. (I can't imagine how I'd convert the multidim array to a stream, especially since it is repeatedly loaded with the decoded data.)

    The tutorial code that I am trying to rewrite makes use of the JOrbis.jar provided by ... ah, the name of the coders escapes me. But believe me, the code in that jar is wild, unlike anything I've ever seen written in Java before. I'm talking public variables, all sorts of OOP no-no's.

    But thanks for the feedback that the path I decided to try is valid, even if it is very inefficient. I will continue to look for bugs in the code. I've verfied that the bais is showing that it has something very close to the expect number of bytes (haven't nailed down the exact expected yet).
  • 3. Re: sanity check, converting ByteArrayInputStream to an AudioInputStream
    EJP Guru
    Currently Being Moderated
    I think I would write the OggVorbis decoding code as a class that extends FilterInputStream, wrap it around the original stream, and wrap the AudioInputStream around that, rather than have all that latency and memory usage. That way you can get some audio through the system almost immediately.

    I don't know anything about SourceDataLines.
  • 4. Re: sanity check, converting ByteArrayInputStream to an AudioInputStream
    sabre150 Expert
    Currently Being Moderated
    807197 wrote:
    I'm in the process of rewriting some tutorial code that plays back OggVorbis files. Instead of playing back the file, I my goal is to save the data as a .WAV file.

    I progressively read the decoded file into a ByteArrayOutputStream baos. Then, I use boas to make a ByteArrayInputStream bais. Then, I try to use bais in the constructor for an AudioInputStream. The plan is to then use the AudioInputStream in an AudioSystem.write() method.
    ByteArrayInputStream bais = new ByteArrayInputStream(boas.toByteArray());
    AudioInputStream ais = AudioSystem.getAudioInputStream(bais);
    AudioSystem.write(ais, AudioFileFormat.Type.WAVE, destinationFile);
    Since you have decoded the 'ogg' file the content of the ByteArrayInputStream is now big or little endian (I can never remember which way round one gets using OggVorbis decoder) raw PCM samples so the construction of the AudioInputStream is bound to fail. You just need a normal InputStream using these PCM samples when using AudioSystem.write().

    As EJP says, you don't need to create the byte array in the first place. Just use the InputStream obtained from the OGG decoder.
  • 5. Re: sanity check, converting ByteArrayInputStream to an AudioInputStream
    810200 Newbie
    Currently Being Moderated
    Interesting idea! (writing to subclass FilterInputStream).

    I think something similar to your idea would be to write the OggVorbis decoder to subclass TargetDataLine. I know I can wrap a TargetDataLine in an AudioInputStream. (Haven't done it with FilterInputStream.) But this decoder isn't exactly well-behaved. The data is stored in pages and packets that vary in size, and there is a header to decode, and I'll have to figure out how to stop and start in order to break it up into a series of reads...how to keep track of progress through the decode. Hmmmm.

    At least, there is no need to play the file back--I don't wish to do that. And the files won't be very large, they will be only a few seconds long each, maybe 30 seconds max. I just want to store them as WAV instead of OGG, for use at a later stage.

    Edited by: 807197 on Jul 5, 2011 11:31 PM
  • 6. Re: sanity check, converting ByteArrayInputStream to an AudioInputStream
    810200 Newbie
    Currently Being Moderated
    @sabre150
    "You just need a normal InputStream using these PCM samples when using AudioSystem.write()."

    I'm afraid I don't follow. How would a "normal InputStream" differ from the data I have already (the raw PCM in bais )? Is there a step or two less that I should do to leave the byte array something that CAN be made into AIS?

    Or are you saying the AudioSystem.write can use an InputStream? Unfortunately, that doesn't seem to be an option. AudioSystem.write insists the first argument be an AIS.

    AH -- just found the constructor new AudioInputStream(InputStream, AudioFormat, long)! I mistakenly thought I was restricted to AudioSystem methods to build the new AudioInputStream. So, all I have to do is grab the file length (can get from boas ) and I should be good to go. (I can make an InputStream from the ByteArrayInputStream.)

    (Also tried casting ByteArrayInputStream to TargetDataLine--maybe a goofy idea but the compiler didn't complain. Runtime error instead.)

    Curious, three of the five ogg files were saved as .wav files. Two of those are playable but with truncated ends. One gives the WindowsMediaPlayer an "unrecognized format". OK, let me mess with this some more. In the morning...

    Thanks again for the input!

    Edited by: 807197 on Jul 6, 2011 12:36 AM
  • 7. Re: sanity check, converting ByteArrayInputStream to an AudioInputStream
    EJP Guru
    Currently Being Moderated
    Interesting idea! (writing to subclass FilterInputStream).
    As sabre150 notes, it turns out they have done it for you. Audio not my strong point, just coding ;-)
  • 8. Re: sanity check, converting ByteArrayInputStream to an AudioInputStream
    sabre150 Expert
    Currently Being Moderated
    807197 wrote:
    @sabre150
    "You just need a normal InputStream using these PCM samples when using AudioSystem.write()."
    I was slightly wrong. I use OggVorbis and it gives me an AudioInputStream which contains all the format information needed by the AudioSystem method
    public static int write(AudioInputStream stream,
                            AudioFileFormat.Type fileType,
                            File out)
                     throws IOException
    so just use it. No need for any other buffering. Without seeing your code I can't be certain that this will work but I do something very very similar.
  • 9. Re: sanity check, converting ByteArrayInputStream to an AudioInputStream
    810200 Newbie
    Currently Being Moderated
    I am following up with the "solution" that I am accepting to the question.

    I must have a different starting point than +@sabre150+. I've been rewriting the ExamplePlayer code that is given as a contributed tutorial. It does not provide an AudioInputStream hook, but simply routes the output directly to a SourceDataLine. So, I'm curious what the source code is that you ( +@sabre150+ ) are using! Perhaps you wrote your own code to directly interface with JOrbis?

    Because I cannot find a path from a SourceDataLine to an AudioInputStream, and because the example code I'm rewriting does not lend itself to progressive reads (needed for subclassing as a TargetDataLine or other streaming structures), I'm doing the following and it does the job, albeit with an extra step. This can be revisited if there turns out to be a "business need" or clear "business benefit" to do so.

    1) reading the entire decoded file into a ByteArrayOutputStream,
    2) using ByteArrayOutputStream.size() and dividing by the frame size to obtain the number of frames,
    3) creating a ByteArrayInputStream via ByteArrayOutputStream.toByteArray(),
    4) creating an InputStream from the ByteArrayInputStream,
    5) creating an AudioInputStream via the constructor(InputStream, AudioFormat, framelength)

    Then, I am able to save the file via the usual AudioSystem.write() method.

    Steps 2 through 5 are pretty minimal, afaik, e.g., basically a "wrapper" action. The main cost is the act of reading in and decoding the entire file. As the files are relatively small in this case, and handled one-at-a-time, the memory requirements should be no more than if one held the largest of the batch in memory as a Clip.

    Playbacks of the newly saved .WAV files have been successful. There is another bug to track down, in that two of my five test cases bombed out during the decoding and never reached the above rigamarole, but that is another issue.

    Many thanks!!

    Edited by: 807197 on Jul 6, 2011 1:34 PM
  • 10. Re: sanity check, converting ByteArrayInputStream to an AudioInputStream
    810200 Newbie
    Currently Being Moderated
    I'm still figuring out this forum system.
    My follow-up was posted as the last comment. Perhaps that will be one comment previous to this one?
  • 11. Re: sanity check, converting ByteArrayInputStream to an AudioInputStream
    sabre150 Expert
    Currently Being Moderated
    I think we must be talking at cross purposes. I'm talking of the Ogg Vorbis SPI for Java Sound ( http://www.javazoom.net/vorbisspi/vorbisspi.htm) which wraps JOrbis (http://www.jcraft.com/jorbis/ ) to allow one to read '.ogg' files as an AudioInputStream. There is a similar SPI for MP3 ( http://www.javazoom.net/mp3spi/mp3spi.html ) which wraps JLayer ( http://www.javazoom.net/javalayer/javalayer.html ).

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points