alsa在pc上安装(我安装的是als-lib-1.0.22):
1、sudo tar xjf alsa-lib-1.0.22.tar.bz2
2、cd alsa-lib-1.0.22/
3、./configure
4、make
5、sudo make install
这就把alsa-libs库安装好了。
编译的时候后一定要加上编译选项 -lasound
gcc -o main.out main.c -lasound
如不加上 -lasound 则会输出类似如下编译问题:
/tmp/ccAG5teI.o: In function `main':
main.c:(.text+0x10a): undefined reference to `snd_output_stdio_attach'
main.c:(.text+0x15f): undefined reference to `snd_pcm_open'
main.c:(.text+0x192): undefined reference to `snd_pcm_dump'
main.c:(.text+0x1e8): undefined reference to `snd_pcm_dump'
main.c:(.text+0x210): undefined reference to `snd_pcm_drain'
测试,要加上sudo,否则可能执行不成功。
sudo ./main.out cq.wav
这样就可以播放.wav音乐了。
播放时输出的设备信息和声音文件头信息:
book@book-desktop:~/workspace/alsa$ sudo ./main.out cq.wav
Plug PCM: Direct Stream Mixing PCM
Hardware PCM card 0 'Ensoniq AudioPCI' device 0 subdevice 0
Its setup is:
stream : PLAYBACK
access : MMAP_INTERLEAVED
format : S16_LE
subformat : STD
channels : 2
rate : 48000
exact rate : 48000 (1572864000/32768)
msbits : 16
buffer_size : 16384
period_size : 1024
period_time : 21333
tstamp_mode : ENABLE
period_step : 1
avail_min : 1024
period_event : 0
start_threshold : 1
stop_threshold : 1073741824
silence_threshold: 0
silence_size : 1073741824
boundary : 1073741824
appl_ptr : 0
hw_ptr : 1024
Invalid rate plugin version 10002
Plug PCM: Rate conversion PCM (48000, sformat=S16_LE)
Protocol version: 10001
Its setup is:
stream : PLAYBACK
access : RW_INTERLEAVED
format : S16_LE
subformat : STD
channels : 2
rate : 44100
exact rate : 44100 (44100/1)
msbits : 16
buffer_size : 15052
period_size : 940
period_time : 21333
tstamp_mode : NONE
period_step : 1
avail_min : 940
period_event : 0
start_threshold : 1
stop_threshold : 15052
silence_threshold: 0
silence_size : 0
boundary : 986447872
Slave: Direct Stream Mixing PCM
Its setup is:
stream : PLAYBACK
access : MMAP_INTERLEAVED
format : S16_LE
subformat : STD
channels : 2
rate : 48000
exact rate : 48000 (48000/1)
msbits : 16
buffer_size : 16384
period_size : 1024
period_time : 21333
tstamp_mode : NONE
period_step : 1
avail_min : 1024
period_event : 0
start_threshold : 1
stop_threshold : 16384
silence_threshold: 0
silence_size : 0
boundary : 1073741824
Hardware PCM card 0 'Ensoniq AudioPCI' device 0 subdevice 0
Its setup is:
stream : PLAYBACK
access : MMAP_INTERLEAVED
format : S16_LE
subformat : STD
channels : 2
rate : 48000
exact rate : 48000 (1572864000/32768)
msbits : 16
buffer_size : 16384
period_size : 1024
period_time : 21333
tstamp_mode : ENABLE
period_step : 1
avail_min : 1024
period_event : 0
start_threshold : 1
stop_threshold : 1073741824
silence_threshold: 0
silence_size : 1073741824
boundary : 1073741824
appl_ptr : 0
hw_ptr : 1024
这篇文章,只是照猫画虎,把ALSA在pc上用起来,实现播放.wav的功能,对其原理没做解释。后面我会学习ALSA声卡驱动,并把alsa-libs移植到开发板上,那时再写点笔记分享出来,供大家参考。
1、sudo tar xjf alsa-lib-1.0.22.tar.bz2
2、cd alsa-lib-1.0.22/
3、./configure
4、make
5、sudo make install
这就把alsa-libs库安装好了。
下面是一个使用alsa库播放.wav文件的例子main.c:
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<stdio.h>
#include<linux/soundcard.h>
#include<alsa/asoundlib.h>
#include <sched.h>
#include <sys/time.h>
#include <math.h>
#include <malloc.h>
#include <string.h>
#include <getopt.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <time.h>
#include <locale.h>
#include <sys/unistd.h>
#include <assert.h>
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef long long off64_t;
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define COMPOSE_ID(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
#define LE_SHORT(v) (v)
#define LE_INT(v) (v)
#define BE_SHORT(v) bswap_16(v)
#define BE_INT(v) bswap_32(v)
#elif __BYTE_ORDER == __BIG_ENDIAN
#define COMPOSE_ID(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24))
#define LE_SHORT(v) bswap_16(v)
#define LE_INT(v) bswap_32(v)
#define BE_SHORT(v) (v)
#define BE_INT(v) (v)
#else
#error "Wrong endian"
#endif
#define WAV_RIFF COMPOSE_ID('R','I','F','F')
#define WAV_WAVE COMPOSE_ID('W','A','V','E')
#define WAV_FMT COMPOSE_ID('f','m','t',' ')
#define WAV_DATA COMPOSE_ID('d','a','t','a')
/* WAVE fmt block constants from Microsoft mmreg.h header */
#define WAV_FMT_PCM 0x0001
#define WAV_FMT_IEEE_FLOAT 0x0003
#define WAV_FMT_DOLBY_AC3_SPDIF 0x0092
#define WAV_FMT_EXTENSIBLE 0xfffe
/* Used with WAV_FMT_EXTENSIBLE format */
#define WAV_GUID_TAG "/x00/x00/x00/x00/x10/x00/x80/x00/x00/xAA/x00/x38/x9B/x71"
typedef struct WAVChunkHeader {
uint32_t type;
uint32_t length;
} WAVChunkHeader_t;
typedef struct SNDPCMContainer {
snd_pcm_t *handle;
snd_output_t *log;
snd_pcm_uframes_t chunk_size;
snd_pcm_uframes_t buffer_size;
snd_pcm_format_t format;
uint16_t channels;
size_t chunk_bytes;
size_t bits_per_sample;
size_t bits_per_frame;
uint8_t *data_buf;
} SNDPCMContainer_t;
typedef struct WAVHeader {
uint32_t magic;
uint32_t length;
uint32_t type;
} WAVHeader_t;
typedef struct WAVFmt {
uint32_t magic;
uint32_t fmt_size;
uint16_t format;
uint16_t channels;
uint32_t sample_rate;
uint32_t bytes_p_second;
uint16_t blocks_align;
uint16_t sample_length;
} WAVFmt_t;
typedef struct WAVContainer {
WAVHeader_t header;
WAVFmt_t format;
WAVChunkHeader_t chunk;
} WAVContainer_t;
int WAV_P_CheckValid(WAVContainer_t *container);
int WAV_ReadHeader(int fd,WAVContainer_t *container);
void SNDWAV_Play(SNDPCMContainer_t *sndpcm,WAVContainer_t *wav,int fd);
ssize_t SNDWAV_WritePcm(SNDPCMContainer_t *sndpcm, size_t wcount);
ssize_t SNDWAV_P_SaveRead(int fd, void *buf, size_t count);
int SNDWAV_SetParams(SNDPCMContainer_t *sndpcm,WAVContainer_t *wav);
int main(int argc,char *argv[])
{
char *filename;
char *devicename = "default";
int fd;
WAVContainer_t wav;
SNDPCMContainer_t playback;
if(argc != 2){
fprintf(stderr,"Usage:./play <FILENAME>\n");
return -1;
}
memset(&playback,0x0,sizeof(playback));
filename = argv[1];
fd = open(filename,O_RDONLY); //打开wav格式的音频文件
if(fd < 0){
fprintf(stderr,"Error WAV_Parse[%s]\n",filename);
goto Err;
}
if(WAV_ReadHeader(fd,&wav) < 0){//读取音频文件属性
fprintf(stderr,"Error WAV_Parse [%s]\n",filename);
goto Err;
}
//创建输出对象
if (snd_output_stdio_attach(&playback.log,stderr,0) < 0){
fprintf(stderr,"Error snd_output_stdio_attach\n");
goto Err;
}
//打开PCM设备
if (snd_pcm_open(&playback.handle,devicename,SND_PCM_STREAM_PLAYBACK,0) < 0){
fprintf(stderr,"Error snd_pcm_open[%s]\n",devicename);
goto Err;
}
snd_pcm_dump(playback.handle,playback.log);
//设置参数
if (SNDWAV_SetParams(&playback,&wav) < 0){
fprintf(stderr,"Error set_snd_pcm_params\n");
goto Err;
}
snd_pcm_dump(playback.handle,playback.log);
//播放
SNDWAV_Play(&playback,&wav,fd);
snd_pcm_drain(playback.handle);
close(fd);
free(playback.data_buf);
snd_output_close(playback.log);
snd_pcm_close(playback.handle);
return 0;
Err:
close(fd);
if (playback.data_buf) free(playback.data_buf);
if (playback.log) snd_output_close(playback.log);
if (playback.handle) snd_pcm_close(playback.handle);
return -1;
}
int WAV_ReadHeader(int fd,WAVContainer_t *container)
{
assert((fd >= 0) && container);
if (read(fd,&container->header,sizeof(container->header)) != sizeof(container->header) ||
read(fd,&container->format,sizeof(container->format)) != sizeof(container->format) ||
read(fd,&container->chunk,sizeof(container->chunk)) != sizeof(container->chunk)){
fprintf(stderr,"Error WAV_ReadHeader\n");
return -1;
}
if (WAV_P_CheckValid(container) < 0)
return -1;
#ifdef WAV_PRINT_MSG
WAV_P_PrintHeader(container);
#endif
return 0;
}
int SNDWAV_SetParams(SNDPCMContainer_t *sndpcm,WAVContainer_t *wav)
{
snd_pcm_hw_params_t *hwparams;
snd_pcm_format_t format;
uint32_t exact_rate;
uint32_t buffer_time,period_time;
/*Allocate the snd_pcm_hw)params_t structrue on the stack.*/
snd_pcm_hw_params_alloca(&hwparams);
/*Init hwparams with full configuration space*/
if (snd_pcm_hw_params_any(sndpcm->handle,hwparams) < 0){
fprintf(stderr,"Error snd_pcm_hw_params_any\n");
goto ERR_SET_PARAMS;
}
if (snd_pcm_hw_params_set_access(sndpcm->handle,hwparams,
SND_PCM_ACCESS_RW_INTERLEAVED) < 0){
fprintf(stderr,"Error snd_pcm_hw_params_set_access\n");
goto ERR_SET_PARAMS;
}
/* set sample format */
if (SNDWAV_P_GetFormat(wav,&format) < 0){
fprintf(stderr,"Error get_snd_pcm_format\n");
goto ERR_SET_PARAMS;
}
if (snd_pcm_hw_params_set_format(sndpcm->handle,hwparams,format) < 0) {
fprintf(stderr,"Error snd_pcm_hw_params_set_format\n");
goto ERR_SET_PARAMS;
}
sndpcm->format = format;
/* Set numbeof channels */
if (snd_pcm_hw_params_set_channels(sndpcm->handle,hwparams,
LE_SHORT(wav->format.channels)) < 0) {
fprintf(stderr,"Error snd_pcm_hw_params_set_channels\n");
goto ERR_SET_PARAMS;
}
sndpcm->channels = LE_SHORT(wav->format.channels);
/* SEt sample rate.If the exact rate is not supported */
/* by the hardware,use nearest possible rate. */
exact_rate = LE_INT(wav->format.sample_rate);
if (snd_pcm_hw_params_set_rate_near(sndpcm->handle,hwparams,
&exact_rate, 0) < 0){
fprintf(stderr,"Error snd_pcm_hw_params_set_rate_near\n");
goto ERR_SET_PARAMS;
}
if (LE_INT(wav->format.sample_rate) != exact_rate){
fprintf(stderr,"The rate %d Hz is not supported by your hardware.\n \
==>Using %d Hz instead.\n",LE_INT(wav->format.sample_rate),exact_rate);
}
if (snd_pcm_hw_params_get_buffer_time_max(hwparams,&buffer_time, 0) < 0){
fprintf(stderr,"Error snd_pcm_hw_params_get_buffer_time_max\n");
goto ERR_SET_PARAMS;
}
if (buffer_time > 500000)
buffer_time = 500000;
period_time = buffer_time / 4;
if (snd_pcm_hw_params_set_buffer_time_near(sndpcm->handle,hwparams,
&buffer_time,0) < 0){
fprintf(stderr,"Error snd_pcm_hw_params_set_buffer_time_near\n");
goto ERR_SET_PARAMS;
}
if (snd_pcm_hw_params_set_period_time_near(sndpcm->handle,hwparams,
&period_time,0) < 0){
fprintf(stderr,"Error snd_pcm_hw_params_set_period_time_near\n");
goto ERR_SET_PARAMS;
}
/* Set hw params */
if (snd_pcm_hw_params(sndpcm->handle,hwparams) < 0){
fprintf(stderr,"Error snd_pcm_hw_params(handle,params)\n");
goto ERR_SET_PARAMS;
}
snd_pcm_hw_params_get_period_size(hwparams,&sndpcm->chunk_size,0);
snd_pcm_hw_params_get_buffer_size(hwparams,&sndpcm->buffer_size);
if (sndpcm->chunk_size == sndpcm->buffer_size){
fprintf(stderr,("Can't use period equal to buffer size (%lu == %lu)\n"),
sndpcm->chunk_size,sndpcm->buffer_size);
goto ERR_SET_PARAMS;
}
sndpcm->bits_per_sample = snd_pcm_format_physical_width(format);
sndpcm->bits_per_frame = sndpcm->bits_per_sample *
LE_SHORT(wav->format.channels);
sndpcm->chunk_bytes = sndpcm->chunk_size * sndpcm->bits_per_frame / 8;
/* Allocate audio data buffer */
sndpcm->data_buf = (uint8_t *)malloc(sndpcm->chunk_bytes);
if (!sndpcm->data_buf){
fprintf(stderr,"Error malloc:[data_buf]\n");
goto ERR_SET_PARAMS;
}
return 0;
ERR_SET_PARAMS:
return -1;
}
void SNDWAV_Play(SNDPCMContainer_t *sndpcm,WAVContainer_t *wav,int fd)
{
int load,ret;
off64_t written = 0;
off64_t c;
off64_t conut = LE_INT(wav->chunk.length);
load = 0;
while (written < conut){
/* Must read [chunk_bytes] bytes data enough. */
do {
c = conut - written;
if (c > sndpcm->chunk_bytes)
c = sndpcm->chunk_bytes;
c -= load;
if (c == 0)
break;
ret = SNDWAV_P_SaveRead(fd,sndpcm->data_buf + load,c);
if (ret < 0){
fprintf(stderr,"Error safe read\n");
exit(-1);
}
if (ret == 0)
break;
load += ret;
}while ((size_t)load < sndpcm->chunk_bytes);
/* Transfer to size frame */
load = load * 8 / sndpcm->bits_per_frame;
ret = SNDWAV_WritePcm(sndpcm,load);
if (ret != load)
break;
ret = ret * sndpcm->bits_per_frame / 8;
written += ret;
load = 0;
}
}
int WAV_P_CheckValid(WAVContainer_t *container)
{
if (container->header.magic != WAV_RIFF ||
container->header.type != WAV_WAVE ||
container->format.magic != WAV_FMT ||
container->format.fmt_size != LE_INT(16) ||
(container->format.channels != LE_SHORT(1) && container->format.channels != LE_SHORT(2))
|| container->chunk.type != WAV_DATA) {
fprintf(stderr, "non standard wav file./n");
return -1;
}
return 0;
}
int SNDWAV_P_GetFormat(WAVContainer_t *wav, snd_pcm_format_t *snd_format)
{
if (LE_SHORT(wav->format.format) != WAV_FMT_PCM)
return -1;
switch (LE_SHORT(wav->format.sample_length)) {
case 16:
*snd_format = SND_PCM_FORMAT_S16_LE;
break;
case 8:
*snd_format = SND_PCM_FORMAT_U8;
break;
default:
*snd_format = SND_PCM_FORMAT_UNKNOWN;
break;
}
return 0;
}
ssize_t SNDWAV_WritePcm(SNDPCMContainer_t *sndpcm, size_t wcount)
{
ssize_t r;
ssize_t result = 0;
uint8_t *data = sndpcm->data_buf;
if (wcount < sndpcm->chunk_size) {
snd_pcm_format_set_silence(sndpcm->format,
data + wcount * sndpcm->bits_per_frame / 8,
(sndpcm->chunk_size - wcount) * sndpcm->channels);
wcount = sndpcm->chunk_size;
}
while (wcount > 0) {
r = snd_pcm_writei(sndpcm->handle, data, wcount);
if (r == -EAGAIN || (r >= 0 && (size_t)r < wcount)) {
snd_pcm_wait(sndpcm->handle, 1000);
}
else if (r == -EPIPE) {
snd_pcm_prepare(sndpcm->handle);
fprintf(stderr, "<<<<<<<<<<<<<<< Buffer Underrun >>>>>>>>>>>>>>>/n");
}
else if (r == -ESTRPIPE) {
fprintf(stderr, "<<<<<<<<<<<<<<< Need suspend >>>>>>>>>>>>>>>/n");
}
else if (r < 0) {
fprintf(stderr, "Error snd_pcm_writei: [%s]", snd_strerror(r));
exit(-1);
}
if (r > 0) {
result += r;
wcount -= r;
data += r * sndpcm->bits_per_frame / 8;
}
}
return result;
}
ssize_t SNDWAV_P_SaveRead(int fd, void *buf, size_t count)
{
ssize_t result = 0, res;
while (count > 0) {
if ((res = read(fd, buf, count)) == 0)
break;
if (res < 0)
return result > 0 ? result : res;
count -= res;
result += res;
buf = (char *)buf + res;
}
return result;
}
编译的时候后一定要加上编译选项 -lasound
gcc -o main.out main.c -lasound
如不加上 -lasound 则会输出类似如下编译问题:
/tmp/ccAG5teI.o: In function `main':
main.c:(.text+0x10a): undefined reference to `snd_output_stdio_attach'
main.c:(.text+0x15f): undefined reference to `snd_pcm_open'
main.c:(.text+0x192): undefined reference to `snd_pcm_dump'
main.c:(.text+0x1e8): undefined reference to `snd_pcm_dump'
main.c:(.text+0x210): undefined reference to `snd_pcm_drain'
测试,要加上sudo,否则可能执行不成功。
sudo ./main.out cq.wav
这样就可以播放.wav音乐了。
播放时输出的设备信息和声音文件头信息:
book@book-desktop:~/workspace/alsa$ sudo ./main.out cq.wav
Plug PCM: Direct Stream Mixing PCM
Hardware PCM card 0 'Ensoniq AudioPCI' device 0 subdevice 0
Its setup is:
stream : PLAYBACK
access : MMAP_INTERLEAVED
format : S16_LE
subformat : STD
channels : 2
rate : 48000
exact rate : 48000 (1572864000/32768)
msbits : 16
buffer_size : 16384
period_size : 1024
period_time : 21333
tstamp_mode : ENABLE
period_step : 1
avail_min : 1024
period_event : 0
start_threshold : 1
stop_threshold : 1073741824
silence_threshold: 0
silence_size : 1073741824
boundary : 1073741824
appl_ptr : 0
hw_ptr : 1024
Invalid rate plugin version 10002
Plug PCM: Rate conversion PCM (48000, sformat=S16_LE)
Protocol version: 10001
Its setup is:
stream : PLAYBACK
access : RW_INTERLEAVED
format : S16_LE
subformat : STD
channels : 2
rate : 44100
exact rate : 44100 (44100/1)
msbits : 16
buffer_size : 15052
period_size : 940
period_time : 21333
tstamp_mode : NONE
period_step : 1
avail_min : 940
period_event : 0
start_threshold : 1
stop_threshold : 15052
silence_threshold: 0
silence_size : 0
boundary : 986447872
Slave: Direct Stream Mixing PCM
Its setup is:
stream : PLAYBACK
access : MMAP_INTERLEAVED
format : S16_LE
subformat : STD
channels : 2
rate : 48000
exact rate : 48000 (48000/1)
msbits : 16
buffer_size : 16384
period_size : 1024
period_time : 21333
tstamp_mode : NONE
period_step : 1
avail_min : 1024
period_event : 0
start_threshold : 1
stop_threshold : 16384
silence_threshold: 0
silence_size : 0
boundary : 1073741824
Hardware PCM card 0 'Ensoniq AudioPCI' device 0 subdevice 0
Its setup is:
stream : PLAYBACK
access : MMAP_INTERLEAVED
format : S16_LE
subformat : STD
channels : 2
rate : 48000
exact rate : 48000 (1572864000/32768)
msbits : 16
buffer_size : 16384
period_size : 1024
period_time : 21333
tstamp_mode : ENABLE
period_step : 1
avail_min : 1024
period_event : 0
start_threshold : 1
stop_threshold : 1073741824
silence_threshold: 0
silence_size : 1073741824
boundary : 1073741824
appl_ptr : 0
hw_ptr : 1024
这篇文章,只是照猫画虎,把ALSA在pc上用起来,实现播放.wav的功能,对其原理没做解释。后面我会学习ALSA声卡驱动,并把alsa-libs移植到开发板上,那时再写点笔记分享出来,供大家参考。