Java AudioInputStream how to support skip with negative number of bytes

陌路散爱 提交于 2019-12-02 13:46:38

问题


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;                                                                                              
}                                                                                                                     

回答1:


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); }
}



回答2:


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!



来源:https://stackoverflow.com/questions/51920844/java-audioinputstream-how-to-support-skip-with-negative-number-of-bytes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!