Understand Wave Format, and Implement a Wave Reader



I found there is no detail discussion for extra data of wave format. Here I post a explicit (for myself) wave header in below:

Data in each column are all in little endian.


Sample Valuesize in byteDescriptionoffset
Chunk ID4Marks the file as a riff file.,Characters are each 1 byte long
That should be "RIFF".
0
Chunk Size44 + (8 + SubChunk1Size) + (8 + SubChunk2Size)4
Format4File Type Header.,For wave purposes, it always equals "WAVE".8
Format Mark4Format chunk marker.,Includes trailing null. should be "fmt "12
Sub-chunk 1 Size4Length of format data16
Audio Format2PCM = 1 (i.e. Linear quantization),Values other than 1 indicate some,form of compression.20
Number of Channels2Mono = 1, Stereo = 2, etc.22
Sampling Rate4Number of Samples per second24
Byte Rate4Byte per second, it should be SamplingRate*NumberOfChannel*BitsPerSample/828
Block Align2Byte for whole sampling point,, it should be NumberOfChannel*BitsPerSample/832
Bits Per Sample28 bits = 8, 16 bits = 1634
Extra Data Header?(2)Extra wave note, in most,case, that "size" is 0 (no extra data), or 2.
This size could be computed by "Subchunk1Size - 16 "
36
Extra Data?extra data, most for audio notes(artist, album, publish year, music type. etc)?
Sub chunk 2 ID4Contains the letters "data"??
Sub chunk 2 size4Audio data length?? + 4
Audio dataAudio data lengthAudio data?? + 8


Lots posts has discuss wave file without extra data, in here, I just note about extra data:

    Length of extra data  is various, but it should be multiple of 4.

    Extra data is useful for audience, it is not junk. If your wave file is within extra data, just print that data in your console or window.

    To find where is the end of extra data, is to find where is the beginning of sub-chunk2. That is, to find the "data" string.


Below is my implement to read a wave file in C.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef TRUE
#define TRUE        (1)
#define FALSE        (0)
#endif

#define MAX_STR_LEN       (256)


#ifdef _MSC_VER
#pragma warning(disable:4996) /*fuss*/

#include <stdarg.h>

int snprintf(char *pBuf, size_t maxN, const char *pFmt, ...)
{
 int len; 
 va_list argList; 
 
 va_start(argList, pFmt); 

 len = vsprintf_s(pBuf, maxN, pFmt, argList);

 va_end(argList); 

 return len;
}/*snprintf*/

#endif


unsigned int GetFileSize(FILE *pFile)
{
 unsigned int currentLen, fileSize;

 currentLen = ftell(pFile);

 fseek(pFile,0, SEEK_END);
 fileSize = ftell(pFile);   

 fseek(pFile,currentLen ,SEEK_SET);
 

 return (unsigned int)fileSize;

}/*GetFileSize*/


unsigned int GetRemainderFileSize(FILE *pFile)
{
 unsigned int currentLen, fileSize;

 currentLen = ftell(pFile);

 fseek(pFile,0, SEEK_END);
 fileSize = ftell(pFile);   

 fseek(pFile,currentLen ,SEEK_SET);

 return (unsigned int)(fileSize - currentLen );

}/*GetRemainderFileSize*/


typedef struct _WaveFeature
{
 char               ChunkID[4];
 /* "RIFF" Header      */ 
 
 unsigned int       ChunkSize;
 /* 36 + SubChunk2Size, or more precisely:4 + (8 + SubChunk1Size) + (8 + SubChunk2Size)  */
 
 char               Format[4];
 /* "WAVE"  */
 
 char               fmt[4];
 /* "fmt "*/
 
 unsigned int       Subchunk1Size;
 /* Size of the fmt chunk */
 
 unsigned short     AudioFormat;
 /* Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM */
 
 unsigned short     NumChannels;
 /* Number of channels 1=Mono 2=Sterio */
 
 unsigned int       SampleRate;
 /* Sampling Frequency in Hz*/
 
 unsigned int       ByteRate;
 /* bytes per second,  SampleRate * NumChannels * BitsPerSample/8*/
 
 unsigned short     BlockAlign;
 /* 2=16-bit mono, 4=16-bit stereo, NumChannels * BitsPerSample/8*/
 
 unsigned short     BitsPerSample;
 /* Number of bits per sample  */
} WaveFeature;


typedef struct _WaveData
{
 char               Subchunk2ID[4]; /* "data"  string   */
 unsigned int       Subchunk2Size; /* Sampled data length    */
} WaveData;


int GetWaveFeatures(unsigned char *pWaveHead, unsigned short *pNumChannel, 
    unsigned int *pNumSamplesPerSec,  unsigned short *pBitsPerSample, 
    unsigned int *pBytesPerSec)
{
 
 char charWord[MAX_STR_LEN];  
 snprintf(&charWord[0], MAX_STR_LEN, "RIFF");
 if(0 != memcmp( &pWaveHead[0], &charWord[0], 4) ) 
  return -1;   
 
 
 snprintf(&charWord[0], MAX_STR_LEN, "WAVE");
 if(0 != memcmp( &pWaveHead[8], &charWord[0], 4) ) 
  return -1;   
 
 snprintf(&charWord[0], MAX_STR_LEN, "fmt ");
 if(0 != memcmp( &pWaveHead[12], &charWord[0], 4) ) 
  return -1; 

 unsigned int  chunkSize;
 memcpy( &chunkSize, &pWaveHead[16], 4);

 *pNumChannel = *((unsigned short*)(pWaveHead + 22));
 *pNumSamplesPerSec = *((unsigned int*)(pWaveHead + 24));
 *pBitsPerSample = *((unsigned short*)(pWaveHead + 34));
 
 *pBytesPerSec = *((unsigned int*)(pWaveHead + 28)); /*ByteRate         */
 if(*pBytesPerSec != (*pNumSamplesPerSec)*(*pNumChannel)*(*pBitsPerSample/8))
  return -2;

 int blockAlign;

 blockAlign = *((unsigned short*)(pWaveHead + 32)); 
 if(blockAlign != (*pNumChannel)*(*pBitsPerSample/8))
  return -2;

 return 0;
}/*GetWaveInfo*/


int GetWaveLength(unsigned char *pWaveHead, unsigned int *pAudioDataLen)
{
 char charWord[MAX_STR_LEN];  
 snprintf(&charWord[0], MAX_STR_LEN, "data");

 
 if(0 != memcmp( &pWaveHead[0], &charWord[0], 4) )
  return -1;  
 
 *pAudioDataLen = *((unsigned int*)(pWaveHead + 4));

 return 0;
}/*GetWaveLength*/


int main(int argc, char *argv[])
{
 
 char inputFileName[MAX_STR_LEN];

 snprintf(&inputFileName[0], MAX_STR_LEN, "03 Rondeau in C Minor.wav");
    
#if defined(__MACH__) && defined(_DEBUG)/*xcode stupid...*/
    char tempStr[MAX_STR_LEN];
    snprintf(&tempStr[0], MAX_STR_LEN, "../../../../../");
    strncat(&tempStr[0], &inputFileName[0], strnlen(&inputFileName[0],MAX_STR_LEN));
    memset(&inputFileName[0], 0, MAX_STR_LEN);
    strncpy(&inputFileName[0], &tempStr[0], strnlen(&tempStr[0],MAX_STR_LEN));
#endif
    
 int k; 
 k = 0;

 while (k < argc) 
 {
  if( 0 == strncmp("-i",argv[k], MAX_STR_LEN))
  {
   if(k + 1 < argc)
   {
    snprintf(inputFileName, MAX_STR_LEN, "%s", argv[k + 1]); 
   }
   else
   {
    printf("-i should be fellowed by input wav file.");
   }/*if*/
  }/*if strncmp*/  

  k++;
 }/*while opt*/

 FILE *pWaveFile;

 pWaveFile = fopen(&inputFileName[0], "rb");

 if(NULL == pWaveFile)
 {
  printf("open file %s error \n", inputFileName);
  return -1;
 }/*if NULL == pWaveFile*/

 printf("file : %s\n", inputFileName);
 
 WaveFeature wavFea;

 fread(&wavFea, sizeof(WaveFeature), 1, pWaveFile);

 unsigned short nChannel; 
 unsigned int nSamplesPerSec;
 unsigned short bitsPerSample;
 unsigned int audioDataLen;
 unsigned int bytesPerSec;
 int isContentExtaBytes;

 int sts;
 sts = GetWaveFeatures((unsigned char*)&wavFea, &nChannel, &nSamplesPerSec, 
  &bitsPerSample,&bytesPerSec);

 
 if(-1 == sts)
 {
  printf("not a wav format\n");
  return -1;
 }
 else if(-2 == sts)
 {
  printf("wav format do not be consisted\n");
  return -1;
 }
 else if(-3 == sts)
 {
  printf("extra parameters are not known\n");
 }/*if -1 == sts*/

 printf("%u channel\n", nChannel);
 printf("simpling frequency = %u\n", nSamplesPerSec);
 printf("%u bits Per Sample\n", bitsPerSample);
 

 {/*scan the data flag, and print extra infomation...*/
  char readBuff[MAX_STR_LEN];
  char charWorddata[8];
  unsigned int ii, jj;
 
  memset(&readBuff[0], 0, MAX_STR_LEN);
  memset(&charWorddata[0], 0, 8);
  snprintf(&charWorddata[0], 8, "data");
 
  unsigned int remainLen;
  int isFoundDataFlag;
  isFoundDataFlag = FALSE;
  remainLen = GetRemainderFileSize(pWaveFile);

  ii = 0;
  while(MAX_STR_LEN < remainLen)
  {

   fread(&readBuff[0], 1, MAX_STR_LEN, pWaveFile);
   jj = 0;
   while(jj < MAX_STR_LEN)
   {
    if(0 == memcmp(&readBuff[jj], &charWorddata[0], 4))
    {
     isFoundDataFlag = TRUE; 
     break;
    }
    else
    {
     if(0 != jj && 0 == readBuff[jj] &&  0 != readBuff[jj - 1])
      printf("\n");
     else
      printf("%c", readBuff[jj]);
    }/*if */

    jj++; 
   }
   if(FALSE != isFoundDataFlag)
    break;
   remainLen -= MAX_STR_LEN;
   ii++;
  }

  if(FALSE == isFoundDataFlag)
  {
   printf("no data flag has been found\n");
   return -2;
  }/*if FALSE == isFoundDataFlag*/

  fseek(pWaveFile, -(MAX_STR_LEN-jj), SEEK_CUR);
 }/*local variables*/


 WaveData wavData;

 fread(&wavData, sizeof(WaveData), 1, pWaveFile);
 sts = GetWaveLength((unsigned char*)&wavData, &audioDataLen);

 if(-1 == sts)
 {
  printf("not a wav format\n");
  return -1;
 }

 audioDataLen = wavData.Subchunk2Size; 

 if(GetRemainderFileSize(pWaveFile) != audioDataLen)
 {
  printf("warning: data length is not consistency\n");
  
  unsigned int remainderLen;
  remainderLen = GetRemainderFileSize(pWaveFile);

  printf("remainderLen = %u\n", remainderLen);  
  printf("audioDataLen = %u\n", audioDataLen);

  if((unsigned int)audioDataLen > remainderLen)
   audioDataLen = remainderLen;
 }/*if */

 float audioPlayTime;
 audioPlayTime = (float)audioDataLen/bytesPerSec;

 printf("audio time = %3.2f sec", audioPlayTime);

 if(audioPlayTime > 60.0f)
  printf(" (%4.3fmins)\n", audioPlayTime/60.0f); 
 else
  printf("\n");

 return 0;
 
}/*main*/


You could use command line -i XXX.wav to specified what file you want to read.



Above is the result when I read a wave file, 03 Rondeau in C Minor.wav.






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值