Monday, May 7, 2018

How does the WAV format look like?

To write PCM samples captured from audio recorder to WAV file, below can be done

1. Create a Random access file.
2. write the wav header, which is as below

rFile.writeBytes("RIFF"); => This is chunk descriptor . 4 bytes
rFile.writeInt(0); => Chunk Size , 4 bytes => total size followed after this field including this => CHUNKSZ1
rFile.writeBytes("WAVE"); => 4 bytes
rFile.writeBytes("fmt "); => 4 bytes (note the space in here)
rFile.writeInt(Integer.reverseBytes(16)); => sub chunk size, 4 bytes
rFile.writeShort(Short.reverseBytes((short) 1)); => Audio format (PCM) => 2 bytes 
rFile.writeShort(Short.reverseBytes(channels)); => number of channels => 2 bytes
rFile.writeInt(Integer.reverseBytes(rate)); => Sampling rate => 4 bytes
rFile.writeInt(Integer.reverseBytes(rate * (resolution / 8) * mChannels)); =>  total bit rate (4 bytes)
rFile.writeShort(Short.reverseBytes((short) (channels * resolution / 8))); => block align (4 bytes)
rFile.writeShort(Short.reverseBytes(resolution)); => bits per sample (16 bit or 8 bit) , 2 byte
rFile.writeBytes("data"); => data chunk CHUNKSZ2
rFile.writeInt(0); => this is sub chunk size (the size of the remaining payload, which is also same as the total size followed by this)

Now if we have the PCM data, in order to put that in WAV format, write the PCM data appended to the random access file and then edit the CHUNKSZ1, CHUNKSZ2 values to the the total size of bytes. for e.g. like this below

// Write size to CHUNKSZ1
rFile.seek(4); // Write size to RIFF header
rFile.writeInt(Integer.reverseBytes(36 + payloadSz)); => 36 being the size of the header excluding the RIFF

rFile.seek(40); // Write size to CHUNKSZ2
rFile.writeInt(Integer.reverseBytes(payloadSz));

3. Close the Random access file.

Now we could make some funny things like repeat the whatever told multiple times in that case below can be done. Copy the contents from 44 bytes and then append to the random access file, come back and edit the payload size values again.

 RandomAccessFile wavFile = new RandomAccessFile(wavFilePath,"rw");
 long totalLength = wavFile.length();
 FileLogger.logInfo("Size of the wav file is "+totalLength);
 wavFile.seek(40);//get past the WAV headers
 //read the current size
 int subChunkSize = Integer.reverseBytes(wavFile.readInt());
 FileLogger.logInfo("subChunkSize is :"+subChunkSize);
 //now read the sub chunk size
 //buffer size
 int BUFF_SIZE = 1024;
 byte[] buffer = new byte[BUFF_SIZE];
 int readOffset = 44; // wav header end
 int countOfBytes  = -1;
 int debugCountOfByes = 0; // to analyze how many bytes were copied
 ByteArrayOutputStream bout = new ByteArrayOutputStream();
 wavFile.seek(readOffset);
 while ((countOfBytes = wavFile.read(buffer,0,BUFF_SIZE)) != -1 ){
  bout.write(buffer);
  readOffset += countOfBytes;
        debugCountOfByes += countOfBytes;
}
FileLogger.logInfo("Copied "+debugCountOfByes+" from file");
byte[] bytes = bout.toByteArray();
//now write this multiple times
for (int i = 0; i < count; i++){
     wavFile.write(bytes,44,bytes.length - 44);
}
int totalNewSize = subChunkSize + (count * bytes.length);
//now alter the wav header for the size values
wavFile.seek(4); // Write size to RIFF header
wavFile.writeInt(Integer.reverseBytes(36 + totalNewSize));

wavFile.seek(40); // Write size to Subchunk2Size field
wavFile.writeInt(Integer.reverseBytes(totalNewSize));
wavFile.close();

references:
http://soundfile.sapp.org/doc/WaveFormat/

No comments:

Post a Comment