因为客户需要用到wav文件来测试播放我们的平台,而客户的应用程序,用在我们的平台上,会有一些问题,所以,我需要从网络上找开源的wav的播放器,终于在网络上找到了wavplay播放器.虽然是基于OSS架构的wav的播放器,不过没有关系,自己还是先来熟悉这个开源的代码吧, 最新的版本是2.0版本,源码的下载地址如下:
http://sourceforge.net/projects/wavplay/?source=dlp 节后抽个时间将其移植到ARM平台上去,这个小软件不管是用来测试,还是用来移植到实际的项目,对我们来说,都是很好的一个参考源码。你说是不?
一:wav文件格式 [引用网络,已做过修改]
wave文件作为多媒体中使用的声波文件格式之一,它是以RIFF格式为标准的。RIFF是英文Resource Interchange File Format的缩写,每个WAVE文件的头四个字节便是“RIFF”,WAVE文件是由若干个Chunk组成的。按照在文件中的出现位置包括:RIFF WAVE Chunk, Format Chunk, Fact Chunk(可选), Data Chunk。如下图所示:
其中除了Fact Chunk外,其他三个Chunk是必须的。每个Chunk有各自的ID,位于Chunk最开始位置,作为标示,而且均为4个字节。并且紧跟在ID后面的是Chunk大小(去除ID和Size所占的字节数后剩下的其他字节数目),4个字节表示,低字节表示数值低位,高字节表示数值高位。下面具体介绍各个Chunk内容。注意: 所有数值表示均为低字节表示低位,高字节表示高位。
1):RIFF WAVE Chunk
以'FIFF'作为标示,然后紧跟着为size字段,该size是整个wav文件大小减去ID和Size所占用的字节数,即FileLen - 8 = Size。然后是Type字段,为'WAVE',表示是wav文件。
2):Format Chunk
以'fmt '作为标示。一般情况下Size为16,此时最后附加信息没有;如果为18,则最后多了2个字节的附加信息。主要由一些软件制成的wav格式中含有该2个字节的附加信息。
3):Fact Chunk
Fact Chunk是可选字段,一般当wav文件由某些软件转化而成,则包含该Chunk。
4):Data Chunk
Data Chunk是真正保存wav数据的地方,以'data'作为该Chunk的标示。然后是数据的大小。紧接着就是wav数据。根据Format Chunk中的声道数以及采样bit数,wav数据的bit位置可以分成以下几种形式:
二:wav文件格式解码
具体的代码,可以仔细研究wavplay的源码中的wavfile.c和wavfile.h文件,这两个文件主要是对wav文件格式进行解码,具体的部分代码如下:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- static const char rcsid[] = "$Id: wavfile.c,v 1.3 2009/11/30 15:02:31 ve3wwg Exp $";
-
- #include <stdio.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <string.h>
-
- #include "wavplay.h"
-
- #define BUFFERSIZE 1024
- #define PCM_WAVE_FORMAT 1
-
- #define TRUE 1
- #define FALSE 0
-
- typedef struct
- { u_long dwSize ;
- u_short wFormatTag ;
- u_short wChannels ;
- u_long dwSamplesPerSec ;
- u_long dwAvgBytesPerSec ;
- u_short wBlockAlign ;
- u_short wBitsPerSample ;
- } WAVEFORMAT ;
-
- typedef struct
- { char RiffID [4] ;
- u_long RiffSize ;
- char WaveID [4] ;
- char FmtID [4] ;
- u_long FmtSize ;
- u_short wFormatTag ;
- u_short nChannels ;
- u_long nSamplesPerSec ;
- u_long nAvgBytesPerSec ;
- u_short nBlockAlign ;
- u_short wBitsPerSample ;
- char DataID [4] ;
- u_long nDataBytes ;
- } WAVE_HEADER ;
-
-
-
- char* findchunk (char* s1, char* s2, size_t n) ;
-
-
-
-
- static WAVE_HEADER waveheader =
- { { 'R', 'I', 'F', 'F' },
- 0,
- { 'W', 'A', 'V', 'E' },
- { 'f', 'm', 't', ' ' },
- 16,
- PCM_WAVE_FORMAT,
- 0,
- 0,
- 0,
- 0,
- 0,
- { 'd', 'a', 't', 'a' },
- 0
- } ;
-
- static ErrFunc v_erf;
-
-
-
-
- static void
- err(const char *format,...) {
- va_list ap;
-
- if ( v_erf == NULL )
- return;
- va_start(ap,format);
- v_erf(format,ap);
- va_end(ap);
- }
-
- int WaveWriteHeader (int wavefile, int channels, u_long samplerate, int sampbits, u_long samples, ErrFunc erf)
- { u_long databytes ;
- u_short blockalign ;
-
- v_erf = erf;
-
- if ( wavefile < 0 ) {
- err("Invalid file descriptor");
- return WW_BADOUTPUTFILE ;
- }
-
- sampbits = (sampbits == 16) ? 16 : 8 ;
-
- blockalign = ((sampbits == 16) ? 2 : 1) * channels ;
- databytes = samples * (u_long) blockalign ;
-
- waveheader.RiffSize = sizeof (WAVE_HEADER) + databytes - 8 ;
- waveheader.wFormatTag = PCM_WAVE_FORMAT ;
- waveheader.nChannels = channels ;
- waveheader.nSamplesPerSec = samplerate ;
- waveheader.nAvgBytesPerSec = samplerate * (u_long) blockalign ;
- waveheader.nBlockAlign = blockalign ;
- waveheader.wBitsPerSample = sampbits ;
- waveheader.nDataBytes = databytes;
-
- if (write (wavefile, &waveheader, sizeof (WAVE_HEADER)) != sizeof (WAVE_HEADER)) {
- err("%s",strerror(errno));
- return WW_BADWRITEHEADER ;
- }
-
- return 0 ;
- } ;
-
- int WaveReadHeader (int wavefile, int* channels, u_long* samplerate, int* samplebits, u_long* samples, u_long* datastart,ErrFunc erf)
- { static WAVEFORMAT waveformat ;
- static char buffer [ BUFFERSIZE ] ;
- char* ptr ;
- u_long databytes ;
-
- v_erf = erf;
-
- if (lseek (wavefile, 0L, SEEK_SET)) {
- err("%s",strerror(errno));
- return WR_BADSEEK ;
- }
-
- read (wavefile, buffer, BUFFERSIZE) ;
-
- if (findchunk (buffer, "RIFF", BUFFERSIZE) != buffer) {
- err("Bad format: Cannot find RIFF file marker");
- return WR_BADRIFF ;
- }
-
- if (! findchunk (buffer, "WAVE", BUFFERSIZE)) {
- err("Bad format: Cannot find WAVE file marker");
- return WR_BADWAVE ;
- }
-
- ptr = findchunk (buffer, "fmt ", BUFFERSIZE) ;
-
- if (! ptr) {
- err("Bad format: Cannot find 'fmt' file marker");
- return WR_BADFORMAT ;
- }
-
- ptr += 4 ;
- memcpy (&waveformat, ptr, sizeof (WAVEFORMAT)) ;
-
- if (waveformat.dwSize < (sizeof (WAVEFORMAT) - sizeof (u_long))) {
- err("Bad format: Bad fmt size");
- return WR_BADFORMATSIZE ;
- }
-
- if (waveformat.wFormatTag != PCM_WAVE_FORMAT) {
- err("Only supports PCM wave format");
- return WR_NOTPCMFORMAT ;
- }
-
- ptr = findchunk (buffer, "data", BUFFERSIZE) ;
-
- if (! ptr) {
- err("Bad format: unable to find 'data' file marker");
- return WR_NODATACHUNK ;
- }
-
- ptr += 4 ;
- memcpy (&databytes, ptr, sizeof (u_long)) ;
-
-
-
- *channels = waveformat.wChannels ;
- *samplerate = waveformat.dwSamplesPerSec ;
- *samplebits = waveformat.wBitsPerSample ;
- *samples = databytes / waveformat.wBlockAlign ;
-
- *datastart = ((u_long) (ptr + 4)) - ((u_long) (&(buffer[0]))) ;
-
- if (waveformat.dwSamplesPerSec != waveformat.dwAvgBytesPerSec / waveformat.wBlockAlign) {
- err("Bad file format");
- return WR_BADFORMATDATA ;
- }
-
- if (waveformat.dwSamplesPerSec != waveformat.dwAvgBytesPerSec / waveformat.wChannels / ((waveformat.wBitsPerSample == 16) ? 2 : 1)) {
- err("Bad file format");
- return WR_BADFORMATDATA ;
- }
-
- return 0 ;
- } ;
-
-
-
- #if 0
- char* WaveFileError (int errno)
- { switch (errno)
- { case WW_BADOUTPUTFILE : return "Bad output file.\n" ;
- case WW_BADWRITEHEADER : return "Not able to write WAV header.\n" ;
-
- case WR_BADALLOC : return "Not able to allocate memory.\n" ;
- case WR_BADSEEK : return "fseek failed.\n" ;
- case WR_BADRIFF : return "Not able to find 'RIFF' file marker.\n" ;
- case WR_BADWAVE : return "Not able to find 'WAVE' file marker.\n" ;
- case WR_BADFORMAT : return "Not able to find 'fmt ' file marker.\n" ;
- case WR_BADFORMATSIZE : return "Format size incorrect.\n" ;
- case WR_NOTPCMFORMAT : return "Not PCM format WAV file.\n" ;
- case WR_NODATACHUNK : return "Not able to find 'data' file marker.\n" ;
- case WR_BADFORMATDATA : return "Format data questionable.\n" ;
- default : return "No error\n" ;
- } ;
- return NULL ;
- } ;
- #endif
-
-
- char* findchunk (char* pstart, char* fourcc, size_t n)
- { char *pend ;
- int k, test ;
-
- pend = pstart + n ;
-
- while (pstart < pend)
- { if (*pstart == *fourcc)
- { test = TRUE ;
- for (k = 1 ; fourcc [k] != 0 ; k++)
- test = (test ? ( pstart [k] == fourcc [k] ) : FALSE) ;
- if (test)
- return pstart ;
- } ;
- pstart ++ ;
- } ;
-
- return NULL ;
- } ;
-
-
具体的解码分析,俺就不分析了。不是很难的代码,至于其它部分的代码,就不贴出来了,可以自己去下载代码来分析,我下载的是1.5B版本,最新的是2.0版本。