Chinaunix首页 | 论坛 | 博客
  • 博客访问: 48571
  • 博文数量: 26
  • 博客积分: 1175
  • 博客等级: 少尉
  • 技术积分: 300
  • 用 户 组: 普通用户
  • 注册时间: 2010-02-14 19:16
文章分类
文章存档

2011年(1)

2010年(25)

我的朋友

分类: LINUX

2010-08-10 11:05:58

WAV格式
概述 WAV格式是微软公司开发的一种声音文件格式,也叫波形声音文件,是最早的数字音频格式,被Windows平台及其应用程序广泛支持。WAV格式支持许多 压缩算法,支持多种音频位数、采样频率和声道,采用44.1kHz的采样频率,16位量化位数,因此WAV的音质与CD相差无几,但WAV格式对存储空间 需求太大不便于交流和传播.

声音格式

  WAV为微软公司(Microsoft)开发的一种声音文件格式,它符合RIFF(Resource Interchange File Format)文件规范,用于保存Windows平台的音频信息资源,被Windows平台及其应用程序所广泛支持,该格式也支持 MSADPCM,CCITT A LAW等多种压缩运算法,支持多种音频数字,取样频率和声道,标准格式化的WAV文件和CD格式一样,也是44.1K的取样频率,16位量化数字,因此在 声音文件质量和CD相差无几! WAV打开工具是WINDOWS的媒体播放器。
 WAVE文件作为多媒体中使用的声波文件格式之一,它是以RIFF格式为标准的。RIFF是英文Resource Interchange File Format的缩写,每个WAVE文件的头四个字节便是“RIFF”。 WAVE文件是由若干个Chunk组成的。按照在文件中的出现位置包括:RIFF WAVE Chunk, Format Chunk, Fact Chunk(可选), Data Chunk。具体见下图:
------------------------------------------------   
| RIFF WAVE Chunk |   
| ID = 'RIFF' |   
| RiffType = 'WAVE' |  
------------------------------------------------  
| Format Chunk |  
| ID = 'fmt ' |  
------------------------------------------------
| Fact Chunk(optional) |  
| ID = 'fact' |
------------------------------------------------
| Data Chunk |
| ID = 'data' |
------------
图1 Wav格式包含Chunk示例 其中除了Fact Chunk外,其他三个Chunk是必须的。每个Chunk有各自的ID,位于Chunk最开始位置,作为标示,而且均为4个字节。并且紧跟在ID后面的是Chunk大小(去除ID和Size所占的字节数后剩下的其他字节数目),4个字节表示,低字节表示数值低位,高字节表示数值高位。下面具体介绍各个Chunk内容。   
PS:所有数值表示均为低字节表示低位,高字节表示高位。
----------

二、具体介绍

  RIFF WAVE Chunk  
==================================   
| |所占字节数| 具体内容 |   
==================================   
| ID | 4 Bytes | 'RIFF' |   
----------------------------------   
| Size | 4 Bytes | |   
----------------------------------   
| Type | 4 Bytes | 'WAVE' |   
---------------------------------- 
图2 RIFF WAVE Chunk 以'FIFF'作为标示,然后紧跟着为size字段,该size是整个wav文件大小减去ID 和Size所占用的字节数,即FileLen - 8 = Size。然后是Type字段,为'WAVE',表示是wav文件。结构定义如下:   
struct RIFF_HEADER   {
char szRiffID[4]; // 'R','I','F','F'   
DWORD dwRiffSize;   
char szRiffFormat[4]; // 'W','A','V','E'   
};


Format Chunk   
=================================================   
| | 字节数 | 具体内容 |   
=================================================  
| ID | 4 Bytes | 'fmt ' |
-------------------------------------------------  
| Size | 4 Bytes | 数值为16或18,18则最后又附加信息|
------------------------------------------------
   
| FormatTag | 2 Bytes | 编码方式一般为0x0001|
------------------------------------------------
|| Channels | 2 Bytes | 声道数目,1--单声道;2--双声道|
 -------------------------------------------------
|| SamplesPerSec | 4 Bytes | 采样频率||
-------------------------------------------------
|| AvgBytesPerSec| 4 Bytes | 每秒所需字节数 ||===> WAVE_FORMAT 
-------------------------------------------------

|| BlockAlign | 2 Bytes | 数据块对齐单位(每个采样需要的字节数)||
 -------------------------------------------------
|| BitsPerSample | 2 Bytes | 每个采样需要的bit数 || 

-------------------------------------------------

|| | 2 Bytes | 附加信息(可选,通过Size来判断有无||

--------------------------------------------------
图3 Format Chunk 以'fmt '作为标示。一般情况下Size为16,此时最后附加信息没有;如果为18 则最后多了2个字节的附加信息。主要由一些软件制成的wav格式中含有该2个字节的附加信息。结构定义如下:   struct WAVE_FORMAT   
{   
WORD wFormatTag;   
WORD wChannels;   
DWORD dwSamplesPerSec;   
DWORD dwAvgBytesPerSec;   
WORD wBlockAlign;   
WORD wBitsPerSample;   
};   
struct FMT_BLOCK   
{   char szFmtID[4]; // 'f','m','t',' '   
      DWORD dwFmtSize;   
      WAVE_FORMAT wavFormat;
};
 Fact Chunk   
==================================   
|所占字节数| 具体内容 |   
==================================   
| ID | 4 Bytes | 'fact' |   
----------------------------------   
| Size | 4 Bytes | 数值为4 |   
----------------------------------   
| data | 4 Bytes | |   
----------------------------------
图4 Fact Chunk
Fact Chunk是可选字段,一般当wav文件由某些软件转化而成,则包含该Chunk。结构定义如下:   struct FACT_BLOCK   
{   
   char szFactID[4]; // 'f','a','c','t'   
   DWORD dwFactSize;   
};
 Data Chunk   
==================================   
| |所占字节数| 具体内容 |   
==================================   
| ID | 4 Bytes | 'data' |   
----------------------------------   
| Size | 4 Bytes | |   
----------------------------------   
| data | | |   
----------------------------------

 图5 Data Chunk
Data Chunk是真正保存wav数据的地方,以'data'作为该Chunk的标示。然后是数据的大小。紧接着就是wav数据。根据Format Chunk中的声道数以及采样bit数,wav数据的bit位置可以分成以下几种形式:   

---------------------------------------------
| 单声道 | 取样1 | 取样2 | 取样3 | 取样4 |
-------------------------------------------
| 8bit量化 | 声道0 | 声道0 | 声道0 | 声道0 |
--------------------------------------------
| 双声道 | 取样1 | 取样2 |
---------------------------------------------
| 8bit量化 | 声道0(左) | 声道1(右) | 声道0(左) | 声道1(右)|
 ---------------------------------------------
| | 取样1 | 取样2 | | 单声道 |
---------------------------------------------
| 16bit量化 | 声道0 | 声道0 | 声道0 | 声道0 | | | (低位字节) | (高位字节) | (低位字节) | (高位字节) |
--------------------------------------------
| | 取样1 |   | 双声道 |
--------------------------------------------
| 16bit量化 | 声道0(左) | 声道0(左) | 声道1(右) | 声道1(右) | | | (低位字节) | (高位字节) | (低位字节) | (高位字节) |
--------------------------------------------
图6 wav数据bit位置安排方式  Data Chunk头结构定义如下:struct DATA_BLOCK   
{   char szDataID[4]; // 'd','a','t','a'   
     DWORD dwDataSize;   
};


好了。基础说明好了。下面就是X86和ARM9的录音代码。基本上都差不多的

#include
#include
#include
#include
#include
#include
#include
#include


#define LENGTH    30        //录音时间,秒
#define RATE    44100            //采样频率
#define SIZE    16            //量化位数
#define CHANNELS 1                //声道数目
#define RSIZE    100                //buf的大小,
#define AUDIO_DEV_FILE "/dev/audio"

unsigned char buf[RSIZE];    //每次循环取得RSIZE大小的容量,放入buf,然后写入文件;放音是相反。

/********以下是wave格式文件的文件头格式说明******/
/*------------------------------------------------
|             RIFF WAVE Chunk                  |
|             ID = 'RIFF'                     |
|             RiffType = 'WAVE'                |
------------------------------------------------
|             Format Chunk                     |
|             ID = 'fmt '                      |
------------------------------------------------
|             Fact Chunk(optional)             |
|             ID = 'fact'                      |
------------------------------------------------
|             Data Chunk                       |
|             ID = 'data'                      |
------------------------------------------------*/
/**********以上是wave文件格式头格式说明***********/
/*wave 文件一共有四个Chunk组成,其中第三个Chunk可以省略,每个Chunk有标示(ID),
大小(size,就是本Chunk的内容部分长度),内容三部分组成*/
struct fhead {
/****RIFF WAVE CHUNK*/
    unsigned char a[4];            //四个字节存放'R','I','F','F'
    long int b;                    //整个文件的长度-8;每个Chunk的size字段,都是表示除了本Chunk的ID和SIZE字段外的长度;
    unsigned char c[4];            //四个字节存放'W','A','V','E'
/****RIFF WAVE CHUNK*/
/****Format CHUNK*/
    unsigned char d[4];            //四个字节存放'f','m','t',''
    long int e;                    //16后没有附加消息,18后有附加消息;一般为16,其他格式转来的话为18
    short int f;                //编码方式,一般为0x0001;
    short int g;                //声道数目,1单声道,2双声道;
    long int h;                    //采样频率;
    long int i;                    //每秒所需字节数;
    short int j;                //每个采样需要多少字节,若声道是双,则两个一起考虑;
    short int k;                //即量化位数
/****Format CHUNK*/
/***Data Chunk**/
    unsigned char p[4];            //四个字节存放'd','a','t','a'
    long int q;                    //语音数据部分长度,不包括文件头的任何部分
} wavehead;                        //定义WAVE文件的文件头结构体


int  set_wav_arg(int fd_dev_r,int fd_dev_w)
{
    int arg;
    int status;
    wavehead.a[0] = 'R';
    wavehead.a[1] = 'I';
    wavehead.a[2] = 'F';
    wavehead.a[3] = 'F';
    wavehead.b = LENGTH * RATE * CHANNELS * SIZE / 8 - 8;
    wavehead.c[0] = 'W';
    wavehead.c[1] = 'A';
    wavehead.c[2] = 'V';
    wavehead.c[3] = 'E';
    wavehead.d[0] = 'f';
    wavehead.d[1] = 'm';
    wavehead.d[2] = 't';
    wavehead.d[3] = ' ';
    wavehead.e = 16;
    wavehead.f = 1;
    wavehead.g = CHANNELS;
    wavehead.h = RATE;
    wavehead.i = RATE * CHANNELS * SIZE / 8;
    wavehead.j = CHANNELS * SIZE / 8;
    wavehead.k = SIZE;
    wavehead.p[0] = 'd';
    wavehead.p[1] = 'a';
    wavehead.p[2] = 't';
    wavehead.p[3] = 'a';
    wavehead.q = LENGTH * RATE * CHANNELS * SIZE / 8;
    /*以上wave 文件头赋值*/
#if 1
    arg = SIZE;
    status = ioctl(fd_dev_r, SOUND_PCM_WRITE_BITS, &arg);    //设置量化位数
    if (status == -1) {
        perror("Cannot set SOUND_PCM_WRITE_BITS ");
        return 1;
    }
    arg = CHANNELS;
    status = ioctl(fd_dev_r, SOUND_PCM_WRITE_CHANNELS, &arg);    //设置声道数
    if (status == -1) {
        perror("Cannot set SOUND_PCM_WRITE_CHANNELS");
        return 1;
    }
    arg = RATE;
    status = ioctl(fd_dev_r, SOUND_PCM_WRITE_RATE, &arg);    //设置采样率
    if (status == -1) {
        perror("Cannot set SOUND_PCM_WRITE_WRITE");
        return 1;
    }
#endif
/////////////////
#if 1
    arg = SIZE;
    status = ioctl(fd_dev_w, SOUND_PCM_WRITE_BITS, &arg);    //设置量化位数
    if (status == -1) {
        perror("Cannot set SOUND_PCM_WRITE_BITS ");
        return 1;
    }
    arg = CHANNELS;
    status = ioctl(fd_dev_w, SOUND_PCM_WRITE_CHANNELS, &arg);    //设置声道数
    if (status == -1) {
        perror("Cannot set SOUND_PCM_WRITE_CHANNELS");
        return 1;
    }
    arg = RATE;
    status = ioctl(fd_dev_w, SOUND_PCM_WRITE_RATE, &arg);    //设置采样率
    if (status == -1) {
        perror("Cannot set SOUND_PCM_WRITE_WRITE");
        return 1;
    }


#endif

}

int   recodred(int fd_dev_r)
{
    int status;
    int fd_f;
/**以下开始录音**/
    system("rm -f *.wav");        //清除已有的wav录音文件
    if ((fd_f = open("./sound.wav", O_CREAT | O_RDWR, 0777)) == -1)    //创建一个wave格式语音文件
    {
        perror("cannot creat the sound file");
    }
    if ((status = write(fd_f, &wavehead, sizeof(wavehead))) == -1)    //写入wave文件的文件头
    {
        perror("write to sound'head wrong!!");
    }
    while(1)
//    for (i = 0; i < (LENGTH * RATE * SIZE * CHANNELS / 8) / RSIZE; i++)    //每次从声卡获得RSIZE大小的数据,共循环了语音长度/RSIZE次
    {

        status = read(fd_dev_r, buf, sizeof(buf));
        if (status != sizeof(buf)) {
            perror("read wrong number of bytes");
        }
        if (write(fd_f, buf, status) == -1) {
            perror("write to sound wrong!!");
        }
   
    }
    close(fd_dev_r);            //关闭只读方式的声卡
    close(fd_f);                //关闭wave文件

}

int read_wav(int fd_dev_w)
{
    int status;
    int fd_f;
    int i;
    #if 1
/***以下是播放wav语音文件**/
        printf("Play...:\n");
        if ((fd_f = open("./sound.wav", O_RDONLY, 0777)) == -1) {
            perror("cannot creat the sound file");
        }
        lseek(fd_f, 44, SEEK_SET);//头文件打包包含的数据
        //for (i = 0; i < (LENGTH * RATE * SIZE * CHANNELS / 8) / RSIZE; i++)
        while(1) {
            status = read(fd_f, buf, sizeof(buf));    //读语音文件
            if(status == 0)
                break;
            if (status != sizeof(buf))
                perror("write wrong number of bytes");

            status = write(fd_dev_w, buf, sizeof(buf));    //送声卡播放
            if (status != sizeof(buf))
                perror("write wrong number of bytes");
                }
        close(fd_f);
        close(fd_dev_w);
        return 0;
#endif

}

int main(void)
{
    pthread_t thread1,thread2;
    int i;
    int fd_dev_r;
    int fd_dev_w;
/*以下wave 文件头赋值*/
    //打开声卡设备,只读方式;并对声卡进行设置
    #if 1
    fd_dev_r = open(AUDIO_DEV_FILE, O_RDONLY, 0777);
    if (fd_dev_r < 0) {
        perror("Cannot open  device");
        return 1;
    }
#endif
//打开声卡设备,只写方式;并对声卡进行设置
#if 1
    fd_dev_w = open(AUDIO_DEV_FILE, O_WRONLY, 0777);
    if (fd_dev_w < 0) {
        perror("Cannot open  device");
        return 1;
    }
#endif
    set_wav_arg(fd_dev_r, fd_dev_w);
    recodred(fd_dev_r );
//    read_wav(fd_dev_w);
}

ARM9录音代码
但是在ARM系统下DSP的设备之能是只读或者只写。不能可读可写要不DMA会出错
2dma2: loadbuffer:timeout loading buffer
3 dma2: loadbuffer:timeout loading buffer
4 dma2: loadbuffer:timeout loading buffer
5 dma2: loadbuffer:timeout loading buffer
6 dma2: loadbuffer:timeout loading buffer
7 dma2: loadbuffer:timeout loading buffer
8 dma2: loadbuffer:timeout loading buffer

采集的频率和参数不用我们自己定的。已经规定了。
采样率: 44100
采样位数: 16
声道数:2
还有一点就是有些S3C2440不支持录音的。原因是寄存器位置错了。所以要修改驱动的地址
s3c2440-audio.c和原来对比,做了如下修改:
在音频驱动的read函数中删除下面的内容,
if (ppos != &file->f_pos)

return -ESPIPE;

1077行删除,添加:

ai_dcon = 0xa2900000;



#include
#include
#include
#include
#include
#include
#include
#include


#define LENGTH    30            //录音时间,秒
#define RATE    44100            //采样频率
#define SIZE    16                //量化位数
#define CHANNELS 2                //声道数目
#define RSIZE    100            //buf的大小,
#define AUDIO_DEV_FILE "/dev/dsp"

unsigned char buf[RSIZE];        //每次循环取得RSIZE大小的容量,放入buf,然后写入文件;放音是相反。

/********以下是wave格式文件的文件头格式说明******/
/*------------------------------------------------
|             RIFF WAVE Chunk                  |
|             ID = 'RIFF'                     |
|             RiffType = 'WAVE'                |
------------------------------------------------
|             Format Chunk                     |
|             ID = 'fmt '                      |
------------------------------------------------
|             Fact Chunk(optional)             |
|             ID = 'fact'                      |
------------------------------------------------
|             Data Chunk                       |
|             ID = 'data'                      |
------------------------------------------------*/
/**********以上是wave文件格式头格式说明***********/
/*wave 文件一共有四个Chunk组成,其中第三个Chunk可以省略,每个Chunk有标示(ID),
大小(size,就是本Chunk的内容部分长度),内容三部分组成*/
struct fhead {
/****RIFF WAVE CHUNK*/
    unsigned char a[4];            //四个字节存放'R','I','F','F'
    long int b;                    //整个文件的长度-8;每个Chunk的size字段,都是表示除了本Chunk的ID和SIZE字段外的长度;
    unsigned char c[4];            //四个字节存放'W','A','V','E'
/****RIFF WAVE CHUNK*/
/****Format CHUNK*/
    unsigned char d[4];            //四个字节存放'f','m','t',''
    long int e;                    //16后没有附加消息,18后有附加消息;一般为16,其他格式转来的话为18
    short int f;                //编码方式,一般为0x0001;
    short int g;                //声道数目,1单声道,2双声道;
    long int h;                    //采样频率;
    long int i;                    //每秒所需字节数;
    short int j;                //每个采样需要多少字节,若声道是双,则两个一起考虑;
    short int k;                //即量化位数
/****Format CHUNK*/
/***Data Chunk**/
    unsigned char p[4];            //四个字节存放'd','a','t','a'
    long int q;                    //语音数据部分长度,不包括文件头的任何部分
} wavehead;                        //定义WAVE文件的文件头结构体


int set_wav_arg(void)
{
    wavehead.a[0] = 'R';
    wavehead.a[1] = 'I';
    wavehead.a[2] = 'F';
    wavehead.a[3] = 'F';
    wavehead.b = LENGTH * RATE * CHANNELS * SIZE / 8 - 8;
    wavehead.c[0] = 'W';
    wavehead.c[1] = 'A';
    wavehead.c[2] = 'V';
    wavehead.c[3] = 'E';
    wavehead.d[0] = 'f';
    wavehead.d[1] = 'm';
    wavehead.d[2] = 't';
    wavehead.d[3] = ' ';
    wavehead.e = 16;
    wavehead.f = 2;
    wavehead.g = CHANNELS;
    wavehead.h = RATE;
    wavehead.i = RATE * CHANNELS * SIZE / 8;
    wavehead.j = CHANNELS * SIZE / 8;
    wavehead.k = SIZE;
    wavehead.p[0] = 'd';
    wavehead.p[1] = 'a';
    wavehead.p[2] = 't';
    wavehead.p[3] = 'a';
    wavehead.q = LENGTH * RATE * CHANNELS * SIZE / 8;
    /*以上wave 文件头赋值 */

}

int recodred(void)
{
    int status;
    int fd_f;
    int fd_dev_r;
/**以下开始录音**/
    //打开声卡设备,只读方式;并对声卡进行设置
#if 1
    fd_dev_r = open(AUDIO_DEV_FILE, O_RDONLY, 0777);
    if (fd_dev_r < 0) {
        perror("Cannot open  device");
        return 1;
    }
#endif

    system("rm -f *.wav");        //清除已有的wav录音文件
    if ((fd_f = open("./sound.wav", O_CREAT | O_RDWR, 0777)) == -1)    //创建一个wave格式语音文件
    {
        perror("cannot creat the sound file");
    }
    if ((status = write(fd_f, &wavehead, sizeof(wavehead))) == -1)    //写入wave文件的文件头
    {
        perror("write to sound'head wrong!!");
    }
    while (1) {

        status = read(fd_dev_r, buf, sizeof(buf));
        if (status != sizeof(buf)) {
            perror("read wrong number of bytes");
        }
        if (write(fd_f, buf, status) == -1) {
            perror("write to sound wrong!!");
        }

    }
    close(fd_dev_r);            //关闭只读方式的声卡
    close(fd_f);                //关闭wave文件



}

int read_wav(void)
{
    int status;
    int fd_f;
    int i;
    int fd_dev_w;
#if 1
/***以下是播放wav语音文件**/
#if 1
    //打开声卡设备,只写方式;并对声卡进行设置
    fd_dev_w = open(AUDIO_DEV_FILE, O_WRONLY, 0777);
    if (fd_dev_w < 0) {
        perror("Cannot open  device");
        return 1;
    }
#endif

    printf("Play...:\n");
    if ((fd_f = open("./sound.wav", O_RDONLY, 0777)) == -1) {
        perror("cannot creat the sound file");
    }
    lseek(fd_f, 44, SEEK_SET);    //头文件打包包含的数据
    while (1) {
        status = read(fd_f, buf, sizeof(buf));    //读语音文件
        if (status == 0)
            break;
        if (status != sizeof(buf))
            perror("write wrong number of bytes");

        status = write(fd_dev_w, buf, sizeof(buf));    //送声卡播放
        if (status != sizeof(buf))
            perror("wrote2 wrong number of bytes");
    }
    close(fd_f);
    close(fd_dev_w);
    return 0;
#endif

}

int main(void)
{
    int i;
    int fd_dev_r;
    int fd_dev_w;
    set_wav_arg();
//  recodred();
    read_wav();
}

好了。基本都这样。



阅读(518) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~