1

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 as described on this answer Java AudioInputStream skip with negative number of bytes always returns 0

I need to create an implementation which supports also negative number of bytes or something like backwards.


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;                                                                                              
}                                                                                                                     
piet.t
  • 11,718
  • 21
  • 43
  • 52
GOXR3PLUS
  • 6,877
  • 9
  • 44
  • 93

2 Answers2

2

You can create your own buffer, It could be ByteArrayOutputStream but that is a bloated thing - always gives me Out of memory after a couple of minutes - or have your own Vector or other ArrayList.

I tried with a 10 min .wav file and it runs fine - as far as playing and adding the bytes to the buffer.

e.g.

Vector v=new Vector();
byte[] data=new byte[basicU];
while(true) {
  k=audioInputStream.read(data, 0, data.length);
  v.add(data);
  if(k<0) break;
  tot+=k;
}

--

Here is my method for playing a file with seeks. I have a thread for generating seek signals. The problem is complicated when we have multiple seeks. I use a variable K to check whether we need to add data to the buffer. I don't use skip but normal read; just don't play it in the line.

public void play() {
  boolean seekingBack=false;
  int i, j, k=0, seekPos=0, basicU=1024;
  AudioFormat targetFormat=null;
  int tot=0;
        new Thread() {
          public void run() {
            while(true) {
              numBytes=(Math.random()>0.5?1:-1)*500000;
              try { Thread.sleep(5000); } catch (Exception e) {} 
              seekSignal=true;
            }
          }}.start();
      try {
      File fileIn=new File("........");
        AudioInputStream audioInputStream=AudioSystem.getAudioInputStream(fileIn);
        targetFormat=audioInputStream.getFormat();
        DataLine.Info dinfo=new DataLine.Info(SourceDataLine.class, targetFormat);
        SourceDataLine line=null;
        line=(SourceDataLine)AudioSystem.getLine(dinfo);
        if(line==null) return;
        line.open(targetFormat);
        line.start();
        Vector v=new Vector();
        byte[] data=new byte[basicU];
        int K=0;
        while(true) {
          if(seekingBack) { // seeking backwards
            K=seekPos;
            k=data.length;
            for(j=0; j<data.length; j++)
              if(seekPos+j<v.size()) data[j]=((Byte)v.get(seekPos+j)).byteValue();
              else { k=j; break; }
            line.write(data, 0, k);
            seekPos+=k;
            K+=k;
            if(seekPos>v.size()-1) seekingBack=false;
          }
          else { // normal playing
            k=audioInputStream.read(data, 0, data.length);
            if(k<0) break;
            line.write(data, 0, k);
            if(K>=v.size()) for(j=0; j<k; j++) v.add(data[j]);
            K+=k;
          }
          if(seekSignal) { // received a seek signal
            if(seekingBack) { // we are on a previous back seek - reading from the buffer
            if(numBytes<0) {
              seekPos+=numBytes;
              if(seekPos<0) seekPos=0;
            }
            else { // depending on where the seek will go (in the buffer or actual audio stream)
              if(numBytes+seekPos<v.size())
                seekPos+=numBytes;
              else { // actual stream
                int rem=numBytes-(v.size()-seekPos);
                K=v.size();
                while(rem>0) {
                  k=audioInputStream.read(data, 0, data.length);
                  if(k<0) break;
                  if(K>=v.size()) for(j=0; j<k; j++) v.add(data[j]);
                  rem-=k;
                  K+=k;
                }
              }
            }
            }
            else { // we are not processing a previous back seek
            if(numBytes>=0) { // forward
                while(numBytes>0) {
                  k=audioInputStream.read(data, 0, data.length);
                  if(k<0) break;
                  if(K>=v.size()) for(j=0; j<k; j++) v.add(data[j]);
                  numBytes-=k;
                  K+=k;
                }
            }
            else { // backward
              seekingBack=true; seekPos=v.size()+numBytes; if(seekPos<0) seekPos=0; }
            }
            seekSignal=false;
          }
        }
        line.stop();
        line.close();
      }
      catch(Exception ex) { ex.printStackTrace(); System.out.println("audio problem "+ex); }
}
gpasch
  • 2,672
  • 3
  • 10
  • 12
  • I like your answer, can you transform the method? I mean a full example would be great.... Let's say i call it for `seek(6000)` and `seek(-8000)` what is the final form of thr seek method. – GOXR3PLUS Aug 24 '18 at 04:45
  • You can test it with full code here :) https://github.com/goxr3plus/java-stream-player – GOXR3PLUS Aug 24 '18 at 04:47
  • 1
    I'll give you my reservations that although you say it has been tested successfully it doesnt work - maybe it runs with no erros thats what you mean - because when you seek you init the audio stream - that' not how you seek: you seek from the current position. Not from the start. But I will give you my answer in a bit. Maybe you can fit it in your program – gpasch Aug 24 '18 at 14:57
0

Use your own buffer which holds a rolling window of history. I'd build a helper class that does this by allocating a List<byte[]> to manage history in blocks of for example 8192 bytes. Then you need some simple overflowing mechanism that throws out the oldest block, in combination with some pointer manipulation to keep track of where you actually are in the stream. Good luck!

Martijn Courteaux
  • 67,591
  • 47
  • 198
  • 287
  • All the code is [here](https://github.com/goxr3plus/java-stream-player) can i have a runnable example please ? That's why i asked here ... just a simple one ... i will improve it and accept the answer . :) :) :_) – GOXR3PLUS Aug 24 '18 at 08:33
  • Let's say we have a really big 40 minutes audio will this approach work , so we firstly go on 36 minutes then try to go back on 1st minute of audio etc... ? – GOXR3PLUS Aug 24 '18 at 08:34
  • 1
    I won't do your coding work, I can point you into the right direction. And yes, this can work, but you will need to keep your entire audio fragment into main memory. If your file gets longer, and you need random access, than use a file format that allows for random access (or if you don't care about hard realtime constraints, just reopen the file), or develop something of your own such that you can (eg: using multiple files: one per fragment). Back of the envelope calculation: 40 min * 60s/min * 44000Hz * 2 bytes/frame = 201 MB, which should indeed fit your RAM. – Martijn Courteaux Aug 24 '18 at 10:02