ANDROIDオーディオシステムハッシュの3:resample-2

8787 ワード

これは前編で述べた下位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のように:
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する.