ANDROIDオーディオシステムハッシュの3:resample-2
これは前編で述べた下位resample処理を受け,Samsungのtiny alsa-libを例に説明する.
tiny alsa-lib
このtiny alsa-libはandroid 2にある.3.1-gingerbread/device/samsung/crespo/libaudio.前述したようにalsa-libはpluginの機能が多すぎて、複雑で肥大しているように見えます.したがって、alsaが上位レベルで呼び出されるプロセスを理解するには、このtiny alsa-libから始めたほうがいいと思います.2つのソースファイル:alsa_pcm.cとalsa_mixer.c,前者はpcm再生録音インタフェース,後者はmixer controlsの制御インタフェースである.
Alsa-libは,実際には/devディレクトリのデバイスノードを操作することによってカーネル空間のオーディオ駆動インタフェースを呼び出すこともあり,これは通常の文字デバイスの呼び出し方法と同様である.Openのように:
ここではこれらのインタフェースの実現をあまり考慮していない.alsa_pcm.cには興味深い関数があります.
SNDRV_MASK_MAXとsnd_maskの定義はそれぞれ以下の通りです.
結合SNDRV_MASK_MAXとsnd_maskは、maskのビット数は256に達することができますが、私たちのコンピュータのワード長は32ビットなので、8つの32ビットの配列で256ビットのマスクを構成します.param_set_mask関数はこのマスクで設定されます.
ここでm->bits[bit>>5]|=(1<(bit&31);コア文であり、bit>>5はbitを32(すなわち配列要素長)で割って配列下付きを取得し、1<(bit&31)は配列要素におけるマスクビットのオフセット量である.bit=255の場合、配列の下付きは7、すなわち配列bitsの最後の要素であり、オフセット量は1<<31である.このとき、bits全体のデータは、bits[7:0]=0 x 8000000000:0 x 00000000:0 x 00000000:0 x 00000000:0 x 00000000:0 x 00000000:0 x 00000000:0 x 00000000:0 x 00000000:0 x 00000000:0 x 256,000ビットのマスクの最上位に1が置かれる.もちろん実際のアプリケーションではそれほど高いマスクは使われませんが、ここでは後で拡張して使用しやすいようにするため、m->bits[0]=0しか必要ありません.m->bits[1]=0で、最大64ビットマスクしか使われていないようです.
ADCLRC制約
pcm_Openには、
放音においても録音においても44.1 khzのサンプリングレートが設定されていることがわかる.我々の下層I 2 S駆動では,放音録音もサンプリング率44.1 khzを固定した.どうしてそうするの?放音はともかく、Androidは各trackのデータをミックスする必要があるため、放音サンプリング率を44.1 khzに固定し、録音も44.1 khzに固定するのはなぜですか?注意:ここでのサンプリングレートは、ハードウェア信号ADCLRC/DACLRC周波数に直接対応します.
まず,I 2 Sプロトコルに関する知識が必要である.放音サンプリングレートDACLRC,録音サンプリングレートADCLRCは,いずれも同じ主クロックMCLKにより分周されている.下部オーディオ駆動では、一般的に以下の構造体がある.
ここで、MCLKは、8 k、16 k、32 k、48 k、96 khzの分周に用いられ、11.025 k、22.05 k、44.1 khzの分周に用いられる2つの適合可能な周波数を有する.具体的な式はrate=mclk/fs、例えば44100=128600/256である.
問題を見ましたか.録音サンプリングレートが8 khzに設定されている場合、MCLKは12288000に変更する必要があり、このときDACLRCは変更され(放音音が鋭くなる)、同時放音録音に不利である.従って、録音サンプリングレートは、必ずしも44.1 khzではなく、11.025 khzの倍数であればよく、同じMCLKから分周できることが保証される.
DownSampler
android 2.3.1-gingerbread/device/samsung/crespo/libaudioではtiny alsa-libのほか、SamsungがAndroidのために書いたAudioHALです.例えばAudioHardware.cpp、これはalsaに相当します.soundのファイル.このHALは汎用性が高く、通話機能のないMIDに移植しても正常に動作し、もちろんSamsungのいくつかの専用性も保持され、主に通話音声チャネル処理である.ここでは、AudioFlingerおよびalsa_soundに詳しいと、すぐにマスターできます.
上記の章で述べたように、下位録音サンプリングレートADCLRC固定は44.1 khzであるが、上位層が8 khzのような他のサンプリングレートを望む場合は、どうすればよいのか.resample間違いない.ここでサポートされる録音サンプリングレートは、8000、11025、16000、22050、44100であり、いずれも44.1 khz以下であるため、downsample(同様に低サンプリングレートから高サンプリングレートに変換することをupsampleと呼ぶ)だけである.以下に簡単な分析を示します.
以上はsetメソッドで、パラメータformat、samplerate、channelcountの正当性を確認し、samplerateがADCLRCと一致しているかどうかを確認し、一致していない場合はDownSamplerを作成します.
readメソッドのコードクリップを見てみましょう.
1、AudioHardware::AudioStreamInALSA::getNextBufferメソッドを呼び出し、オーディオpcmデータを取得し、bufferに保存し、次のbufferのアドレスを計算する.
2、buffer中のデータを各チャンネルのデータに分解し、mInLeftとmInRightに保存する.
3、元のオーディオpcmデータサンプリングレートが44.1 khzであるため、resample_を呼び出す2_1データを22.05 khzサンプリングレートに変換する.
4、1)上位層に必要なsamplerate=11.025 khzの場合resample_を呼び出す2_1データサンプリングレートを22.05 khzから11.025 khzに変換する.
2)上位層に必要なsamplerate=8 khzの場合resample_を呼び出す441_320は、データサンプリングレートを11.025 khzから8 khzに変換する.
5、上位層に必要なsamplerate=16 khzの場合、resample_を呼び出す441_320は、データサンプリングレートを22.05 khzから16 khzに変換する.
本物のresample処理はresample_2_1()とresample_441_320()の2つの関数です.前者は倍数2のサンプリングレートをresampleしたもので、例えば44100->22050、22050->11025、16000->8000などである.後者は、44100->32000、22050->16000、11025->8000など、441/320のサンプリングレートをresampleする.
tiny alsa-lib
このtiny alsa-libはandroid 2にある.3.1-gingerbread/device/samsung/crespo/libaudio.前述したようにalsa-libはpluginの機能が多すぎて、複雑で肥大しているように見えます.したがって、alsaが上位レベルで呼び出されるプロセスを理解するには、このtiny alsa-libから始めたほうがいいと思います.2つのソースファイル:alsa_pcm.cとalsa_mixer.c,前者はpcm再生録音インタフェース,後者はmixer controlsの制御インタフェースである.
Alsa-libは,実際には/devディレクトリのデバイスノードを操作することによってカーネル空間のオーディオ駆動インタフェースを呼び出すこともあり,これは通常の文字デバイスの呼び出し方法と同様である.Openのように:
struct pcm *pcm_open(unsigned flags)
{
const char *dname;
struct pcm *pcm;
struct snd_pcm_info info;
struct snd_pcm_hw_params params;
struct snd_pcm_sw_params sparams;
unsigned period_sz;
unsigned period_cnt;
LOGV("pcm_open(0x%08x)",flags);
pcm = calloc(1, sizeof(struct pcm));
if (!pcm)
return &bad_pcm;
if (flags & PCM_IN) {
dname = "/dev/snd/pcmC0D0c"; //capture
} else {
dname = "/dev/snd/pcmC0D0p"; //playback
}
...
pcm->flags = flags;
pcm->fd = open(dname, O_RDWR);
if (pcm->fd < 0) {
oops(pcm, errno, "cannot open device '%s'");
return pcm;
}
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_INFO, &info)) {
oops(pcm, errno, "cannot get info - %s");
goto fail;
}
...
}
ここではこれらのインタフェースの実現をあまり考慮していない.alsa_pcm.cには興味深い関数があります.
static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned bit)
{
if (bit >= SNDRV_MASK_MAX)
return;
if (param_is_mask(n)) {
struct snd_mask *m = param_to_mask(p, n);
m->bits[0] = 0;
m->bits[1] = 0;
m->bits[bit >> 5] |= (1 << (bit & 31));
}
}
SNDRV_MASK_MAXとsnd_maskの定義はそれぞれ以下の通りです.
#define SNDRV_MASK_MAX 256
struct snd_mask {
__u32 bits[(SNDRV_MASK_MAX+31)/32];
};
結合SNDRV_MASK_MAXとsnd_maskは、maskのビット数は256に達することができますが、私たちのコンピュータのワード長は32ビットなので、8つの32ビットの配列で256ビットのマスクを構成します.param_set_mask関数はこのマスクで設定されます.
ここでm->bits[bit>>5]|=(1<(bit&31);コア文であり、bit>>5はbitを32(すなわち配列要素長)で割って配列下付きを取得し、1<(bit&31)は配列要素におけるマスクビットのオフセット量である.bit=255の場合、配列の下付きは7、すなわち配列bitsの最後の要素であり、オフセット量は1<<31である.このとき、bits全体のデータは、bits[7:0]=0 x 8000000000:0 x 00000000:0 x 00000000:0 x 00000000:0 x 00000000:0 x 00000000:0 x 00000000:0 x 00000000:0 x 00000000:0 x 00000000:0 x 256,000ビットのマスクの最上位に1が置かれる.もちろん実際のアプリケーションではそれほど高いマスクは使われませんが、ここでは後で拡張して使用しやすいようにするため、m->bits[0]=0しか必要ありません.m->bits[1]=0で、最大64ビットマスクしか使われていないようです.
ADCLRC制約
pcm_Openには、
param_set_int(¶ms, SNDRV_PCM_HW_PARAM_RATE, 44100);
if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_HW_PARAMS, ¶ms)) {
oops(pcm, errno, "cannot set hw params");
goto fail;
}
放音においても録音においても44.1 khzのサンプリングレートが設定されていることがわかる.我々の下層I 2 S駆動では,放音録音もサンプリング率44.1 khzを固定した.どうしてそうするの?放音はともかく、Androidは各trackのデータをミックスする必要があるため、放音サンプリング率を44.1 khzに固定し、録音も44.1 khzに固定するのはなぜですか?注意:ここでのサンプリングレートは、ハードウェア信号ADCLRC/DACLRC周波数に直接対応します.
まず,I 2 Sプロトコルに関する知識が必要である.放音サンプリングレートDACLRC,録音サンプリングレートADCLRCは,いずれも同じ主クロックMCLKにより分周されている.下部オーディオ駆動では、一般的に以下の構造体がある.
struct _coeff_div {
u32 mclk;
u32 rate;
u16 fs;
u8 sr;
u8 bclk_div;
};
/* codec hifi mclk clock divider coefficients */
static const struct _coeff_div coeff_div[] = {
/* 8k */
{12288000, 8000, 1536, 0x4, 0x0},
/* 11.025k */
{11289600, 11025, 1024, 0x8, 0x0},
/* 16k */
{12288000, 16000, 768, 0x5, 0x0},
/* 22.05k */
{11289600, 22050, 512, 0x9, 0x0},
/* 32k */
{12288000, 32000, 384, 0x7, 0x0},
/* 44.1k */
{11289600, 44100, 256, 0x6, 0x07},
/* 48k */
{12288000, 48000, 256, 0x0, 0x07},
/* 96k */
{12288000, 96000, 128, 0x1, 0x04},
};
ここで、MCLKは、8 k、16 k、32 k、48 k、96 khzの分周に用いられ、11.025 k、22.05 k、44.1 khzの分周に用いられる2つの適合可能な周波数を有する.具体的な式はrate=mclk/fs、例えば44100=128600/256である.
問題を見ましたか.録音サンプリングレートが8 khzに設定されている場合、MCLKは12288000に変更する必要があり、このときDACLRCは変更され(放音音が鋭くなる)、同時放音録音に不利である.従って、録音サンプリングレートは、必ずしも44.1 khzではなく、11.025 khzの倍数であればよく、同じMCLKから分周できることが保証される.
DownSampler
android 2.3.1-gingerbread/device/samsung/crespo/libaudioではtiny alsa-libのほか、SamsungがAndroidのために書いたAudioHALです.例えばAudioHardware.cpp、これはalsaに相当します.soundのファイル.このHALは汎用性が高く、通話機能のないMIDに移植しても正常に動作し、もちろんSamsungのいくつかの専用性も保持され、主に通話音声チャネル処理である.ここでは、AudioFlingerおよびalsa_soundに詳しいと、すぐにマスターできます.
上記の章で述べたように、下位録音サンプリングレートADCLRC固定は44.1 khzであるが、上位層が8 khzのような他のサンプリングレートを望む場合は、どうすればよいのか.resample間違いない.ここでサポートされる録音サンプリングレートは、8000、11025、16000、22050、44100であり、いずれも44.1 khz以下であるため、downsample(同様に低サンプリングレートから高サンプリングレートに変換することをupsampleと呼ぶ)だけである.以下に簡単な分析を示します.
status_t AudioHardware::AudioStreamInALSA::set(
AudioHardware* hw, uint32_t devices, int *pFormat,
uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics)
{
if (pFormat == 0 || *pFormat != AUDIO_HW_IN_FORMAT) {
*pFormat = AUDIO_HW_IN_FORMAT; //AudioSystem::PCM_16_BIT
return BAD_VALUE;
}
if (pRate == 0) {
return BAD_VALUE;
}
//getInputSampleRate: sampleRate
// :8000, 11025, 16000, 22050, 44100
// , sampleRate , BAD_VALUE
uint32_t rate = AudioHardware::getInputSampleRate(*pRate);
if (rate != *pRate) {
*pRate = rate;
return BAD_VALUE;
}
if (pChannels == 0 || (*pChannels != AudioSystem::CHANNEL_IN_MONO &&
*pChannels != AudioSystem::CHANNEL_IN_STEREO)) {
*pChannels = AUDIO_HW_IN_CHANNELS; //AudioSystem::CHANNEL_IN_MONO
return BAD_VALUE;
}
mHardware = hw;
LOGV("AudioStreamInALSA::set(%d, %d, %u)", *pFormat, *pChannels, *pRate);
//getBufferSize: buffer
//popCount: u 0 , ,
mBufferSize = getBufferSize(*pRate, AudioSystem::popCount(*pChannels));
mDevices = devices;
mChannels = *pChannels;
mChannelCount = AudioSystem::popCount(mChannels);
mSampleRate = rate;
// mSampleRate AUDIO_HW_OUT_SAMPLERATE(44.1khz) , down resample
if (mSampleRate != AUDIO_HW_OUT_SAMPLERATE) {
mDownSampler = new AudioHardware::DownSampler(mSampleRate,
mChannelCount,
AUDIO_HW_IN_PERIOD_SZ,
this);
status_t status = mDownSampler->initCheck();
if (status != NO_ERROR) {
delete mDownSampler;
LOGW("AudioStreamInALSA::set() downsampler init failed: %d", status);
return status;
}
mPcmIn = new int16_t[AUDIO_HW_IN_PERIOD_SZ * mChannelCount];
}
return NO_ERROR;
}
以上はsetメソッドで、パラメータformat、samplerate、channelcountの正当性を確認し、samplerateがADCLRCと一致しているかどうかを確認し、一致していない場合はDownSamplerを作成します.
readメソッドのコードクリップを見てみましょう.
ssize_t AudioHardware::AudioStreamInALSA::read(void* buffer, ssize_t bytes)
{
......
// DownSampler
if (mDownSampler != NULL) {
size_t frames = bytes / frameSize();
size_t framesIn = 0;
mReadStatus = 0;
do {
size_t outframes = frames - framesIn;
// DownSampler resample , pcm , resample
mDownSampler->resample(
(int16_t *)buffer + (framesIn * mChannelCount),
&outframes);
framesIn += outframes;
} while ((framesIn < frames) && mReadStatus == 0);
ret = mReadStatus;
bytes = framesIn * frameSize();
} else {
TRACE_DRIVER_IN(DRV_PCM_READ)
// DownSampler, pcm
ret = pcm_read(mPcm, buffer, bytes);
TRACE_DRIVER_OUT
}
......
}
から、上位層に必要なsamplerateが44.1 khzと一致しない場合、DownSample::resample処理:1、AudioHardware::AudioStreamInALSA::getNextBufferメソッドを呼び出し、オーディオpcmデータを取得し、bufferに保存し、次のbufferのアドレスを計算する.
2、buffer中のデータを各チャンネルのデータに分解し、mInLeftとmInRightに保存する.
3、元のオーディオpcmデータサンプリングレートが44.1 khzであるため、resample_を呼び出す2_1データを22.05 khzサンプリングレートに変換する.
4、1)上位層に必要なsamplerate=11.025 khzの場合resample_を呼び出す2_1データサンプリングレートを22.05 khzから11.025 khzに変換する.
2)上位層に必要なsamplerate=8 khzの場合resample_を呼び出す441_320は、データサンプリングレートを11.025 khzから8 khzに変換する.
5、上位層に必要なsamplerate=16 khzの場合、resample_を呼び出す441_320は、データサンプリングレートを22.05 khzから16 khzに変換する.
本物のresample処理はresample_2_1()とresample_441_320()の2つの関数です.前者は倍数2のサンプリングレートをresampleしたもので、例えば44100->22050、22050->11025、16000->8000などである.後者は、44100->32000、22050->16000、11025->8000など、441/320のサンプリングレートをresampleする.