本文主要讨论在Ubuntu通过ALSA库录制wav音频文件干货共享,并附上以下编写过程和源代码,供感兴趣的学者耐心查阅。
1、安装ALSA库
sudoapt-get install alsa-base, alsa-utils, alsa-source,libasound2-dev
2、目标
本次讲的录音,就是通过ALSA的库函数,从声卡中读取pcm格式的音频文件,在文件头部添加一个wav的头,以此得到在Ubuntu能点击播放的wav文件。
3、基本概念
编程之前,先让我们各个参数的含义,和一些基本概念:
样本长度(sample):样本是记录音频数据最基本的单位,常见的有8位和16位。
通道数(channel):该参数为1表示单声道,2则是立体声。
桢(frame):桢记录了一个声音单元,其长度为样本长度与通道数的乘积。
采样率(rate):每秒钟采样次数,该次数是针对桢而言。
周期(period):音频设备一次处理所需要的桢数,对于音频设备的数据访问以及音频数据的存储,都是以此为单位。
数据存储模式:一般选择交错模式(interleaved),者是一种音频数据的记录方式,在交错模式下,数据以连续桢的形式存放,即首先记录完桢1的左声道样本和右声道样本(假设为立体声格式),再开始桢2的记录。而在非交错模式下,首先记录的是一个周期内所有桢的左声道样本,再记录右声道样本,数据是以连续通道的方式存储。不过多数情况下,我们只需要使用交错模式就可以了。
4、源代码
这里我们录制的音频以16位样本长度,单声道,16000采样率为例。
当我们打开一个音频句柄后,用
snd_pcm_hw_params_set_rate_near() 设置采样率
snd_pcm_hw_params_set_format()设置样本长度
snd_pcm_hw_params_set_channels()设置通道数
snd_pcm_hw_params_set_buffer_time_near()设置buffer_size的大小
snd_pcm_hw_params_set_period_time_near()设置period_size的大小
snd_pcm_hw_params_set_access()设置数据记录模式
话不多说,贴上代码:
#include <;
#include <alsa;
#include <;
#include <uni;
#include <;
/* wav音频头部格式 */
typedef struct _wave_pcm_hdr
{
char riff[4]; // = "RIFF"
int size_8; // = fileSize - 8
char wave[4]; // = "WAVE"
char fmt[4]; // = "fmt "
int fmt_size; // = 下一个结构体的大小 : 16
shortint format_tag; // = PCM : 1
shortint channels; // = 通道数 : 1
int samples_per_sec; // = 采样率: 8000 | 6000 | 11025 | 16000
int avg_bytes_per_sec; // = 每秒字节数: samples_per_sec * bits_per_sample / 8
shortint block_align; // = 每采样点字节数 : wBitsPerSample / 8
shortint bits_per_sample; // = 量化比特数:8 | 16
char data[4]; // = "data";
int data_size; // = 纯数据长度 : FileSize - 44
}wave_pcm_hdr;
/* 默认wav音频头部数据 */
wave_pcm_hdr default_wav_hdr =
{
{'R', 'I', 'F', 'F' },·
0,
{'W','A', 'V', 'E'},
{'f','m', 't', ' '},
16,
1,
1,
16000,
32000,
2,
16,
{'d','a', 't', 'a'},
0
};
/***************pcm operation****************************/
int setDeviceParam(snd_pcm_t*handle,snd_pcm_hw_params_t *params,snd_pcm_access_t access);
void audioRecord(const char* file);
const char *device ="default"; /* playback device */
const snd_pcm_format_t format =SND_PCM_FORMAT_S16; /* sample format*/
snd_pcm_access_t access_mode=SND_PCM_ACCESS_RW_INTERLEAVED;
static unsigned int rate = 16000; /* stream rate */
static unsigned int channels = 1; /* count of channels */
static unsigned int buffer_time =500*1000; /* ring bufferlength in us */
static unsigned int period_time =100*1000; /* period time inus */
static unsigned int resample = 1;
static snd_pcm_sframes_t buffer_size;
static snd_pcm_sframes_t period_size;
pthread_t rec_thread_id;
bool flag = true;
/***************pcm****************************/
int setDeviceParam(snd_pcm_t*handle,snd_pcm_hw_params_t *params,snd_pcm_access_t access){
unsignedint rrate;
snd_pcm_uframes_tsize;
interr, dir;
/*choose all parameters */
err= snd_pcm_hw_params_any(handle, params);
if(err < 0) {
printf("Brokenconfiguration for playback: no configurations available: %s\n",snd_strerror(err));
returnerr;
}
/*set hardware resampling */
err= snd_pcm_hw_params_set_rate_resample(handle, params, resample);
if(err < 0) {
printf("Resamplingsetup failed for playback: %s\n", snd_strerror(err));
returnerr;
}
/*set the interleaved read/write format */
err= snd_pcm_hw_params_set_access(handle, params, access);
if(err < 0) {
printf("Accesstype not available for playback: %s\n", snd_strerror(err));
returnerr;
}
/*set the sample format */
err= snd_pcm_hw_params_set_format(handle, params, format);
if(err < 0) {
printf("Sampleformat not available for playback: %s\n", snd_strerror(err));
returnerr;
}
/*set the count of channels */
err= snd_pcm_hw_params_set_channels(handle, params, channels);
if(err < 0) {
printf("Channelscount (%i) not available for playbacks: %s\n", channels,snd_strerror(err));
returnerr;
}
/*set the stream rate */
rrate= rate;
err= snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
if(err < 0) {
printf("Rate%iHz not available for playback: %s\n", rate, snd_strerror(err));
returnerr;
}
if(rrate != rate) {
printf("Ratedoesn't match (requested %iHz, get %iHz)\n", rate, err);
return-EINVAL;
}
/*set the buffer time */
err= snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time,&dir);
if(err < 0) {
printf("Unableto set buffer time %i for playback: %s\n", buffer_time,snd_strerror(err));
returnerr;
}
err= snd_pcm_hw_params_get_buffer_size(params, &size);
if(err < 0) {
printf("Unableto get buffer size for playback: %s\n", snd_strerror(err));
returnerr;
}
/*set the period time */
err= snd_pcm_hw_params_set_period_time_near(handle, params, &period_time,&dir);
if(err < 0) {
printf("Unableto set period time %i for playback: %s\n", period_time,snd_strerror(err));
returnerr;
}
err= snd_pcm_hw_params_get_period_size(params, &size, &dir);
if(err < 0) {
printf("Unableto get period size for playback: %s\n", snd_strerror(err));
returnerr;
}
period_size= size;
buffer_size= period_size * 2;
printf("---------period_size= %d----------\n",period_size);
/*write the parameters to device */
err= snd_pcm_hw_params(handle, params);
if(err < 0) {
printf("Unableto set hw params for playback: %s\n", snd_strerror(err));
returnerr;
}
return0;
}
void audioRecord(const char* file){
//getaudio from mic
interr;
snd_pcm_t*handle;
snd_pcm_sframes_tframes;
if((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_CAPTURE, 0)) < 0){
//cout<< "open mix error" << endl;
printf("openmix error\n");
exit(-1);
}
snd_pcm_hw_params_t*params=NULL;
snd_pcm_hw_params_alloca(¶ms);
err= setDeviceParam(handle,params,access_mode);
//breakdownwhen try to free the parmas, why?
//snd_pcm_hw_params_free(params);
//cout<< "buffer_size:" << buffer_size << endl;
//cout<< "buffer_time:" << buffer_time << endl;
printf("buffer_size:%d\n",buffer_size);
char*audiobuffer=(char*)malloc(buffer_size*2);
memset(audiobuffer,0,buffer_size*2);
FILE*fp = fopen(file, "wb+");
fwrite(&default_wav_hdr,sizeof(default_wav_hdr) ,1, fp); //添加wav音频头,使用采样率为16000
//cout<< "audio recorder start" << endl;
printf("audiorecorder start\n");
//for(inti=0; i<20; i++){
while(flag){
frames= snd_pcm_readi(handle, audiobuffer, buffer_size);
fwrite(audiobuffer,buffer_size*2, 1, fp);
de buffer_size*2; //计算data_size大小
//printf("[%ld]actuallyread from PCM device\n", frames);
usleep(2*1000);//防止频繁占用CPU
}
//}
free(audiobuffer);
de de + (sizeof(default_wav_hdr) - 8);
/*将修正过的数据写回文件头部,音频文件为wav格式 */
fseek(fp,4, 0);
fwrite(&de),1, fp); //写入size_8的值
fseek(fp,40, 0); //将文件指针偏移到存储data_size值的位置
fwrite(&de,sizeof(de),1, fp); //写入data_size的值
fclose(fp);
snd_pcm_close(handle);
//cout<< "------ record over --------" << endl;
printf("---------recordover--------\n");
}
void *rec_thread_run()
{
audioRecord("1.wav");
}
int main()
{
chara;
intret;
ret= pthread_create(&rec_thread_id,NULL,rec_thread_run,NULL);
if(ret!= 0){
printf("rec_threadcreate is defeated");
return0;
}
while(1){
scanf("%c",&a);
if('q'==a){
flag= false;
printf("flagis false\n");
break;
}
}
pthread_join(rec_thread_id,NULL);
return0;
}
代码中buffer_size = period_size* channels*format /8
用snd_pcm_readi()读一个buffer_size的大小,但是读出来的是两个buffer_sized的大小,所以在写进文件时,一定要写入2*buffe_size的数据,不然就不丢失数据,让音频失帧,笔者在这个地方就上过当…..
最后编译时,因为用到了多线程和ALSA库,所以编译链接时要加上:
gcc –c recorde_ –o recorde_test -lasound -lpthread 运行文件进行录音,按q键结束。
本文原创首发于【TOP智能机器人课堂】微信公众号,有兴趣可关注我哟!如想加入讨论学习群组,请关注我们并且留言,谢谢!
1.《关于怎么做wav文件,你需要知道这些如何在Ubuntu下录制wav音频文件?》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《关于怎么做wav文件,你需要知道这些如何在Ubuntu下录制wav音频文件?》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/gl/3074316.html