From: http://blog.csdn.net/dansen_xu/archive/2008/05/09/2425498.aspx
在网上找到uda1341的驱动,编译成模块uda1341.ko,打开内核配置菜单
Device Drivers --->
Sound --->
Sound card support
make modules 生成 soundcore.ko 模块,加载这两个模块就可以播放音频了
仔细看了uda1341的驱动,发现只能播放16bit的音频,单声道的音频在应用程序
向驱动复制时转化成双声道。所以最好选择双声道16bit的wav音频。
先看看wav文件的格式,前面是一些音频信息,可以读取作为参数。
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示例
RIFF WAVE Chunk
==================================
| | 所占字节数| 具体内容 |
==================================
| ID | 4 Bytes | 'RIFF' |
----------------------------------
| Size | 4 Bytes | |
----------------------------------
| Type | 4 Bytes | 'WAVE' |
----------------------------------
图2 RIFF WAVE Chunk
以'RIFF'作为标示,然后紧跟着为size字段,该size是整个wav文件大小减去ID和Size所占用的字节数,即FileLen - 8 = Size。然后是Type字段,为'WAVE',表示是wav文件。
结构定义如下:
struct RIFF_HEADER
{
char szRiffID[4];
DWORD dwRiffSize;
char szRiffFormat[4];
};
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 | 每秒所需字节数 |
--------------------------------------------------------------------
| BlockAlign | 2 Bytes | 数据块对齐单位(每个采样字节数,声道*采样bit/8)|
--------------------------------------------------------------------
| BitsPerSample | 2 Bytes | 采样量化bit数 |
--------------------------------------------------------------------
| | 2 Bytes | 附加信息(可选,通过Size来判断有无)|
--------------------------------------------------------------------
图3 Format Chunk
以'fmt '作为标示。一般情况下Size为16,此时最后附加信息没有;如果为18
则最后多了2个字节的附加信息。主要由一些软件制成的wav格式中含有该2个字节的
附加信息。
结构定义如下:
struct FMT_BLOCK
{
char szFmtID[4];
DWORD dwFmtSize;
WAVE_FORMAT wavFormat;
};
struct WAVE_FORMAT
{
WORD wFormatTag;
WORD wChannels;
DWORD dwSamplesPerSec;
DWORD dwAvgBytesPerSec;
WORD wBlockAlign;
WORD wBitsPerSample;
};
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];
DWORD dwFactSize;
};
Data Chunk
==================================
| | 所占字节数| 具体内容 |
==================================
| ID | 4 Bytes | 'data' |
----------------------------------
| Size | 4 Bytes | |
----------------------------------
| data | | |
----------------------------------
图5 Data Chunk
Data Chunk头结构定义如下:
struct DATA_BLOCK
{
char szDataID[4];
DWORD dwDataSize;
};
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位置安排方式
用ultraedit打开xp关机声的wav文件,可以看这些参数
最重要的三个参数就是 audio_channels 声道数, audio_fmt 采样位数, audio_rate 采样频率。这样每秒就需要把(采样频率×采样位数×声道)/ 8 字节的数据通过IIS接口送给 uda1341 。
以下是网上的一个播放wav文件的c程序,稍加修改就可以在ARM上播放。这里我已经知道参数,所以只读一次跳过,并未使用。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define OPEN_DSP_FAILED 0x00000001 /*open dsp failed!*/
#define SAMPLERATE_STATUS 0x00000002 /*samplerate status failed*/
#define SET_SAMPLERATE_FAILED 0x00000003 /*set samplerate failed*/
#define CHANNELS_STATUS 0x00000004 /*Channels status failed*/
#define SET_CHANNELS_FAILED 0x00000005 /*set channels failed*/
#define FMT_STATUS 0x00000006 /*FMT status failed*/
#define SET_FMT_FAILED 0x00000007 /*set fmt failed*/
#define OPEN_FILE_FAILED 0x00000008 /*opem filed failed*/
int S32410_Audio_Play(char *pathname,int nSampleRate,int nChannels,int fmt)
{
int dsp_fd,mix_fd,status,arg;
dsp_fd = open("/dev/sound/dsp" , O_RDWR); /*open dsp 注意设备的地址*/
if(dsp_fd < 0)
{
return OPEN_DSP_FAILED;
}
arg = nSampleRate;
status = ioctl(dsp_fd,SOUND_PCM_WRITE_RATE,&arg); /*set samplerate*/
if(status < 0)
{
close(dsp_fd);
return SAMPLERATE_STATUS;
}
if(arg != nSampleRate)
{
close(dsp_fd);
return SET_SAMPLERATE_FAILED;
}
arg = nChannels; /*set channels*/
status = ioctl(dsp_fd, SOUND_PCM_WRITE_CHANNELS, &arg);
if(status < 0)
{
close(dsp_fd);
return CHANNELS_STATUS;
}
if( arg != nChannels)
{
close(dsp_fd);
return SET_CHANNELS_FAILED;
}
arg = fmt; /*set bit fmt*/
status = ioctl(dsp_fd, SOUND_PCM_WRITE_BITS, &arg);
if(status < 0)
{
close(dsp_fd);
return FMT_STATUS;
}
if(arg != fmt)
{
close(dsp_fd);
return SET_FMT_FAILED;
} /*以上设置三个重要参数 默认就是 44100 2 16*/
FILE *file_fd = fopen(pathname,"r");
if(file_fd == NULL)
{
close(dsp_fd);
return OPEN_FILE_FAILED;
}
int num = 3*nChannels*nSampleRate*fmt/8; //每次写的字节数
int get_num; //其实每次写8192个字节的整数倍最好
char buf[num]; //因为驱动里就是开辟了8块8192大小的 DMA buffer,每次需要把一个buffer写满,才通过DMA发送,这样循环次数会更少.
get_num = fread(buf,1,36,file_fd); //跳过wav文件音频信息头 36个字节
while(feof(file_fd) == 0)
{
get_num = fread(buf,1,num,file_fd);
write(dsp_fd,buf,get_num);
if(get_num != num)
{
close(dsp_fd);
fclose(file_fd);
return 0;
}
}
close(dsp_fd);
fclose(file_fd);
return 0;
}
int main()
{
int value;
value = S32410_Audio_Play("down.wav",44100,2,16);
fprintf(stderr,"value is %d\n",value);
return 0;
}