Linux的基于alsa的wav音频播放器

1.导读
c语言实现alsa播放
c语言实现ALSA录音

用alsa录音并ffmpeg推流RTMP
ffmpeg拉音频流并用alsa播放

2.源码

#include <alsa/asoundlib.h>
#include <stdio.h>
#include <termios.h>
#include <term.h>
#include <curses.h>
#include <unistd.h>
#include <stdlib.h>
#include <math.h>

static struct termios initial_settings, new_settings;
static int peek_character = -1;
void init_keyboard();
void close_keyboard();
int kbhit();
int readch();

static char *device = "hw:1,0";			/* playback device */
static snd_pcm_format_t format = SND_PCM_FORMAT_S16;	/* sample format */
static unsigned int rate = 44100;			/* stream rate */
static unsigned int channels = 2;			/* count of channels */
static unsigned int buffer_time = 500000;		/* ring buffer length in us */
static unsigned int period_time = 100000;		/* period time in us */
static int resample = 1;				/* enable alsa-lib resampling */
static snd_pcm_sframes_t buffer_size;
static snd_pcm_sframes_t period_size;
snd_pcm_access_t mode = SND_PCM_ACCESS_RW_INTERLEAVED;
static snd_output_t *output = NULL;
static int set_hwparams(snd_pcm_t *handle,snd_pcm_hw_params_t *params,snd_pcm_access_t access);
FILE * open_and_print_file_params(char *file_name);


/*配置参数*/
static int set_hwparams(snd_pcm_t *handle,snd_pcm_hw_params_t *params,snd_pcm_access_t access)
{
	unsigned int rrate;
	snd_pcm_uframes_t size;
	int err, dir;

	/* choose all parameters */
	err = snd_pcm_hw_params_any(handle, params);
	if (err < 0) {
		printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
		return err;
	}
	/* set hardware resampling */
	err = snd_pcm_hw_params_set_rate_resample(handle, params, resample);
	if (err < 0) {
		printf("Resampling setup failed for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the interleaved read/write format */
	/*访问格式*/
	err = snd_pcm_hw_params_set_access(handle, params, mode);
	if (err < 0) {
		printf("Access type not available for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the sample format */
	/*采样格式*/
	err = snd_pcm_hw_params_set_format(handle, params, format);
	if (err < 0) {
		printf("Sample format not available for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the count of channels */
	/*音频声道*/
	err = snd_pcm_hw_params_set_channels(handle, params, channels);
	if (err < 0) {
		printf("Channels count (%u) not available for playbacks: %s\n", channels, snd_strerror(err));
		return err;
	}

	/* set the stream rate */
	/*采样率*/
	rrate = rate;
	err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
	if (err < 0) {
		printf("Rate %uHz not available for playback: %s\n", rate, snd_strerror(err));
		return err;
	}
	if (rrate != rate) {
		printf("Rate doesn't match (requested %uHz, get %iHz)\n", rate, err);
		return -EINVAL;
	}

	/* set the buffer time */
	/*最大缓冲时间,500000=0.5s*/
	err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
	if (err < 0) {
		printf("Unable to set buffer time %u for playback: %s\n", buffer_time, snd_strerror(err));
		return err;
	}
	err = snd_pcm_hw_params_get_buffer_size(params, &size);
	if (err < 0) {
		printf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
		return err;
	}

	buffer_size = size;
	printf("buffer_size=%ld\n",buffer_size);
	/* set the period time */
	/*每次采样周期,100000=0.1s*/
	err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
	if (err < 0) {
		printf("Unable to set period time %u for playback: %s\n", period_time, snd_strerror(err));
		return err;
	}
	/*每次采样周期点数,44100*0.1=4410*/
	err = snd_pcm_hw_params_get_period_size(params, &size, &dir);
	if (err < 0) {
		printf("Unable to get period size for playback: %s\n", snd_strerror(err));
		return err;
	}
	period_size = size;
	printf("period_size=%ld\n",period_size);
	/* write the parameters to device */
	err = snd_pcm_hw_params(handle, params);
	if (err < 0) {
		printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
		return err;
	}
	return 0;
}

#define u32 unsigned int
#define u8  unsigned char
#define u16 unsigned short
/*wav文件参数*/
typedef  struct	{
	u32 	dwSize;
	u16		wFormatTag;
	u16		wChannels;
	u32 	dwSamplesPerSec;
	u32 	dwAvgBytesPerSec;
	u16		wBlockAlign;
	u16		wBitsPerSample;
} WAVEFORMAT;

/*wav文件头格式*/
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;

WAVE_HEADER g_wave_header;

/*读取wav文件头格式*/
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;
}

/*初始化键盘*/
void init_keyboard()
{
    tcgetattr(0,&initial_settings);
    new_settings = initial_settings;
    new_settings.c_lflag &= ~ICANON;
    new_settings.c_lflag &= ~ECHO;
    new_settings.c_lflag &= ~ISIG;
    new_settings.c_cc[VMIN] = 1;
    new_settings.c_cc[VTIME] = 0;
    tcsetattr(0, TCSANOW, &new_settings);
}

/*关闭键盘*/
void close_keyboard()
{
    tcsetattr(0, TCSANOW, &initial_settings);
}

/*检测按键*/
int kbhit()
{
    char ch;
    int nread;
    if(peek_character != -1)
        return 1;
    new_settings.c_cc[VMIN]=0;
    tcsetattr(0, TCSANOW, &new_settings);
    nread = read(0,&ch,1);
    new_settings.c_cc[VMIN]=1;
    tcsetattr(0, TCSANOW, &new_settings);
    if(nread == 1) 
	{
        peek_character = ch;
        return 1;
    }
    return 0;
}

/*读取按键对应ASCII*/
int readch()
{
    char ch;
    if(peek_character != -1) {
        ch = peek_character;
        peek_character = -1;
        return ch;
    }
    read(0,&ch,1);
    return ch;

}

/*音量db调节*/
void *vol_adjust (void *dst,float db,size_t count)
{

   void *start = dst;
   char tmp;
   
   while (count--) {
        tmp=(*(char *)dst)*pow(10,db/20);
		if(tmp>127)
        {
			*(char*)dst = 127 ;
		}
		else if(tmp<-128)
        {
			*(char*)dst = -128;
		}
		else
		{
			/* code */
			*(char*)dst = tmp;
		}
        dst = (char *)dst +1;
    }
   return(start);
}

int main()
{
	char *infile = "鄧紫棋 - 睡公主.wav" ;
    /*打开wav并获取和输出wav格式*/
	FILE *fp = open_and_print_file_params(infile);
	if (fp == NULL)
	{
		printf("open_and_print_file_params error\n");
		return -1;
	}

	/*设置采样率*/
	rate = g_wave_header.nSamplesPerSec;
	/*设置音频采样格式*/
    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;
		return -1; 
	}
    /*设置音频声道*/
    channels=g_wave_header.nChannels;
    /*计算音乐时间*/
    int music_sec=g_wave_header.nDataBytes/g_wave_header.nAvgBytesPerSec;
	printf("music_time:%d:%d\n",(int)music_sec/60,music_sec%60);

	snd_pcm_t *handle;
	snd_pcm_hw_params_t *hwparams;

	snd_pcm_hw_params_alloca(&hwparams);

	printf("Playback device is %s\n", device);
	printf("Stream parameters are %uHz, %s, %u channels\n", rate, snd_pcm_format_name(format), channels);
    
    int err;

    err = snd_output_stdio_attach(&output, stdout, 0);
	if (err < 0) {
		printf("Output failed: %s\n", snd_strerror(err));
		return 0;
	}
    /*设置播放模式*/
	err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0);
	if (err < 0) 
	{
		printf("Playback open error: %s\n", snd_strerror(err));
		return 0;
	}
    /*设置参数*/
    err = set_hwparams(handle, hwparams, mode);
	if (err < 0) {
		printf("Setting of hwparams failed: %s\n", snd_strerror(err));
		return 0;
	}
    
	//snd_pcm_dump(handle, output);

	short *samples;

	int size;
	size=(period_size * channels * snd_pcm_format_physical_width(format))/8;
	samples = (short *)malloc(size);
    
    int rc;
	char ch;
	init_keyboard();
	float vol=0;
	int BytesPerSec= g_wave_header.nAvgBytesPerSec;

    while (1) 
	{
        /*读取音频数据*/
		rc = fread(samples, sizeof(int), size, fp);
		if (rc == 0) {
			fprintf(stderr, "end of file on input\n");
			break;
		} 
		else if (rc != size) {
			fprintf(stderr,"short read: read %d bytes\n", rc);
		}
        /*音量调节*/
		vol_adjust(samples,vol,size*4);

		/*写入音频数据*/
		rc = snd_pcm_writei(handle, samples, size);
		
		if (rc == -EPIPE) {
			fprintf(stderr, "underrun occurred\n");
			err=snd_pcm_prepare(handle);
			if( err <0){
				fprintf(stderr, "can not recover from underrun: %s\n",snd_strerror(err));
			}
		
		} 
		else if (rc < 0) {
			fprintf(stderr,"error from writei: %s\n",snd_strerror(rc));
		}  
		else if (rc != (int)size) {
			fprintf(stderr,"short write, write %d frames\n", rc);
		}

        /*按键控制*/
		if(kbhit()) {
            ch = readch();
            if(ch=='w')
			{
                printf("音量+\n");
				vol=vol+2;
			}
			else if(ch=='s')
			{
                printf("音量-\n");
                vol=vol-2;
				
			}
			else if(ch=='d')
			{
                printf("快进1秒\n");
				fseek(fp, BytesPerSec , SEEK_CUR);
			}
			else if(ch=='a')
			{
                printf("后退1秒\n");
				fseek(fp, -(BytesPerSec) , SEEK_CUR);
			}
            else if(ch==27)
            {
                printf("退出播放\n");
				break;
            }
        }
        
	}

    close_keyboard();
	free(samples);
	snd_pcm_close(handle);
	
	return 0;
}

3.编译

gcc alsa_play_plus.c -o alsa_play_plus -lasound -lm

4.如果有bug
①键盘设备名称;修改键盘设备权限0777;
②音频设备名称;
③alsa环境;
④libcurse库;
这些bug请大家自己查下怎么改。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值