Java AudioInputStream skip with negative number of bytes always returns 0

500 views Asked by At

I am trying to skip a negative number of bytes with AudioInputStream skip(long bytes) method .

The problem is trying to (let's say a small number of bytes...) :

int skipped = audioInputStream.skip(-bytes);

always returns 0 .... i don't know what to do .


Here is the full code of the library on github .

What i do is recreating the line every time the user skips audio which is extremely slow when i can of course do much better ... by just going backward or forward . Now it supports only forward ...

/**                                                                                                                   
 * Skip bytes in the File input stream. It will skip N frames matching to bytes, so it will never skip given bytes len
 *                                                                                                                    
 * @param bytes                                                                                                       
 *            the bytes                                                                                               
 * @return value bigger than 0 for File and value = 0 for URL and InputStream                                         
 * @throws StreamPlayerException                                                                                      
 *             the stream player exception                                                                            
 */                                                                                                                   
public long seek(long bytes) throws StreamPlayerException {                                                           
    long totalSkipped = 0;                                                                                            

    //If it is File                                                                                                   
    if (dataSource instanceof File) {                                                                                 

        //Check if the requested bytes are more than totalBytes of Audio                                              
        long bytesLength = getTotalBytes();                                                                           
        System.out.println("Bytes: " + bytes + " BytesLength: " + bytesLength);                                       
        if ( ( bytesLength <= 0 ) || ( bytes >= bytesLength )) {                                                      
            generateEvent(Status.EOM, getEncodedStreamPosition(), null);                                              
            return totalSkipped;                                                                                      
        }                                                                                                             

        logger.info(() -> "Bytes to skip : " + bytes);                                                                
        Status previousStatus = status;                                                                               
        status = Status.SEEKING;                                                                                      

        try {                                                                                                         
            synchronized (audioLock) {                                                                                
                generateEvent(Status.SEEKING, AudioSystem.NOT_SPECIFIED, null);                                       
                initAudioInputStream();                                                                               
                if (audioInputStream != null) {                                                                       

                    long skipped;                                                                                     
                    // Loop until bytes are really skipped.                                                           
                    while (totalSkipped < ( bytes )) { //totalSkipped < (bytes-SKIP_INACCURACY_SIZE)))                
                        //System.out.println("Running");                                                              
                        skipped = audioInputStream.skip(bytes - totalSkipped);                                        
                        if (skipped == 0)                                                                             
                            break;                                                                                    
                        totalSkipped += skipped;                                                                      
                        logger.info("Skipped : " + totalSkipped + "/" + bytes);                                       
                        if (totalSkipped == -1)                                                                       
                            throw new StreamPlayerException(StreamPlayerException.PlayerException.SKIP_NOT_SUPPORTED);

                        logger.info("Skeeping:" + totalSkipped);                                                      
                    }                                                                                                 
                }                                                                                                     
            }                                                                                                         
            generateEvent(Status.SEEKED, getEncodedStreamPosition(), null);                                           
            status = Status.OPENED;                                                                                   
            if (previousStatus == Status.PLAYING)                                                                     
                play();                                                                                               
            else if (previousStatus == Status.PAUSED) {                                                               
                play();                                                                                               
                pause();                                                                                              
            }                                                                                                         

        } catch (IOException ex) {                                                                                    
            logger.log(Level.WARNING, ex.getMessage(), ex);                                                           
        }                                                                                                             
    }                                                                                                                 
    return totalSkipped;                                                                                              
}                                                                                                                     

Continuation of this question is here ... Java AudioInputStream how to support skip with negative number of bytes

1

There are 1 answers

1
Slaw On BEST ANSWER

AudioInputStream.skip does not support negative arguments. If you read the Javadoc of InputStream.skip it says (emphasis mine):

Skips over and discards n bytes of data from this input stream. The skip method may, for a variety of reasons, end up skipping over some smaller number of bytes, possibly 0. This may result from any of a number of conditions; reaching end of file before n bytes have been skipped is only one possibility. The actual number of bytes skipped is returned. If n is negative, the skip method for class InputStream always returns 0, and no bytes are skipped. Subclasses may handle the negative value differently.

It does mention that subclasses may change this behavior but the documentation of AudioInputStream does not give any indication it does this.

Class (AudioInputStream) javadoc:

An audio input stream is an input stream with a specified audio format and length. The length is expressed in sample frames, not bytes. Several methods are provided for reading a certain number of bytes from the stream, or an unspecified number of bytes. The audio input stream keeps track of the last byte that was read. You can skip over an arbitrary number of bytes to get to a later position for reading. An audio input stream may support marks. When you set a mark, the current position is remembered so that you can return to it later.

The AudioSystem class includes many methods that manipulate AudioInputStream objects. For example, the methods let you:

  • obtain an audio input stream from an external audio file, stream, or URL
  • write an external file from an audio input stream
  • convert an audio input stream to a different audio format

AudioInputStream.skip javadoc:

Skips over and discards a specified number of bytes from this audio input stream.

This method will always skip an integral number of frames. If n does not specify an integral number of frames, a maximum of n - (n % frameSize) bytes will be skipped.

Also, if you look at the implementation of AudioInputStream.skip you can see that the second if statement immediately returns 0 if n is <= 0.

@Override
public long skip(long n) throws IOException {
    // make sure not to skip fractional frames
    final long reminder = n % frameSize;
    if (reminder != 0) {
        n -= reminder;
    }
    if (n <= 0) {
        return 0;
    }

    if (frameLength != AudioSystem.NOT_SPECIFIED) {
        // don't skip more than our set length in frames.
        if ((n / frameSize) > (frameLength - framePos)) {
            n = (frameLength - framePos) * frameSize;
        }
    }
    long remaining = n;
    while (remaining > 0) {
        // Some input streams like FileInputStream can return more bytes,
        // when EOF is reached.
        long ret = Math.min(stream.skip(remaining), remaining);
        if (ret == 0) {
            // EOF or not? we need to check.
            if (stream.read() == -1) {
                break;
            }
            ret = 1;
        } else if (ret < 0) {
            // the skip should not return negative value, but check it also
            break;
        }
        remaining -= ret;
    }
    final long temp =  n - remaining;

    // if no error, update our position.
    if (temp % frameSize != 0) {
        // Throw an IOException if we've skipped a fractional number of frames
        throw new IOException("Could not skip an integer number of frames.");
    }
    framePos += temp/frameSize;
    return temp;
}

Java 10 source code.