x86和ARM9的录音
时间:2010-08-10 来源:usa9002
WAV格式
概述 WAV格式是微软公司开发的一种声音文件格式,也叫波形声音文件,是最早的数字音频格式,被Windows平台及其应用程序广泛支持。WAV格式支持许多 压缩算法,支持多种音频位数、采样频率和声道,采用44.1kHz的采样频率,16位量化位数,因此WAV的音质与CD相差无几,但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:所有数值表示均为低字节表示低位,高字节表示高位。
----------
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 <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdio.h>
#include <linux/soundcard.h>
#include <termios.h>
#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)
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdio.h>
#include <linux/soundcard.h>
#include <termios.h>
#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();
}
好了。基本都这样。
==================================
| |所占字节数| 具体内容 |
==================================
| 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 <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdio.h>
#include <linux/soundcard.h>
#include <termios.h>
#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 <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdio.h>
#include <linux/soundcard.h>
#include <termios.h>
#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();
}
好了。基本都这样。
相关阅读 更多 +