Search code examples
javaaudiotrim

Trim beginning and end from a WAV file with the Java sound API


I have the basics made. However, the output file just repeats the WAV headerbytes over and over. The resulting file is the right size, but it is filed with junk.

I am trying to use a class that extends AudioInputStream so that I can use it seamlessly with other code that will mix it with another AudioInputStream(which works beautifully).

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;

public class TrimmerAIS extends AudioInputStream{

private final AudioInputStream stream;
private final long startByte,endByte;
private long t_bytesRead=0;
public TrimmerAIS(AudioFormat audioFormat,AudioInputStream audioInputStream,long startMilli,long endMilli){
    super(new ByteArrayInputStream(new byte[0]),audioFormat,AudioSystem.NOT_SPECIFIED);
    stream=audioInputStream;
    //startByte=(long)((startMilli/1000f)*stream.getFormat().getSampleRate()*stream.getFormat().getSampleSizeInBits()*stream.getFormat().getChannels())/8;
    //endByte=(long)((endMilli/1000f)*stream.getFormat().getSampleRate()*stream.getFormat().getSampleSizeInBits()*stream.getFormat().getChannels())/8;
    startByte=(long)((startMilli/1000)*stream.getFormat().getFrameRate()*stream.getFormat().getFrameSize());
    endByte=(long)((endMilli/1000)*stream.getFormat().getFrameRate()*stream.getFormat().getFrameSize());
}
private byte[] tempBuffer;

@Override
public int available() throws IOException{
    return (int)(endByte-startByte-t_bytesRead);
}
public int read(byte[] abData,int nOffset,int nLength) throws IOException{
    // set up the temporary byte buffer
    if(tempBuffer==null||tempBuffer.length<nLength){
        tempBuffer=new byte[nLength];
    }
    int bytesRead=0;
    if(t_bytesRead<startByte){
        do{//skip to starting byte
            bytesRead=(int)skip(startByte-t_bytesRead);
            t_bytesRead+=bytesRead;
        }while(t_bytesRead<startByte);
    }
    if(t_bytesRead>=endByte){
        return -1;
    }

    bytesRead=stream.read(tempBuffer,0,nLength);
    if(bytesRead==-1){//premature EOF
        return -1;
    }else if(bytesRead==0){
        return 0;
    }
    t_bytesRead+=bytesRead;
    if(t_bytesRead>=endByte){//correct bytes read to exclude any bytes over the limit
        bytesRead=(int)(bytesRead-(t_bytesRead-endByte));
    }
    return bytesRead;
}
public static void main(String[] args) throws UnsupportedAudioFileException, IOException{
    AudioInputStream music=null;
    music = AudioSystem.getAudioInputStream(new File("music/0.wav"));
    music=new TrimmerAIS(music.getFormat(),music,0,15000);
    AudioSystem.write(music,AudioFileFormat.Type.WAVE,new File("out.wav"));
}
}

I simply cannot find anything wrong with it. I did find a similar post on here that linked to https://forums.oracle.com/forums/thread.jspa?threadID=2136229&tstart=0. Which is what mine does except that you give it the start and end positions in milliseconds and it can be passed on to/wrapped in another AIS or output without the need to read the segment wanted into an array and then writing it. Been trying to figure this out for the past 3 hours and my mind is a little fried. So if something doesn't make sense, feel free to ask for clarification. I would prefer to not parse the file but if I have to I have to.


Solution

  • Heh, whoops! Just needed to remove tempBuffer and replace it with abData. Was a long day. Below is the corrected code. Thought about deleting this since it was a simple mistake, but the only reason I made this class is because it does not already exist.

    import java.io.ByteArrayInputStream;
    import java.io.File;
    import java.io.IOException;
    
    import javax.sound.sampled.AudioFileFormat;
    import javax.sound.sampled.AudioFormat;
    import javax.sound.sampled.AudioInputStream;
    import javax.sound.sampled.AudioSystem;
    import javax.sound.sampled.UnsupportedAudioFileException;
    
    public class TrimmerAIS extends AudioInputStream{
    
    private final AudioInputStream stream;
    private final long startByte,endByte;
    private long t_bytesRead=0;
    
    public TrimmerAIS(AudioFormat audioFormat,AudioInputStream audioInputStream,long startMilli,long endMilli){
        super(new ByteArrayInputStream(new byte[0]),audioFormat,AudioSystem.NOT_SPECIFIED);
        stream=audioInputStream;
        //calculate where to start and where to end
        startByte=(long)((startMilli/1000)*stream.getFormat().getFrameRate()*stream.getFormat().getFrameSize());
        endByte=(long)((endMilli/1000)*stream.getFormat().getFrameRate()*stream.getFormat().getFrameSize());
    }
    
    @Override
    public int available() throws IOException{
        return (int)(endByte-startByte-t_bytesRead);
    }
    public int read(byte[] abData,int nOffset,int nLength) throws IOException{
        int bytesRead=0;
        if(t_bytesRead<startByte){
            do{
                bytesRead=(int)skip(startByte-t_bytesRead);
                t_bytesRead+=bytesRead;
            }while(t_bytesRead<startByte);
        }
        if(t_bytesRead>=endByte)//end reached. signal EOF
            return -1;
    
        bytesRead=stream.read(abData,0,nLength);
        if(bytesRead==-1)
            return -1;
        else if(bytesRead==0)
            return 0;
    
        t_bytesRead+=bytesRead;
        if(t_bytesRead>=endByte)// "trim" the extra by altering the number of bytes read
            bytesRead=(int)(bytesRead-(t_bytesRead-endByte));
    
        return bytesRead;
    }
    public static void main(String[] args) throws UnsupportedAudioFileException, IOException{
        AudioInputStream music=null;
        music = AudioSystem.getAudioInputStream(new File("music/0.wav"));
        music=new TrimmerAIS(music.getFormat(),music,0,15000);
        AudioSystem.write(music,AudioFileFormat.Type.WAVE,new File("out.wav"));
    }
    }