Linux alsa-lib c语言 播放wav音频

时间:2021-06-19 19:45:49
编程步骤:

1.指定使用最新的ALSA API
#define ALSA_PCM_NEW_HW_PARAMS_API  

2.包含头文件
#include <alsa/asoundlib.h>

3.定义变量
snd_pcm_t *handle;  //调用snd_pcm_open打开PCM设备返回的文件句柄,后续的操作都使用是、这个句柄操作这个PCM设备
snd_pcm_hw_params_t *params;  //设置流的硬件参数
snd_pcm_uframes_t frames;    //snd_pcm_uframes_t其实是unsigned long类型

4.打开PCM设备
使用snd_pcm_open函数
/* Open PCM device for playback */    
rc = snd_pcm_open(&handle, "defalut", SND_PCM_STREAM_PLAYBACK, 0);    
if (rc < 0)
{    
    printf("unable to open pcm device\n");  
    return -1;  
}

函数原型:
int snd_pcm_open(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);

参数说明:
pcm:即第三步声明的变量handle,用来接收返回的PCM文件句柄
name:可以是hw:0,0,或plughw:0,0
stream:可以是SND_PCM_STREAM_PLAYBACK  //Playback stream
    或SND_PCM_STREAM_CAPTURE  //Capture stream
mode:暂时不明白是什么意思

5.分配一个硬件参数对象
/* Allocate a hardware parameters object */    
snd_pcm_hw_params_alloca(&params);

6.向对象填充默认值
/* Fill it in with default values. */    
snd_pcm_hw_params_any(handle, params);

7.设置所需的硬件参数
7.1.设置交错模式
/* Interleaved mode */    
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);  
可以设置的模式如下:


7.2.设置格式
/* Signed 16-bit little-endian format */    
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);  

7.3设置通道
/* Two channels (stero) */    
snd_pcm_hw_params_set_channels(handle, params, 2);    
        
7.4设置采用率
/* 44100 bits/second sampling rate */    
val = 44100;    
snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);    

8.将上面设置的参数传给驱动,只有经过这步,上面设置的参数才有效
/* Write the parameters to the dirver */    
rc = snd_pcm_hw_params(handle, params);    
if (rc < 0) {    
    printf("unable to set hw parameters: %s\n", snd_strerror(rc));    
    exit(1);    
}

9.获取存储采用数据buffer的大小
int dir;  
snd_pcm_hw_params_get_period_size(params, &frames, &dir);    
size = frames * 4;    
buffer = (char *)malloc(size);

获取period time时间
snd_pcm_hw_params_get_period_time(params, &val, &dir);

10.将数据读入buffer
rc = read(0, buffer, size);

11.将buffer数据写入PCM设备
rc = snd_pcm_writei(handle, buffer, frames);    
if (rc == -EPIPE) {    
    printf("underrun occured\n");    
}    
else if (rc < 0) {    
    printf("error from writei: %s\n", snd_strerror(rc));    
}    

12.将handle冲刷干净,关闭流,释放buffer
snd_pcm_drain(handle);    
snd_pcm_close(handle);    

free(buffer);

以下是通过alsa-lib播放wav文件的代码

/*
*作者:韦访
*CSDN:https://blog.csdn.net/rookie_wei
**/
#define ALSA_PCM_NEW_HW_PARAMS_API

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alsa/asoundlib.h>

#define u32 unsigned int
#define u8   unsigned char
#define u16 unsigned short


#pragma pack(push) 
#pragma pack(1)     //1字节对齐

typedef  struct	{
	u32 	dwSize;
	u16		wFormatTag;
	u16		wChannels;
	u32 	dwSamplesPerSec;
	u32 	dwAvgBytesPerSec;
	u16		wBlockAlign;
	u16		wBitsPerSample;
} WAVEFORMAT;

typedef  struct	{
	u8    	RiffID [4];
	u32     RiffSize;
	u8    	WaveID[4];
	u8    	FmtID[4];
	u32     FmtSize;
	u16   	wFormatTag;
	u16   	nChannels;
	u32 	nSamplesPerSec;  /*采样频率*/
	u32 	nAvgBytesPerSec; /*每秒所需字节数*/
	u16		nBlockAlign; /*数据块对齐单位,每个采样需要的字节数*/
	u16		wBitsPerSample;/*每个采样需要的bit数*/
	u8		DataID[4];
	u32 	nDataBytes;
} WAVE_HEADER;

#pragma pack(pop) /* 恢复先前的pack设置 */

WAVE_HEADER g_wave_header;
snd_pcm_t *gp_handle;  //调用snd_pcm_open打开PCM设备返回的文件句柄,后续的操作都使用是、这个句柄操作这个PCM设备
snd_pcm_hw_params_t *gp_params;  //设置流的硬件参数
snd_pcm_uframes_t g_frames;    //snd_pcm_uframes_t其实是unsigned long类型
char *gp_buffer;
u32 g_bufsize;

FILE * open_and_print_file_params(char *file_name)
{
	FILE * fp = fopen(file_name, "r");
	if (fp == NULL)
	{
		printf("can't open wav file\n");
		return NULL;
	}
	

	memset(&g_wave_header, 0, sizeof(g_wave_header));
	fread(&g_wave_header, 1, sizeof(g_wave_header), fp);
	
	printf("RiffID:%c%c%c%c\n", g_wave_header.RiffID[0], g_wave_header.RiffID[1], g_wave_header.RiffID[2], g_wave_header.RiffID[3]);
	printf("RiffSize:%d\n", g_wave_header.RiffSize);
	printf("WaveID:%c%c%c%c\n", g_wave_header.WaveID[0], g_wave_header.WaveID[1], g_wave_header.WaveID[2], g_wave_header.WaveID[3]);
	printf("FmtID:%c%c%c%c\n", g_wave_header.FmtID[0], g_wave_header.FmtID[1], g_wave_header.FmtID[2], g_wave_header.FmtID[3]);
	printf("FmtSize:%d\n", g_wave_header.FmtSize);
	printf("wFormatTag:%d\n", g_wave_header.wFormatTag);
	printf("nChannels:%d\n", g_wave_header.nChannels);
	printf("nSamplesPerSec:%d\n", g_wave_header.nSamplesPerSec);
	printf("nAvgBytesPerSec:%d\n", g_wave_header.nAvgBytesPerSec);
	printf("nBlockAlign:%d\n", g_wave_header.nBlockAlign);
	printf("wBitsPerSample:%d\n", g_wave_header.wBitsPerSample);
	printf("DataID:%c%c%c%c\n", g_wave_header.DataID[0], g_wave_header.DataID[1], g_wave_header.DataID[2], g_wave_header.DataID[3]);
	printf("nDataBytes:%d\n", g_wave_header.nDataBytes);
	
	return fp;
}

int set_hardware_params()
{
	int rc;
	/* Open PCM device for playback */    
	rc = snd_pcm_open(&gp_handle, "hw:0,0", SND_PCM_STREAM_PLAYBACK, 0);    
	if (rc < 0) 
	{    
		printf("unable to open pcm device\n"); 
		return -1;   
	} 

	
	/* Allocate a hardware parameters object */    
	snd_pcm_hw_params_alloca(&gp_params); 
	
	/* Fill it in with default values. */    
	rc = snd_pcm_hw_params_any(gp_handle, gp_params);
	if (rc < 0) 
	{    
		printf("unable to Fill it in with default values.\n");    
		goto err1;  
	}
	

	/* Interleaved mode */    
	rc = snd_pcm_hw_params_set_access(gp_handle, gp_params, SND_PCM_ACCESS_RW_INTERLEAVED);
	if (rc < 0) 
	{    
		printf("unable to Interleaved mode.\n");    
		goto err1;  
	}
		
	snd_pcm_format_t format;
	
	if (8 == g_wave_header.FmtSize)
	{
		format = SND_PCM_FORMAT_U8;
	}
	else if (16 == g_wave_header.FmtSize)
	{
		format = SND_PCM_FORMAT_S16_LE;
	}
	else if (24 == g_wave_header.FmtSize)
	{
		format = SND_PCM_FORMAT_U24_LE;
	}
	else if (32 == g_wave_header.FmtSize)
	{
		format = SND_PCM_FORMAT_U32_LE;
	}
	else
	{
		printf("SND_PCM_FORMAT_UNKNOWN.\n");    
		format = SND_PCM_FORMAT_UNKNOWN;
		goto err1; 
	}
	
		
	/* set format */    
	rc = snd_pcm_hw_params_set_format(gp_handle, gp_params, format);
	if (rc < 0) 
	{    
		printf("unable to set format.\n");    
		goto err1;  
	}
	
	/* set channels (stero) */    
	snd_pcm_hw_params_set_channels(gp_handle, gp_params, g_wave_header.nChannels);
	if (rc < 0) 
	{    
		printf("unable to set channels (stero).\n");    
		goto err1;  
	}
	
	/* set sampling rate */       
    u32 dir, rate = g_wave_header.nSamplesPerSec;  
	rc = snd_pcm_hw_params_set_rate_near(gp_handle, gp_params, &rate, &dir); 
	if (rc < 0) 
	{    
		printf("unable to set sampling rate.\n");    
		goto err1;  
	} 
	
	/* Write the parameters to the dirver */    
	rc = snd_pcm_hw_params(gp_handle, gp_params);    
	if (rc < 0) {    
		printf("unable to set hw parameters: %s\n", snd_strerror(rc));    
		goto err1;  
	} 
	
	snd_pcm_hw_params_get_period_size(gp_params, &g_frames, &dir);
	g_bufsize = g_frames * 4;
	gp_buffer = (u8 *)malloc(g_bufsize);
	if (gp_buffer == NULL)
	{
		printf("malloc failed\n");
		goto err1;  
	}

	return 0;
	
err1:
	snd_pcm_close(gp_handle);
	return -1;	
}


int main(int argc, char *argv[])
{
	if (argc < 2)
	{
		printf("usage: %s filename.wav\n", argv[0]);
		return -1;
	}
	
	FILE * fp = open_and_print_file_params(argv[1]);
	int fd = open(argv[1], O_RDONLY);
	if (fp == NULL)
	{
		printf("open_and_print_file_params error\n");
		return -1;
	}
	
	int ret = set_hardware_params();
	if (ret < 0)
	{
		printf("set_hardware_params error\n");
		return -1;
	}
	
	size_t rc;
	while (1)
	{
		rc = fread(gp_buffer, g_bufsize, 1, fp);
		if (rc <1)
		{
			break;
		}
		
		ret = snd_pcm_writei(gp_handle, gp_buffer, g_frames);    
		if (ret == -EPIPE) {    
			printf("underrun occured\n");  
			break;  
		}    
		else if (ret < 0) {    
			printf("error from writei: %s\n", snd_strerror(ret));    
			break;
		} 
	}
	
	
	snd_pcm_drain(gp_handle);    
	snd_pcm_close(gp_handle);    
	free(gp_buffer); 
	fclose(fp);
	return 0;
}

编译命令:

gcc wavplayer.c -o wavplayer -lasound -ldl -lm