基於Linux 2.6內核ALSA架構的PCM混音demo


一個混音例程,多聲道混音成單聲道,錄制下了原始聲音和混音之后的聲音。

混音之后的聲音是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(&params);
    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);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM