您的位置:首页 > 运维架构 > 网站架构

基于Linux 2.6内核ALSA架构的PCM混音demo

2012-10-26 16:44 381 查看
一个混音例程,多声道混音成单声道,录制下了原始声音和混音之后的声音。

混音之后的声音是8kHz,16bit,带符号单声道的声音。

#define ALSA_PCM_NEW_HW_PARAMS_API
#include <alsa/asoundlib.h>
#include <alloca.h>
#include <stdio.h>

void mixchannel(FILE *fp, const snd_pcm_channel_area_t *areas, unsigned int chs, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames, snd_pcm_format_t fmt, unsigned int step);

int main(int argc, char *argv[]) {
const char *dev = "hw:0,0";
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
snd_pcm_format_t fmt = SND_PCM_FORMAT_S16_LE;
unsigned int channel = 2;
unsigned int rate = 44100;//源采样率
unsigned int drate = 8000;//目标采样率
unsigned int step;
unsigned int phbits;
unsigned int fmtbits;
snd_pcm_uframes_t periods;
FILE *fp1, *fp2;
int rval;
if (argc != 3) {
printf("usage: %s raw.file, mono.file.\n", argv[0]);
return 0;
}
fp1 = fopen(argv[1], "w");//原始流
fp2 = fopen(argv[2], "w");//混音之后的流
if (!fp1 || !fp2) {
printf("file open error!\n");
return -1;
}
rval = snd_pcm_open(&handle, dev, SND_PCM_STREAM_CAPTURE, 0);
if (rval < 0) {
printf("open failed!(%s).\n", snd_strerror(rval));
return -1;
}
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(handle, params);
rval = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_MMAP_INTERLEAVED);
if (rval < 0) {
printf("set access failed!(%s).\n", snd_strerror(rval));
return -1;
}
rval = snd_pcm_hw_params_set_format(handle, params, fmt);
if (rval < 0) {
printf("set format failed!(%s).\n", snd_strerror(rval));
return -1;
}
rval = snd_pcm_hw_params_set_channels(handle, params, channel);
if (rval < 0) {
printf("set channel(%u) failed!(%s).\n", channel, snd_strerror(rval));
return -1;
}
rval = snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0);
if (rval < 0) {
printf("set rate(%u) failed!(%s).\n", rate, snd_strerror(rval));
return -1;
}
rval = snd_pcm_hw_params(handle, params);
if (rval < 0) {
printf("set params failed!(%s).\n", snd_strerror(rval));
return -1;
}
snd_pcm_hw_params_get_format(params, &fmt);
phbits = snd_pcm_format_physical_width(fmt);
fmtbits = snd_pcm_format_width(fmt);
snd_pcm_hw_params_get_period_size(params, &periods, 0);
printf("capture.\n");
printf("rate %u -> %u.\n", rate, drate);
printf("channel %d -> 1.\n", channel);
printf("fmt:%s, bytes: %u, bits:%u.\n", snd_pcm_format_name(fmt), phbits/8, fmtbits);
rval = snd_pcm_start(handle);
if (rval < 0) {
printf("start failed!(%s).\n", snd_strerror(rval));
return -1;
}
step = rate/drate;
while (1) {
snd_pcm_uframes_t offset, frames;
snd_pcm_sframes_t avail, commits;
snd_pcm_state_t state = snd_pcm_state(handle);
if (state == SND_PCM_STATE_XRUN) {
rval = snd_pcm_prepare(handle);
if (rval < 0) {
printf("strong error!(%s).\n", snd_strerror(rval));
break;
}
rval = snd_pcm_start(handle);
if (rval < 0) {
printf("start error!(%s)\n", snd_strerror(rval));
break;
}
}
avail = snd_pcm_avail_update(handle);
if (avail < 0) {
continue;
}
if (avail < periods) {
rval = snd_pcm_wait(handle, -1);
if (rval < 0) {
continue;
}
}
//printf("avail:%lu.\n", avail);
while (avail >= periods) {
frames = periods;
const snd_pcm_channel_area_t *areas;
rval = snd_pcm_mmap_begin(handle, &areas, &offset, &frames);
//交错模式下的PCM数据保存在同一个缓冲区内,可以直接写入文件
fwrite(areas->addr + areas->step/8*offset, frames, areas->step/8, fp1);
mixchannel(fp2, areas, channel, offset, frames, fmt, step);
commits = snd_pcm_mmap_commit(handle, offset, frames);
//printf("commit, commits:%ld.\n", commits);
if (rval < 0) {
break;
}
if (commits < 0 || commits != frames) {
break;
}
avail -= periods;
}
}
fclose(fp1);
fclose(fp2);
snd_pcm_drop(handle);
snd_pcm_close(handle);
return 0;
}

void mixchannel(FILE *fp, const snd_pcm_channel_area_t *areas, unsigned int chs, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames, snd_pcm_format_t fmt, unsigned int step) {
static char buf[1024*1024];
char *bp = buf;
const char *smp;
int fbytes = snd_pcm_format_physical_width(fmt)/8;
int fbits = snd_pcm_format_width(fmt);
int bigendian = snd_pcm_format_big_endian(fmt);
int sfmt = snd_pcm_format_signed(fmt);
unsigned long long umask, val;
long long smask;
for (int i = rand()%step; i < frames; i += step) {
umask = 0;
for (int ch = 0; ch < chs; ++ch) {
smp = areas[ch].addr + areas[ch].first/8 + areas[ch].step/8*(offset+i);
val = 0;
for (int j = 0; j < fbytes; ++j) {
if (bigendian) {
val += ((unsigned char)smp[j] >> ((fbytes-j-1)*8));
} else {
val += ((unsigned char)smp[j] << (j*8));
}
}
if (sfmt) {
val ^= (1u<<(fbits-1));    //转换值域,带符号转无符号
}
umask += val;         //混声道
}
umask /= chs;    //如果不取均值,则是噪音,但是取均值之后,声音会变弱,原理上应该是不需要取均值的,也许是哪里出了其他问题
umask &= 0xffff;  //截断16bit
smask = umask-0x8000u;//转换值域,无符号转成带符号
*bp = smask & 0xff;
*(bp+1) = (smask >> 8) & 0xff;
bp += 2;
}
fwrite(buf, 2, frames/step, fp);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: