天天看點

android+WM9714(AC97)調試

s3c6410平台,codec為WM9714(驅動可使用WM9713的),使用ALSA,android版本1.5. MID上移植android以來一直沒有聲音。

首先确認AC97的硬體連接配接,AC-LINK通訊是否正常,這裡使用到wince中的init寄存器組,在

sound/soc/codecs/wm9713.c中替換static const u16 wm9713_reg[]相應的寄存器,以适配硬體

配置。

做完這部分工作後,應該啟動時就會有噗噗聲,如果能聽到,說明硬體連接配接正常,軟體讀寫CODEC正常. 

在網上查了很多資料,很多說要配置/system/etc/asound.conf,可我也不知道怎麼去配,找了一堆的資料學習,然後對照wm9713.c中

的參數和别人的asound.conf,終于弄了一份上去,不過似乎不起說明作用,用alsa_aplay z.wav還是提示錯誤"Unable to install 

hw params:",以下函數出錯:

err = snd_pcm_hw_params(handle, params);

也就是說真正要和硬體互動的時候錯了。。 終端裡用logcat指令檢視log資訊,發現還是一直列印消息:

I/ServiceManager( 1185): Waiting for sevice media.audio_flinger...

I/ServiceManager( 1185): Waiting for sevice media.audio_flinger...

W/AudioSystem( 1185): AudioFlinger not published, waiting...

I/ServiceManager( 1185): Waiting for sevice media.audio_flinger...

I/ServiceManager( 1185): Waiting for sevice media.audio_flinger...

I/ServiceManager( 1185): Waiting for sevice media.audio_flinger...

I/ServiceManager( 1185): Waiting for sevice media.audio_flinger...

I/ServiceManager( 1185): Waiting for sevice media.audio_flinger...

W/AudioSystem( 1185): AudioFlinger not published, waiting...

I/ServiceManager( 1185): Waiting for sevice media.audio_flinger...

I/ServiceManager( 1185): Waiting for sevice media.audio_flinger...

這說明audio服務還是沒有正常啟動起來! 于是查android源代碼,在frameworks/base/libs/audioflinger/AudioFlinger.cpp中,

void AudioFlinger::instantiate() {

LOGD("*******++AudioFlinger::instantiate()/n");

    defaultServiceManager()->addService(

            String16("media.audio_flinger"), new AudioFlinger());

LOGD("*******--AudioFlinger::instantiate()/n");

}

有調用此函數将自己添加到服務管理器,但是不知道什麼原因,系統的服務管理器并沒有

得到media.audio_flinger的管理權,是以就會出現上面的一直等待的情況。

跟進new AudioFlinger()

先建立一個audio硬體對象 mAudioHardware = AudioHardwareInterface::create();

AudioHardwareInterface* AudioHardwareInterface::create()

{

    AudioHardwareInterface* hw = 0;

    char value[PROPERTY_VALUE_MAX];

#ifdef GENERIC_AUDIO

    hw = new AudioHardwareGeneric();

#else

    // if running in emulation - use the emulator driver

    if (property_get("ro.kernel.qemu", value, 0)) {

        LOGD("Running in emulation - using generic audio driver");

        hw = new AudioHardwareGeneric();

    }

    else {

        LOGV("Creating Vendor Specific AudioHardware");

        hw = createAudioHardware();

    }

#endif

    if (hw->initCheck() != NO_ERROR) {

        LOGW("Using stubbed audio hardware. No sound will be produced.");

        delete hw;

        hw = new AudioHardwareStub();

    }

#ifdef DUMP_FLINGER_OUT

    // This code adds a record of buffers in a file to write calls made by AudioFlinger.

    // It replaces the current AudioHardwareInterface object by an intermediate one which

    // will record buffers in a file (after sending them to hardware) for testing purpose.

    // This feature is enabled by defining symbol DUMP_FLINGER_OUT.

    // The output file is FLINGER_DUMP_NAME. Pause are not recorded in the file.

    hw = new AudioDumpInterface(hw);    // replace interface

#endif

    return hw;

}

這裡hw = createAudioHardware();才是真正的初始化主體:

    android::AudioHardwareInterface *createAudioHardware(void) {

        return new android::AudioHardwareALSA();

    }

也就是AudioHardwareInterface::create()傳回的是實際的硬體初始化對象AudioHardwareALSA的指針。

接着跟進:

AudioHardwareALSA::AudioHardwareALSA() :

    mOutput(0),

    mInput(0),

    mIPC(0) // sangsu fix : for IPC

{

    snd_lib_error_set_handler(&ALSAErrorHandler);

    mMixer = new ALSAMixer;  //建立混合器對象

    mIPC = new AudioHardwareIPC; // sangsu fix : IPC init

}

再看mIPC = new AudioHardwareIPC;

AudioHardwareIPC::AudioHardwareIPC() :

    mClient(NULL)

{

LOGD("### %s", __func__);

    int err = 0;

    mClient = OpenClient_RILD();

    if (mClient == NULL){

        LOGE("[*] OpenClient_RILD() error/n");

        err = 1;

    }

    if (RegisterRequestCompleteHandler(mClient, RIL_REQUEST_OEM_HOOK_RAW, 

onRawReqComplete) != RIL_CLIENT_ERR_SUCCESS){

        LOGE("[*] RegisterRequestCompleteHandler() error/n");

        err = 1;

    }

    if (RegisterUnsolicitedHandler(mClient, 11004, onUnsol) != 

RIL_CLIENT_ERR_SUCCESS){

        LOGE("[*] RegisterUnsolicitedHandler() error/n");

        err = 1;

    }

    if (Connect_RILD(mClient) != RIL_CLIENT_ERR_SUCCESS){

        LOGE("[*] Connect_RILD() error/n");

        err = 1;

    }

    if (!err) LOGD("Success Initializing IPC");

    else LOGE("Failed Initializing IPC");

}

在logcat裡也确實有列印出這些出錯資訊,說明RIL服務沒有運作。

再次回到AudioFlinger::AudioFlinger(),建立音頻接口對象後,接着打開OutputStream

AudioStreamOut *hwOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status);

實際調用函數:

AudioStreamOut *

AudioHardwareALSA::openOutputStream(int format,

                                    int channelCount,

                                    uint32_t sampleRate,

                                    status_t *status)

{

AutoMutex lock(mLock);

LOGD("++AudioHardwareALSA::openOutputStream()/n");

    // only one output stream allowed

    if (mOutput) {

        *status = ALREADY_EXISTS;

        return 0;

    }

    AudioStreamOutALSA *out = new AudioStreamOutALSA(this);

    *status = out->set(format, channelCount, sampleRate);

    if (*status == NO_ERROR) {

        mOutput = out;

        // Some information is expected to be available immediately after

        // the device is open.

        uint32_t routes = mRoutes[mMode];

        mOutput->setDevice(mMode, routes);

    }

    else {

        delete out;

    }

LOGD("--AudioHardwareALSA::openOutputStream()/n");

    return mOutput;

}

進入new AudioStreamOutALSA(this)

AudioStreamOutALSA::AudioStreamOutALSA(AudioHardwareALSA *parent) :

    mParent(parent),

    mPowerLock(false)

{

    static StreamDefaults _defaults = {

        devicePrefix   : "AndroidPlayback",

        direction      : SND_PCM_STREAM_PLAYBACK,

        format         : SND_PCM_FORMAT_S16_LE,   // AudioSystem::PCM_16_BIT

        channels       : 2,

        sampleRate     : DEFAULT_SAMPLE_RATE,

        latency        : 250000,                  // Desired Delay in usec

        // sangsu fix : optimizing output buffer size

        bufferSize     : 2048,                   // Desired Number of samples

        };

    setStreamDefaults(&_defaults);

}

這裡就是設定的預設OutStream參數了。。 再看out->set(format, channelCount, sampleRate);

            status_t                set(int format          = 0,

                                        int channelCount    = 0,

                                        uint32_t sampleRate = 0) {

                return ALSAStreamOps::set(format, channelCount, sampleRate);

            }

status_t ALSAStreamOps::set(int      format,

                            int      channels,

                            uint32_t rate)

{

    if (channels != 0)

        mDefaults->channels = channels;

    if (rate != 0)

        mDefaults->sampleRate = rate;

    switch(format) {

      // format == 0

        case AudioSystem::DEFAULT:

            break;

        case AudioSystem::PCM_16_BIT:

            mDefaults->format = SND_PCM_FORMAT_S16_LE;

            break;

        case AudioSystem::PCM_8_BIT:

            mDefaults->format = SND_PCM_FORMAT_S8;

            break;

        default:

            LOGE("Unknown PCM format %i. Forcing default", format);

            break;

    }

    return NO_ERROR;

}

也就是繼續設定default參數。 然後調用mOutput->setDevice(mMode, routes);

status_t AudioStreamOutALSA::setDevice(int mode, uint32_t newDevice)

{

    AutoMutex lock(mLock);

    return ALSAStreamOps::setDevice(mode, newDevice);

}

status_t ALSAStreamOps::setDevice(int mode, uint32_t device)

{

    // Close off previously opened device.

    // It would be nice to determine if the underlying device actually

    // changes, but we might be manipulating mixer settings (see asound.conf).

    //

    close();

    const char *stream = streamName();

    status_t    status = open (mode, device);

    int     err;

    if (status != NO_ERROR)

        return status;

    err = snd_pcm_hw_params_any(mHandle, mHardwareParams);

    if (err < 0) {

        LOGE("Unable to configure hardware: %s", snd_strerror(err));

        return NO_INIT;

    }

    status = setPCMFormat(mDefaults->format);

    // Set the interleaved read and write format.

    err = snd_pcm_hw_params_set_access(mHandle, mHardwareParams,

                                       SND_PCM_ACCESS_RW_INTERLEAVED);

    if (err < 0) {

        LOGE("Unable to configure PCM read/write format: %s",

            snd_strerror(err));

        return NO_INIT;

    }

    //

    // Some devices do not have the default two channels.  Force an error to

    // prevent AudioMixer from crashing and taking the whole system down.

    //

    // Note that some devices will return an -EINVAL if the channel count

    // is queried before it has been set.  i.e. calling channelCount()

    // before channelCount(channels) may return -EINVAL.

    //

    status = channelCount(mDefaults->channels);

    if (status != NO_ERROR)

        return status;

    // Don't check for failure; some devices do not support the default

    // sample rate.

    sampleRate(mDefaults->sampleRate);

    // Disable hardware resampling.

    status = setHardwareResample(false);

    if (status != NO_ERROR)

        return status;

    snd_pcm_uframes_t bufferSize = mDefaults->bufferSize;

    unsigned int latency = mDefaults->latency;

    // Make sure we have at least the size we originally wanted

    err = snd_pcm_hw_params_set_buffer_size(mHandle, mHardwareParams, bufferSize);

    if (err < 0) {

        LOGE("Unable to set buffer size to %d:  %s",

             (int)bufferSize, snd_strerror(err));

        return NO_INIT;

    }

    // Setup buffers for latency

    err = snd_pcm_hw_params_set_buffer_time_near (mHandle, mHardwareParams,

                                                  &latency, NULL);

    if (err < 0) {

        unsigned int periodTime = latency / 4;

        err = snd_pcm_hw_params_set_period_time_near (mHandle, mHardwareParams,

                                                      &periodTime, NULL);

        if (err < 0) {

            LOGE("Unable to set the period time for latency: %s", snd_strerror(err));

            return NO_INIT;

        }

        snd_pcm_uframes_t periodSize;

        err = snd_pcm_hw_params_get_period_size (mHardwareParams, &periodSize, NULL);

        if (err < 0) {

            LOGE("Unable to get the period size for latency: %s", snd_strerror(err));

            return NO_INIT;

        }

        bufferSize = periodSize * 4;

        if (bufferSize < mDefaults->bufferSize)

            bufferSize = mDefaults->bufferSize;

        err = snd_pcm_hw_params_set_buffer_size_near (mHandle, mHardwareParams, &bufferSize);

        if (err < 0) {

            LOGE("Unable to set the buffer size for latency: %s", snd_strerror(err));

            return NO_INIT;

        }

    } else {

        // OK, we got buffer time near what we expect. See what that did for bufferSize.

        err = snd_pcm_hw_params_get_buffer_size (mHardwareParams, &bufferSize);

        if (err < 0) {

            LOGE("Unable to get the buffer size for latency: %s", snd_strerror(err));

            return NO_INIT;

        }

        // Does set_buffer_time_near change the passed value? It should.

        err = snd_pcm_hw_params_get_buffer_time (mHardwareParams, &latency, NULL);

        if (err < 0) {

            LOGE("Unable to get the buffer time for latency: %s", snd_strerror(err));

            return NO_INIT;

        }

        unsigned int periodTime = latency / 4;

        err = snd_pcm_hw_params_set_period_time_near (mHandle, mHardwareParams,

                                                      &periodTime, NULL);

        if (err < 0) {

            LOGE("Unable to set the period time for latency: %s", snd_strerror(err));

            return NO_INIT;

        }

    }

    LOGD("Buffer size: %d", (int)bufferSize);

    LOGD("Latency: %d", (int)latency);

    mDefaults->bufferSize = bufferSize;

    mDefaults->latency = latency;

    // Commit the hardware parameters back to the device.

    err = snd_pcm_hw_params(mHandle, mHardwareParams);

    if (err < 0) {

        LOGE("Unable to set hardware parameters: %s", snd_strerror(err));

        return NO_INIT;

    }

    status = setSoftwareParams();

    return status;

}

在調用了一系列設定參數的函數後,最後調用err = snd_pcm_hw_params(mHandle, mHardwareParams);

将參數寫入到硬體。

完成這個過程後,接着建立 MixerThread ,AudioRecordThread ,至此media.audio_flinger服務初始化完畢。

在我們的MID上,wince6.0下有調試WM9714通過,可正常發聲和錄音。可android下使用alsa_aplay測試總是出現問題。

一氣之下重新解壓android包,編譯,燒寫ramdisk-uboot.img,system.img,userdata.img,偶然在android界面點開music player,找

到SD卡上的音樂,竟然能發出不連續的聲音,欣喜若狂!  趕緊在kernel中關掉列印資訊,再次進入播放,悠揚的聲音出來了!

暈~~~  竟然不用動什麼東西! 也不需要配asound.conf,所需要注意的是測試在android界面裡用music player播放就好,

alsa_aplay還是在這種場合不合适,因為android加載,占用了音頻裝置。每次燒寫也應該帶上ramdisk-uboot.img,這個很重要。

我的alsa_aplay版本:

# alsa_aplay --version

aplay: version 1.0.19 by Jaroslav Kysela <[email protected]> 

繼續閱讀