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);
}
}
}