本文主要讨论在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(&params);

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