***************************************************************************************************************************
作者:EasyWave 时间:2013.02.05
类别:Linux 应用之wavplay播放器 声明:转载,请保留链接
注意:如有错误,欢迎指正。这些是我学习的日志文章......
***************************************************************************************************************************
因为客户需要用到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文件格式进行解码,具体的部分代码如下:
/* $Id: wavfile.c,v 1.3 2009/11/30 15:02:31 ve3wwg Exp $
* Copyright: wavfile.c (c) Erik de Castro Lopo erikd@zip.com.au
*
* wavfile.c - Functions for reading and writing MS-Windoze .WAV files.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details (licensed by file COPYING or GPLv*).
*
* This code was originally written to manipulate Windoze .WAV files
* under i386 Linux (erikd@zip.com.au).
*
* ve3wwg@gmail.com
*/
static const char rcsid[] = "$Id: wavfile.c,v 1.3 2009/11/30 15:02:31 ve3wwg Exp $";
#include
#include
#include
#include
#include
#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, /* FmtSize*/
PCM_WAVE_FORMAT, /* wFormatTag*/
0, /* nChannels*/
0,
0,
0,
0,
{ 'd', 'a', 't', 'a' },
0
} ; /* waveheader*/
static ErrFunc v_erf; /* wwg: Error reporting function */
/*
* Error reporting function for this source module:
*/
static void
err(const char *format,...) {
va_list ap;
if ( v_erf == NULL )
return; /* Only report error if we have function */
va_start(ap,format);
v_erf(format,ap); /* Use caller's supplied function */
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; /* wwg: Set error reporting function */
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)); /* wwg: report the error */
return WW_BADWRITEHEADER ;
}
return 0 ;
} ; /* WaveWriteHeader*/
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 ] ; /* Function is not reentrant.*/
char* ptr ;
u_long databytes ;
v_erf = erf; /* wwg: Set error reporting function */
if (lseek (wavefile, 0L, SEEK_SET)) {
err("%s",strerror(errno)); /* wwg: Report error */
return WR_BADSEEK ;
}
read (wavefile, buffer, BUFFERSIZE) ;
if (findchunk (buffer, "RIFF", BUFFERSIZE) != buffer) {
err("Bad format: Cannot find RIFF file marker"); /* wwg: Report error */
return WR_BADRIFF ;
}
if (! findchunk (buffer, "WAVE", BUFFERSIZE)) {
err("Bad format: Cannot find WAVE file marker"); /* wwg: report error */
return WR_BADWAVE ;
}
ptr = findchunk (buffer, "fmt ", BUFFERSIZE) ;
if (! ptr) {
err("Bad format: Cannot find 'fmt' file marker"); /* wwg: report error */
return WR_BADFORMAT ;
}
ptr += 4 ; /* Move past "fmt ".*/
memcpy (&waveformat, ptr, sizeof (WAVEFORMAT)) ;
if (waveformat.dwSize < (sizeof (WAVEFORMAT) - sizeof (u_long))) {
err("Bad format: Bad fmt size"); /* wwg: report error */
return WR_BADFORMATSIZE ;
}
if (waveformat.wFormatTag != PCM_WAVE_FORMAT) {
err("Only supports PCM wave format"); /* wwg: report error */
return WR_NOTPCMFORMAT ;
}
ptr = findchunk (buffer, "data", BUFFERSIZE) ;
if (! ptr) {
err("Bad format: unable to find 'data' file marker"); /* wwg: report error */
return WR_NODATACHUNK ;
}
ptr += 4 ; /* Move past "data".*/
memcpy (&databytes, ptr, sizeof (u_long)) ;
/* Everything is now cool, so fill in output data.*/
*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"); /* wwg: report error */
return WR_BADFORMATDATA ;
}
if (waveformat.dwSamplesPerSec != waveformat.dwAvgBytesPerSec / waveformat.wChannels / ((waveformat.wBitsPerSample == 16) ? 2 : 1)) {
err("Bad file format"); /* wwg: report error */
return WR_BADFORMATDATA ;
}
return 0 ;
} ; /* WaveReadHeader*/
/*===========================================================================================*/
#if 0
char* WaveFileError (int errno)
{ switch (errno)
{ case WW_BADOUTPUTFILE : return "Bad output file.
" ;
case WW_BADWRITEHEADER : return "Not able to write WAV header.
" ;
case WR_BADALLOC : return "Not able to allocate memory.
" ;
case WR_BADSEEK : return "fseek failed.
" ;
case WR_BADRIFF : return "Not able to find 'RIFF' file marker.
" ;
case WR_BADWAVE : return "Not able to find 'WAVE' file marker.
" ;
case WR_BADFORMAT : return "Not able to find 'fmt ' file marker.
" ;
case WR_BADFORMATSIZE : return "Format size incorrect.
" ;
case WR_NOTPCMFORMAT : return "Not PCM format WAV file.
" ;
case WR_NODATACHUNK : return "Not able to find 'data' file marker.
" ;
case WR_BADFORMATDATA : return "Format data questionable.
" ;
default : return "No error
" ;
} ;
return NULL ;
} ; /* WaveFileError*/
#endif
/*===========================================================================================*/
char* findchunk (char* pstart, char* fourcc, size_t n)
{ char *pend ;
int k, test ;
pend = pstart + n ;
while (pstart < pend)
{ if (*pstart == *fourcc) /* found match for first char*/
{ test = TRUE ;
for (k = 1 ; fourcc [k] != 0 ; k++)
test = (test ? ( pstart [k] == fourcc [k] ) : FALSE) ;
if (test)
return pstart ;
} ; /* if*/
pstart ++ ;
} ; /* while lpstart*/
return NULL ;
} ; /* findchuck*/
/* $Source: /cvsroot/wavplay/code/wavfile.c,v $ */
具体的解码分析,俺就不分析了。不是很难的代码,至于其它部分的代码,就不贴出来了,可以自己去下载代码来分析,我下载的是1.5B版本,最新的是2.0版本。