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请大家自己查下怎么改。