ALSA是linux的编译框架,linux 2.6 版本发后支持
只要查看/dev/snd这个目录下面存在就可以使用
1、查看当前alsa版本
cat /proc/asound/version
alsa_sound.c
#define ALSA_PCM_NEW_HW_PARAMS_API
#include
#include
#include
struct fhead
{
/****RIFF WAVE CHUNK*/
unsigned char a[4];//四个字节存放'R','I','F','F'
long int b; //整个文件的长度-8;每个Chunk的size字段,都是表示除了本Chunk的ID和SIZE字段外的长度;
unsigned char c[4];//四个字节存放'W','A','V','E'
/****RIFF WAVE CHUNK*/
/****Format CHUNK*/
unsigned char d[4];//四个字节存放'f','m','t',''
long int e; //16后没有附加消息,18后有附加消息;一般为16,其他格式转来的话为18
short int f; //编码方式,一般为0x0001;
short int g; //声道数目,1单声道,2双声道;
long int h; //采样频率;
long int i; //每秒所需字节数;
short int j; //每个采样需要多少字节,若声道是双,则两个一起考虑;
short int k; //即量化位数
/****Format CHUNK*/
/***Data Chunk**/
unsigned char p[4];//四个字节存放'd','a','t','a'
long int q; //语音数据部分长度,不包括文件头的任何部分
} wavehead; //定义WAVE文件的文件头结构体
long LENGTH = 3; //录音时间,秒
int RATE=9600; //采样频率
char SIZE=16; //量化位数
char CHANNELS=1; //声道数目
int RSIZE=8; //buf的大小,
int alsa_record(char *voicename,long t,int rate, char size,char ch_num,int bufsize)
{
long loops;
int rc;
int size;
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
unsigned int val;
int dir;
snd_pcm_uframes_t frames;
char *buffer;
int fd_f;
int status;
LENGTH = t; //录音时间,秒
RATE = rate; //采样频率
SIZE = size; //量化位数
CHANNELS = ch_num; //声道数目
RSIZE = bufsize; //buf的大小,
/*以下wave 文件头赋值*/
wavehead.a[0] = 'R';
wavehead.a[1] = 'I';
wavehead.a[2] = 'F';
wavehead.a[3] = 'F';
wavehead.b = LENGTH * RATE * CHANNELS * SIZE / 8 - 8;
wavehead.c[0] = 'W';
wavehead.c[1] = 'A';
wavehead.c[2] = 'V';
wavehead.c[3] = 'E';
wavehead.d[0] = 'f';
wavehead.d[1] = 'm';
wavehead.d[2] = 't';
wavehead.d[3] = ' ';
wavehead.e = 16;
wavehead.f = 1;
wavehead.g = CHANNELS;
wavehead.h = RATE;
wavehead.i = RATE * CHANNELS * SIZE / 8;
wavehead.j = CHANNELS * SIZE / 8;
wavehead.k = SIZE;
wavehead.p[0] = 'd';
wavehead.p[1] = 'a';
wavehead.p[2] = 't';
wavehead.p[3] = 'a';
wavehead.q = LENGTH * RATE * CHANNELS * SIZE / 8;
/*以上wave 文件头赋值*/
/* Open PCM device for recording (capture). */
rc = snd_pcm_open(&handle, "default",
SND_PCM_STREAM_CAPTURE, 0);
if (rc < 0)
{
fprintf(stderr,
"unable to open pcm device: %s\n",
snd_strerror(rc));
exit(1);
}
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(¶ms);
/* Fill it in with default values. */
snd_pcm_hw_params_any(handle, params);
/* Set the desired hardware parameters. */
/* Interleaved mode */
snd_pcm_hw_params_set_access(handle, params,
SND_PCM_ACCESS_RW_INTERLEAVED);
/* Signed 16-bit little-endian format */
snd_pcm_hw_params_set_format(handle, params,
SND_PCM_FORMAT_S16_LE);
/* Two channels (stereo) */
snd_pcm_hw_params_set_channels(handle, params, CHANNELS);
/* 44100 bits/second sampling rate (CD quality) */
val = RATE;
snd_pcm_hw_params_set_rate_near(handle, params,
&val, &dir);
/* Set period size to 32 frames. */
frames = 32;
snd_pcm_hw_params_set_period_size_near(handle,
params, &frames, &dir);
/* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0)
{
fprintf(stderr,
"unable to set hw parameters: %s\n",
snd_strerror(rc));
exit(1);
}
/* Use a buffer large enough to hold one period */
snd_pcm_hw_params_get_period_size(params,
&frames, &dir);
size = frames * 2; /* 2 bytes/sample, 2 channels */
buffer = (char *) malloc(size);
/* We want to loop for 5 seconds */
snd_pcm_hw_params_get_period_time(params,
&val, &dir);
loops = 5000000 / val;
if(( fd_f = open("voicename", O_CREAT | O_RDWR, 0777)) == -1) //创建一个wave格式语音文件
{
perror("cannot creat the sound file");
}
if((status = write(fd_f, &wavehead, sizeof(wavehead))) == -1) //写入wave文件的文件头
{
perror("write to sound'head wrong!!");
}
while (loops > 0)
{
loops--;
rc = snd_pcm_readi(handle, buffer, frames);
if (rc == -EPIPE)
{
/* EPIPE means overrun */
fprintf(stderr, "overrun occurred\n");
snd_pcm_prepare(handle);
}
else if (rc < 0)
{
fprintf(stderr,
"error from read: %s\n",
snd_strerror(rc));
}
else if (rc != (int)frames)
{
fprintf(stderr, "short read, read %d frames\n", rc);
}
if(write(fd_f, buffer, size) == -1)
{
perror("write to sound wrong!!");
}
if (rc != size)
fprintf(stderr,
"short write: wrote %d bytes\n", rc);
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
free(buffer);
close(fd_f);
return 0;
return 0;
}
int alsa_play(char *wav_name,unsigned int val,char chs)
{
int i;
int ret;
int buf[128];
int dir=0;
char *buffer;
int size;
snd_pcm_uframes_t frames;
snd_pcm_uframes_t periodsize;
snd_pcm_t *playback_handle;//PCM设备句柄pcm.h
snd_pcm_hw_params_t *hw_params;//硬件信息和PCM流配置
FILE *fp = fopen(wav_name, "rb");
if(fp == NULL)
return -1;
fseek(fp, 100, SEEK_SET);
//1. 打开PCM,最后一个参数为0意味着标准配置
ret = snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (ret < 0) {
perror("snd_pcm_open");
exit(1);
}
//2. 分配snd_pcm_hw_params_t结构体
ret = snd_pcm_hw_params_malloc(&hw_params);
if (ret < 0) {
perror("snd_pcm_hw_params_malloc");
exit(1);
}
//3. 初始化hw_params
ret = snd_pcm_hw_params_any(playback_handle, hw_params);
if (ret < 0) {
perror("snd_pcm_hw_params_any");
exit(1);
}
//4. 初始化访问权限
ret = snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
if (ret < 0) {
perror("snd_pcm_hw_params_set_access");
exit(1);
}
//5. 初始化采样格式SND_PCM_FORMAT_U8,8位
ret = snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_S16_LE);
if (ret < 0)
{
perror("snd_pcm_hw_params_set_format");
exit(1);
}
//6. 设置采样率,如果硬件不支持我们设置的采样率,将使用最接近的
//val = 441000;//,有些录音采样频率固定为8KHz
//val = 128000;
ret = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &val, &dir);
if (ret < 0)
{
perror("snd_pcm_hw_params_set_rate_near");
exit(1);
}
//7. 设置通道数量
ret = snd_pcm_hw_params_set_channels(playback_handle, hw_params, chs);
if (ret < 0)
{
perror("snd_pcm_hw_params_set_channels");
exit(1);
}
/* Set period size to 32 frames. */
frames = 32;
periodsize = frames ;
ret = snd_pcm_hw_params_set_buffer_size_near(playback_handle, hw_params, &periodsize);
if (ret < 0)
{
printf("Unable to set buffer size %li : %s\n", frames * 2, snd_strerror(ret));
}
periodsize /= 2;
ret = snd_pcm_hw_params_set_period_size_near(playback_handle, hw_params, &periodsize, 0);
if (ret < 0)
{
printf("Unable to set period size %li : %s\n", periodsize, snd_strerror(ret));
}
//8. 设置hw_params
ret = snd_pcm_hw_params(playback_handle, hw_params);
if (ret < 0) {
perror("snd_pcm_hw_params");
exit(1);
}
/* Use a buffer large enough to hold one period */
snd_pcm_hw_params_get_period_size(hw_params, &frames, &dir);
size = frames * 2; /* 2 bytes/sample, 2 channels */
buffer = (char *) malloc(size);
fprintf(stderr,
"size = %d\n",
size);
while (1)
{
ret = fread(buffer, 1, size, fp);
if(ret == 0)
{
fprintf(stderr, "end of file on input\n");
break;
}
else if (ret != size)
{
}
//9. 写音频数据到PCM设备
while(ret = snd_pcm_writei(playback_handle, buffer, frames)<0)
{
usleep(2000);
if (ret == -EPIPE)
{
/* EPIPE means underrun */
fprintf(stderr, "underrun occurred\n");
//完成硬件参数设置,使设备准备好
snd_pcm_prepare(playback_handle);
}
else if (ret < 0)
{
fprintf(stderr,
"error from writei: %s\n",
snd_strerror(ret));
}
}
}
//10. 关闭PCM设备句柄
snd_pcm_close(playback_handle);
return 0;
}
main.c
#include
#include
#include "alsa_sound.h"
int main()
{
printf("开始录音\n");
alsa_record("./test.wav", 10, 9600, 16, 1, 8);
printf("播放录音\n");
alsa_play("test.wad",128000,1);
return 0;
}
编译 gcc main.c alsa_sound.c -o main -lasound -lm -ldl