alsa-libs安装与ALSA应用编程

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库安装好了。

下面是一个使用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移植到开发板上,那时再写点笔记分享出来,供大家参考。


猜你喜欢

转载自blog.csdn.net/qq_22863733/article/details/80174407