sof
sof

Reputation: 9649

Failed to passing InputStream object to Java Sound API

It works with a File object being passed to AudioSystem#getAudioFileFormat, but why does it fail with an InputStream object below? Any suggestion?

import java.io.*;
import javax.sound.sampled.*;

public class Test {

    public static void main(String[] args) throws Exception {

        AudioSystem.getAudioFileFormat(new File(
                "myaudio.wav"));
        AudioSystem.getAudioFileFormat(new FileInputStream(
                "myaudio.wav"));

    }
}

Output:

Exception in thread "main" java.io.IOException: mark/reset not supported
    at java.io.InputStream.reset(InputStream.java:330)
    at com.sun.media.sound.WaveFileReader.getAudioFileFormat(WaveFileReader.java:88)
    at javax.sound.sampled.AudioSystem.getAudioFileFormat(AudioSystem.java:985)
    at Test.main(Test.java:10)

@EDIT

According to the answers from @René Jeschke, @Phil Freihofner and @Andrew Thompson, wherever mark/reset is required as the mandatory protocal for Java Sound API to interact with the IO stream, IMHO, the type of buffered stream instead of the raw one should have been specially defined as the signature of the parameter to be passed. Doing so would narrow to the more desirable result than arbitrarily accepting an IO stream then resorting to IOException as the adverse indicator.

Upvotes: 4

Views: 1193

Answers (3)

Neet
Neet

Reputation: 4037

FileInputStream does not support mark/reset (for random access), wrap it into a BufferedInputStream to get mark/reset support.

Edit: Why is this so? getAudioFileFormat iterates through every audio file reader currently registered. Each reader tries to identify the file format by reading some specific bytes. So every reader has to undo its changes to the stream (to allow other readers to re-read all data if needed).

When you supply a File, that's not a problem because each reader just opens a new stream, but when you pass a stream, each reader must mark the current stream position, do its readings, and reset the stream to its beginning state when it's finished.

That's why you need the BufferedInputStream because it adds an in-memory buffer to support mark/reset.

Edit2: Because the question was 'Why does it fail with FileInputStream?' I did not suggest any workarounds, or replacements but tried to explain why it fails when one is using a FileInputStream. There are also cases where you can not use URL or the like (consider e.g. a binary package file containing audio files).

Upvotes: 5

Phil Freihofner
Phil Freihofner

Reputation: 7910

The AudioSystem.getAudioFileFormat() methods call the abstract class "AudioFileReader" in package javax.sound.sampled.spi.

The comments in the code for the method AudioFileFormat getAudioFileFormat(InputStream stream) throws UnsupportedAudioFileException, IOException; say that mark/reset may be required:

 * Obtains the audio file format of the input stream provided. The stream must
 * point to valid audio file data. In general, audio file readers may
 * need to read some data from the stream before determining whether they
 * support it. These parsers must
 * be able to mark the stream, read enough data to determine whether they
 * support the stream, and, if not, reset the stream's read pointer to its original
 * position. If the input stream does not support this, this method may fail
 * with an <code>IOException</code>.

In contrast, the forms public abstract AudioFileFormat getAudioFileFormat(URL url) throws UnsupportedAudioFileException, IOException; and public abstract AudioFileFormat getAudioFileFormat(File file) throws UnsupportedAudioFileException, IOException; do NOT make this requirement. The support for the parsers only comes up with the InputStream as a parameter.

The AudioSystem.getAudioInputStream() are similarly commented for the various overloads.

When a mark/reset error comes up in the context of working with Audio Files, the solution to try first is to load the file via its URL and thus avoid the mark/reset requirement (as mentioned by Andrew Thompson). If that doesn't work, sure, use BufferedInputStream, but it shouldn't have to come to that for valid audio files.

The issue is also documented in the Oracle Bug database as bug #7095006.

Upvotes: 1

Andrew Thompson
Andrew Thompson

Reputation: 168795

This works for me (with other Wavs).

AudioSystem.getAudioFileFormat(new File(
            "myaudio.wav").toURI().toURL());

The code on the JavaSound info. page also uses an URL.

If what you have is something that is neither a File or URL, the problem can be fixed by buffering it, as mentioned by René Jeschke, or I generally just read all the byte[] and establish a ByteArrayInputStream. That is positionable (supports mark/reset).

Upvotes: 1

Related Questions