Android Framework學習筆記 -- Audio 混音

android的混音是通過AudioMixer來實現的,最近遇到了一個混音的問題,該是好好看看音頻的基本知識了。

音頻的基本知識

示意圖

很早之前就知道音頻存儲是通過採樣來實現的,就是所謂的A/D(Analog-to-Digital Converter與D/A(Digital Analog Converter)
音軌有很多屬性如

  • 採樣率(sampleRate)
  • 編碼格式(format)
  • 通道(channelCount)
  • 總幀數(frameCount) ?
  • 音量(volume) ?
  • 播放速率(playbackRate) ?

前三個比較重要,具體去了解一下

採樣率

看到知乎上有一個回答,感覺挺形象的什麼是音頻的採樣率?採樣率和音質有沒有關係?

一般的採樣率固定在44100HZ(- -就是一秒記44100次),理由是因爲人耳聽覺範圍在20HZ~20KHZ,這樣記錄能還原最高22.05KHZ的聲音。
還有一個採樣率48000HZ也比較常見

編碼格式

指每次採樣所用的bit數,比如8bit,16bit
從命名來看,android好像用來8bit,16bit跟32bit

  • AUDIO_FORMAT_PCM_16_BIT
  • AUDIO_FORMAT_PCM_8_BIT
  • AUDIO_FORMAT_PCM_32_BIT
  • AUDIO_FORMAT_PCM_8_24_BIT
  • AUDIO_FORMAT_PCM_FLOAT
  • AUDIO_FORMAT_PCM_16_BIT_OFFLOAD ?
  • AUDIO_FORMAT_PCM_16_BIT_OFFLOAD ?

通道

就是兩個耳機有不同的聲音??
一般有單通道(mono) 雙通道(stereo)

AudioMixer混音過程

參考博客園上的一篇博客[Android] 混音器AudioMixer 結合代碼分析。

AudioMixer的創建

mNormalFrameCount爲輸入buffer大小??mSampleRate爲輸出採樣率

AudioFlinger::MixerThread::MixerThread(...)
    : ...{
    ...
    mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
    ...
}

配置參數

在MixerThread的prepareTracks_l會對AudioMixer的參數進行配置,如音量,輸入源,混音參數等。

AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(...){
    ...
    // 設置輸入源  name是引索集合,track是mActiveTracks??
    mAudioMixer->setBufferProvider(name, track);

    // 啓用該啓用的音軌並更新狀態
    mAudioMixer->enable(name);

    // 設置左右音量
    mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf);
    mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, &vrf);
    mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, &vaf);

    // 設置混音格式
    mAudioMixer->setParameter(name,AudioMixer::TRACK,AudioMixer::FORMAT, (void *)track->format());

    // 設置通道數
    mAudioMixer->setParameter(name,AudioMixer::TRACK,AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)track->channelMask());

    ...
    // 設置採樣率
    mAudioMixer->setParameter(name,AudioMixer::RESAMPLE,AudioMixer::SAMPLE_RATE,(void *)(uintptr_t)reqSampleRate);

    // 設置播放速率  
    mAudioMixer->setParameter(name,AudioMixer::TIMESTRETCH,AudioMixer::PLAYBACK_RATE,&playbackRate);

    ... // mark一下
    if (mMixerBufferEnabled && (track->mainBuffer() == mSinkBuffer || track->mainBuffer() == mMixerBuffer)) {
         mAudioMixer->setParameter(name,AudioMixer::TRACK,AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
    }

    // 設置輸出
    mAudioMixer->setParameter(name,AudioMixer::TRACK,AudioMixer::MAIN_BUFFER, (void *)mMixerBuffer);
    ...
}

混音函數選擇

先計算track的flags,然後通過flags來判斷要混音的函數

void AudioMixer::process__validate(state_t* state, int64_t pts)
{
    ...(計算需要invalidate的track)

    // 主要通過下列幾個參數去判斷
    bool all16BitsStereoNoResample = true;
    bool resampling = false;
    bool volumeRamp = false;
    uint32_t en = state->enabledTracks;

    while (en) {    //對所有需要進行混音的track
        const int i = 31 - __builtin_clz(en); //取出最高位爲1的bit
        en &= ~(1<<i);  //把這一位置爲0

        countActiveTracks++;
        track_t& t = state->tracks[i];  //取出來track

        // 計算flags
        uint32_t n = 0;
        n |= NEEDS_CHANNEL_1 + t.channelCount - 1;    //至少有一個channel需要混音
        n |= NEEDS_FORMAT_16;          //必須爲16bit PCM
        n |= t.doesResample() ? NEEDS_RESAMPLE_ENABLED : NEEDS_RESAMPLE_DISABLED; //是否需要重採樣
        if (t.auxLevel != 0 && t.auxBuffer != NULL) {
            n |= NEEDS_AUX_ENABLED;
        }

        if (t.volumeInc[0]|t.volumeInc[1]) {
            volumeRamp = true;
        } else if (!t.doesResample() && t.volumeRL == 0) {
            n |= NEEDS_MUTE_ENABLED;
        }
        t.needs = n;    //更新track flag

        ...(通過flags選擇混音函數)

        //這裏調用一次進行混音,後續會在MixerThread的threadLoop_mix內調用
        state->hook(state, pts);  

        ...
    }
}

混音

就是像搓面把幾根面搓成一團

示意圖

在android裏使用process_xxx函數來實現,幾個方法大同小異(不是我說的),舉個process__genericResampling當例子。

// generic code with resampling
void AudioMixer::process__genericResampling(state_t* state, int64_t pts)
{
    int32_t* const outTemp = state->outputTemp;
    size_t numFrames = state->frameCount;

    uint32_t e0 = state->enabledTracks;
    while (e0) {
        ...(選出有相同mainBuffer的track集合e1)
        int32_t *out = t1.mainBuffer;
        memset(outTemp, 0, size);

        // 搓面
        while (e1) {
            const int i = 31 - __builtin_clz(e1);
            e1 &= ~(1<<i);
            track_t& t = state->tracks[i];
            int32_t *aux = NULL;
            if (CC_UNLIKELY(t.needs & NEEDS_AUX)) {
                aux = t.auxBuffer;
            }

            // this is a little goofy, on the resampling case we don't
            // acquire/release the buffers because it's done by
            // the resampler.
            if (t.needs & NEEDS_RESAMPLE) {
                t.resampler->setPTS(pts);
                t.hook(&t, outTemp, numFrames, state->resampleTemp, aux);
            } else {...}
        }
    }
}

真正的採樣在這(最終的採樣在resample)

void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount,
        int32_t* temp, int32_t* aux)
{
    ALOGVV("track__genericResample\n");
    t->resampler->setSampleRate(t->sampleRate);

    // ramp gain - resample to temp buffer and scale/mix in 2nd step
    if (aux != NULL) {
        ...(設置ramp音量)
    } else {
        if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) {
            t->resampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
            memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
            t->resampler->resample(temp, outFrameCount, t->bufferProvider);
            volumeRampStereo(t, out, outFrameCount, temp, aux);
        }

        // constant gain
        else {
            t->resampler->setVolume(t->mVolume[0], t->mVolume[1]);
            t->resampler->resample(out, outFrameCount, t->bufferProvider);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章