“WAV” audio file format for VS1003 using Arduino

This program will demonstrate the format of “WAV” file to be created for recording voice from VS1003. It is a sample demonstration of “WAV” file format and is for a reference only.

This program will create a “WAV” file named “RECORD2.WAV”. Then, “WAV” header bytes will be written to the “RECORD2.WAV”. Size of header file will be 512 bytes. After writing the header bytes, DATA will be written as group of 512 bytes. We could write any number of DATA bytes. But, after writing all the DATA, size of the file “RECORD2.WAV” should be divisible by 512. Here, size of “RECORD2.WAV” is 31232 which is divisible by 512.

After writing the entire data, some modifications have to be made in the header bytes or first 512 bytes of “RECORD2.WAV”. They are

Update “RIFF” header chunk at byte 4
Update “FACT” header chunk at byte 48
Update “DATA” header chunk at byte 508

Here, for illustration, DATA bytes are copied from an existing “WAV” file named “RECORD1.WAV”. DATA bytes starts at byte number 512. Hence, after writing header bytes to “RECORD2.WAV”, DATA bytes are copied from “RECORD1.WAV” to “RECORD2.WAV”.

Step 1 : Download “RECORD1.WAV” and copy to your SD card.

Step 2 : Complete the circuit and upload the following program to Arduino MEGA.

#include <SD.h>

File file1, file2;
byte data[4];

char copyFromFile[] = "RECORD1.WAV";
char copyToFile[] = "RECORD2.WAV";
  
const unsigned char RIFFHeader0fn[] = {
    'R' , 'I' , 'F' , 'F' , // Chunk ID (RIFF)
    0xF8, 0x3D, 0x00, 0x00, // Chunk payload size (calculate after rec!)
    'W' , 'A' , 'V' , 'E' , // RIFF resource format type

    'f' , 'm' , 't' , ' ' , // Chunk ID (fmt )
    0x14, 0x00, 0x00, 0x00, // Chunk payload size (0x14 = 20 bytes)
    0x11, 0x00,             // Format Tag (IMA ADPCM)
    0x01, 0x00,             // Channels (1)
    0x80, 0x3e, 0x00, 0x00, // Sample Rate, 0x3e80 = 16.0kHz
    0xd7, 0x0f, 0x00, 0x00, // Average Bytes Per Second
    0x00, 0x01,             // Data Block Size (256 bytes) 
    0x04, 0x00,             // ADPCM encoded bits per sample (4 bits)
    0x02, 0x00,             // Extra data (2 bytes)
    0xf9, 0x01,             // Samples per Block (505 samples)

    'f' , 'a' , 'c' , 't' , // Chunk ID (fact)
    0xc8, 0x01, 0x00, 0x00, // Chunk payload size (456 bytes (zeropad!))
    0x5C, 0x76, 0x00, 0x00  // Number of Samples (calculate after rec!)
};

const unsigned char RIFFHeader504fn[] = {
    'd' , 'a' , 't' , 'a' , // Chunk ID (data)
    0x00, 0x3C, 0x00, 0x00,  // Chunk payload size (calculate after rec!)
};

void setup() {
  // put your setup code here, to run once:

  Serial.begin(9600);
  
  SD.begin(4);

  if((file1 = SD.open(copyFromFile, FILE_READ)))
  {
    Serial.println("File1 opening successfull...");
  }

  if (SD.exists(copyToFile))
  {
    SD.remove(copyToFile);
  }

  if ((file2 = SD.open(copyToFile, FILE_WRITE)))
  {
    Serial.println("File2 opening successfull...");
  }

  file1.seek(0);
  file2.seek(0);

  // First 512 bytes = 52 bytes RIFFHEADER0 + 452 bytes '0' + 8 bytes RIFFHEADER504

  // Create a wav file and create the necessary header from address 0 to 51
  file2.write(RIFFHeader0fn, sizeof(RIFFHeader0fn));

  // Write '0' (0x30) from address 51 to 503 
  for (int i = 0; i<452; i++)
  {
    file2.write('0');
  }

  // write second RIFF header from 504 to 511
  file2.write(RIFFHeader504fn, sizeof(RIFFHeader504fn));
  file2.flush();

  // Remaining data bytes as a group of 512 bytes

  for(int i=0;i< file1.size();i++)
  { 
    i < 512 ? file1.read() : file2.write(file1.read());
  }
  
  Serial.println("Finished...");
  Serial.print("RECORD1 size : ");
  Serial.print(file1.size());
  Serial.println(" Bytes");
  Serial.print("RECORD2 size : ");
  Serial.print(file2.size());
  Serial.print(" Bytes");
  
  file1.close();
  file2.close();

  if (!(file2 = SD.open(copyToFile, O_WRITE)))
  {
    Serial.println("Failed to open record file in O_WRITE mode");
  }

  // After writing the data to "WAV" file,
  //    Update "RIFF" header chunk at byte 4
  //    Update "FACT" header chunk at byte 48
  //    Update "DATA" header chunk at byte 508

  unsigned long int paySize1 = file2.size() - 8;
  unsigned long int numSamp = ((file2.size() - 512)/256) * 505;
  unsigned long int paySize2 = file2.size() - 512;

  data[3] = paySize1 >> 24;//shift it over so only last byte exists
  data[2] = paySize1 >> 16;
  data[1] = paySize1 >> 8;
  data[0] = paySize1;
  //    Update "RIFF" header chunk at byte 4
  file2.seek(4);
  file2.write(data, sizeof(data));

  data[3] = numSamp >> 24;//shift it over so only last byte exists
  data[2] = numSamp >> 16;
  data[1] = numSamp >> 8;
  data[0] = numSamp;
  //    Update "FACT" header chunk at byte 48
  file2.seek(48);
  file2.write(data, sizeof(data));

  data[3] = paySize2 >> 24;//shift it over so only last byte exists
  data[2] = paySize2 >> 16;
  data[1] = paySize2 >> 8;
  data[0] = paySize2;
  //    Update "DATA" header chunk at byte 508
  file2.seek(508);
  file2.write(data, sizeof(data));
  file2.close();
}

void loop() {
  // put your main code here, to run repeatedly:

}

Step 3 : After successful uploading, open your serial monitor. It will take 5 to 10 minutes to complete the entire process. So be patient.

Step 4 : Open your SD card on computer. “RECORD2.WAV” will be generated in the SD card with same data as “RECORD1.WAV”. Check the file size of “RECORD1.WAV” and “RECORD2.WAV”

0 0 vote
Article Rating

Published by

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
X