Android Audio specail Introduction 1 -- ICS Audio volume setting path

Android 闲来无事, 在ICS 中对Android 音频架构又是一通乱改,本来就调来调去的混乱逻辑变得更加飘忽不定。 本文旨在理清Android audio 架构,以volume setting 为例。

以情景分析方式入题:


首先从JNI 入手, 基本的声音调节函数

./mydroid/frameworks/base/core/jni/android_media_AudioSystem.cpp

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

static int
android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env, jobject thiz, jint stream, jint index)
{
    return check_AudioSystem_Command(AudioSystem::setStreamVolumeIndex(static_cast <audio_stream_type_t>(stream), index));
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

调节特定码流的音量, 调到公用函数AudioSystem::setStreamVolumeIndex


---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

status_t AudioSystem::setStreamVolumeIndex(audio_stream_type_t stream, int index)
{   
    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
    if (aps == 0) return PERMISSION_DENIED;
    return aps->setStreamVolumeIndex(stream, index);
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

又调到了AudioPolicyService.


---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, int index)
{   
    if (mpAudioPolicy == NULL) {
        return NO_INIT;
    }
    if (!checkPermission()) {
        return PERMISSION_DENIED;
    }
    if (stream < 0 || stream >= AUDIO_STREAM_CNT) {
        return BAD_VALUE;
    }


    return mpAudioPolicy->set_stream_volume_index(mpAudioPolicy, stream, index);
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

看到这,就要聊一下mpAudioPolicy 怎么来的了,这个关心ICS新架构。


---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

AudioPolicyService::AudioPolicyService()
    : BnAudioPolicyService() , mpAudioPolicyDev(NULL) , mpAudioPolicy(NULL)
{
    char value[PROPERTY_VALUE_MAX];
    const struct hw_module_t *module;
    int forced_val;
    int rc;


    Mutex::Autolock _l(mLock);

    //这两个线程一会会提到
    // start tone playback thread
    mTonePlaybackThread = new AudioCommandThread(String8(""));
    // start audio commands thread
    mAudioCommandThread = new AudioCommandThread(String8("ApmCommandThread"));


    // 试图寻找AUDIO_POLICY_HARDWARE_MODULE_ID 这个模块,这个概念和当年的私有policy类是一样的概念。

    // 现在audio hardware层分为传统的和非传统的, 现在还没看到谁家用新架构写Policy, 基本还都是用传统的。

    /* instantiate the audio policy manager */
    rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);
    if (rc)
        return;


    rc = audio_policy_dev_open(module, &mpAudioPolicyDev);
    LOGE_IF(rc, "couldn't open audio policy device (%s)", strerror(-rc));
    if (rc)

        return;

    // 这里会创建一个policy device, 
    rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this,
                                               &mpAudioPolicy);

    LOGE_IF(rc, "couldn't create audio policy (%s)", strerror(-rc));
    if (rc)
        return;


    rc = mpAudioPolicy->init_check(mpAudioPolicy);
    LOGE_IF(rc, "couldn't init_check the audio policy (%s)", strerror(-rc));
    if (rc)
        return;


    property_get("ro.camera.sound.forced", value, "0");
    forced_val = strtol(value, NULL, 0);
    mpAudioPolicy->set_can_mute_enforced_audible(mpAudioPolicy, !forced_val);


    LOGI("Loaded audio policy from %s (%s)", module->name, module->id);


    // load audio pre processing modules
    if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) {
        loadPreProcessorConfig(AUDIO_EFFECT_VENDOR_CONFIG_FILE);

    } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) {
        loadPreProcessorConfig(AUDIO_EFFECT_DEFAULT_CONFIG_FILE);
    }
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

挖进去看create_audio_policy, 现在传统的policy 在hardware/libhardware_legacy/audio/audio_policy_hal.cpp


---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    dev->device.create_audio_policy = create_legacy_ap;


then, 看实现函数


static int create_legacy_ap(const struct audio_policy_device *device,
                            struct audio_policy_service_ops *aps_ops,
                            void *service,
                            struct audio_policy **ap)
{

。。。。

    lap->policy.set_can_mute_enforced_audible =
        ap_set_can_mute_enforced_audible;
    lap->policy.init_check = ap_init_check;
    lap->policy.get_output = ap_get_output;
    lap->policy.start_output = ap_start_output;
    lap->policy.stop_output = ap_stop_output;
    lap->policy.release_output = ap_release_output;
    lap->policy.get_input = ap_get_input;
    lap->policy.start_input = ap_start_input;
    lap->policy.stop_input = ap_stop_input;
    lap->policy.release_input = ap_release_input;
    lap->policy.init_stream_volume = ap_init_stream_volume;
    lap->policy.set_stream_volume_index = ap_set_stream_volume_index;
    lap->policy.get_stream_volume_index = ap_get_stream_volume_index;
    lap->policy.get_strategy_for_stream = ap_get_strategy_for_stream;
    lap->policy.get_devices_for_stream = ap_get_devices_for_stream;
    lap->policy.get_output_for_effect = ap_get_output_for_effect;
    lap->policy.register_effect = ap_register_effect;
    lap->policy.unregister_effect = ap_unregister_effect;
    lap->policy.set_effect_enabled = ap_set_effect_enabled;
    lap->policy.is_stream_active = ap_is_stream_active;
    lap->policy.dump = ap_dump;


    lap->aps_ops = aps_ops;
    lap->service_client =
        new AudioPolicyCompatClient(aps_ops, service);

    if (!lap->service_client) {
        ret = -ENOMEM;
        goto err_new_compat_client;
    }


    lap->apm = createAudioPolicyManager(lap->service_client);

    if (!lap->apm) {
        ret = -ENOMEM;
        goto err_create_apm;
    }


    *ap = &lap->policy;
    return 0;

}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

看到了吧,还是要继续挖,又是个函数指针

ap_set_stream_volume_index

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

static int ap_set_stream_volume_index(struct audio_policy *pol,
                                      audio_stream_type_t stream,
                                      int index)
{
    struct legacy_audio_policy *lap = to_lap(pol);
    return lap->apm->setStreamVolumeIndex((AudioSystem::stream_type)stream,
                                          index);

}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

嘿,这个lap是audio_policy转来的, 这是刚刚初始化 的那个东西啊

 lap->apm = createAudioPolicyManager(lap->service_client)

还记得吗,向上看20行。  那这apm是哪来的啊,继续挖。


---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface)
{
    return new AudioPolicyManager(clientInterface);
}


class AudioPolicyManager: public AudioPolicyManagerBase
{   


public:
                AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
                : AudioPolicyManagerBase(clientInterface) {}



        virtual ~AudioPolicyManager() {}


protected:
        // true is current platform implements a back microphone
        virtual bool hasBackMicrophone() const { return false; }
#ifdef WITH_A2DP
        // true is current platform supports suplication of notifications and ringtones over A2DP output
        virtual bool a2dpUsedForSonification() const { return true; }
#endif

};

竟然是这么单纯的类,继续看父累

原来实现都在父类里,mydroid/hardware/libhardware_legacy/audio/AudioPolicyManagerBase.cpp

这不就是GB当年的版本嘛。。

果然,看到我要找的函数

status_t AudioPolicyManagerBase::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
{
{


    if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) {
        return BAD_VALUE;
    }


    // Force max volume if stream cannot be muted
    if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax;


    LOGV("setStreamVolumeIndex() stream %d, index %d", stream, index);
    mStreams[stream].mIndexCur = index;


    // compute and apply stream volume on all outputs according to connected device
    status_t status = NO_ERROR;
    for (size_t i = 0; i < mOutputs.size(); i++) {
        status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), mOutputs.valueAt(i)->device());
        if (volStatus != NO_ERROR) {
            status = volStatus;
        }
    }
    return status;
}



status_t AudioPolicyManagerBase::checkAndSetVolume(int stream, int index, audio_io_handle_t output, uint32_t device, int delayMs, bool force)
{

    if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
            force) {
        mOutputs.valueFor(output)->mCurVolume[stream] = volume;
        LOGV("setStreamVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
        if (stream == AudioSystem::VOICE_CALL ||
            stream == AudioSystem::DTMF ||
            stream == AudioSystem::BLUETOOTH_SCO) {
            // offset value to reflect actual hardware volume that never reaches 0
            // 1% corresponds roughly to first step in VOICE_CALL stream volume setting (see AudioService.java)
            volume = 0.01 + 0.99 * volume;
            // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
            // enabled
            if (stream == AudioSystem::BLUETOOTH_SCO) {
                mpClientInterface->setStreamVolume(AudioSystem::VOICE_CALL, volume, output, delayMs);
            }
        }


        mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);
    }

}

继续mpClientInterface。。 这个是传进来的,与AudioPolicyManager 一起构造的

    lap->service_client =
        new AudioPolicyCompatClient(aps_ops, service);


class AudioPolicyCompatClient : public AudioPolicyClientInterface {
public:
    AudioPolicyCompatClient(struct audio_policy_service_ops *serviceOps,
                            void *service) :
            mServiceOps(serviceOps) , mService(service) {}

}

status_t AudioPolicyCompatClient::setStreamVolume(
                                             AudioSystem::stream_type stream,
                                             float volume,
                                             audio_io_handle_t output,
                                             int delayMs)
{
    return mServiceOps->set_stream_volume(mService, (audio_stream_type_t)stream,
                                          volume, output, delayMs);

}

阿,这个client就是个傀儡。。

绕了这麽久,其实这函数是传进来的。请别怪我绕这么个大弯,ICS就是这么干的

回过去,看AudioPolicyService, opt 是从这传进来的

    rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this,
                                               &mpAudioPolicy);

namespace {
    struct audio_policy_service_ops aps_ops = {
        open_output           : aps_open_output,
        open_duplicate_output : aps_open_dup_output,
        close_output          : aps_close_output,
        suspend_output        : aps_suspend_output,
        restore_output        : aps_restore_output,
        open_input            : aps_open_input,
        close_input           : aps_close_input,
        set_stream_volume     : aps_set_stream_volume,
        set_stream_output     : aps_set_stream_output,
        set_parameters        : aps_set_parameters,
        get_parameters        : aps_get_parameters,
        start_tone            : aps_start_tone,
        stop_tone             : aps_stop_tone,
        set_voice_volume      : aps_set_voice_volume,
        move_effects          : aps_move_effects,
#ifdef OMAP_ENHANCEMENT
        set_FMRxActive        : aps_set_FMRxActive,
#endif
    };
}; // namespace <unnamed>


static int aps_set_stream_volume(void *service, audio_stream_type_t stream,
                                     float volume, audio_io_handle_t output,
                                     int delay_ms)
{
    AudioPolicyService *audioPolicyService = (AudioPolicyService *)service;


    return audioPolicyService->setStreamVolume(stream, volume, output,
                                               delay_ms);

}

还是在audioPolicyService

int AudioPolicyService::setStreamVolume(audio_stream_type_t stream,
                                        float volume,
                                        audio_io_handle_t output,
                                        int delayMs)
{
    return (int)mAudioCommandThread->volumeCommand((int)stream, volume,
                                                   (int)output, delayMs);

}

终于看到点别的了, 这就是刚讲的两个thread, 这个是负责命令的。

status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream,
                                                               float volume,
                                                               int output,
                                                               int delayMs)
{
    status_t status = NO_ERROR;


    AudioCommand *command = new AudioCommand();
    command->mCommand = SET_VOLUME;

    VolumeData *data = new VolumeData();
    data->mStream = stream;
    data->mVolume = volume;
    data->mIO = output;
    command->mParam = data;
    if (delayMs == 0) {
        command->mWaitStatus = true;
    } else {
        command->mWaitStatus = false;
    }
    Mutex::Autolock _l(mLock);
    insertCommand_l(command, delayMs);
    LOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d",
            stream, volume, output);
    mWaitWorkCV.signal();
    if (command->mWaitStatus) {
        command->mCond.wait(mLock);
        status =  command->mStatus;
        mWaitWorkCV.signal();
    }
    return status;
}

加到了命令队列, 

bool AudioPolicyService::AudioCommandThread::threadLoop()
{

                case SET_VOLUME: {
                    VolumeData *data = (VolumeData *)command->mParam;
                    LOGV("AudioCommandThread() processing set volume stream %d, \
                            volume %f, output %d", data->mStream, data->mVolume, data->mIO);
                    command->mStatus = AudioSystem::setStreamVolume(data->mStream,
                                                                    data->mVolume,
                                                                    data->mIO);

                    if (command->mWaitStatus) {
                        command->mCond.signal();
                        mWaitWorkCV.wait(mLock);
                    }
                    delete data;
                    }break;

}

终于,我们要离开policyservices 了。

status_t AudioSystem::setStreamVolume(int stream, float value, int output)
{   
    if (uint32_t(stream) >= AUDIO_STREAM_CNT) return BAD_VALUE;
    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
    if (af == 0) return PERMISSION_DENIED;
    af->setStreamVolume(stream, value, output);
    return NO_ERROR;
}   
  

Audio Flinger, 终于看见真正干活的了。

status_t AudioFlinger::setStreamVolume(int stream, float value, int output)
{
    // check calling permissions
    if (!settingsAllowed()) {
        return PERMISSION_DENIED;
    }
    if (stream < 0 || uint32_t(stream) >= AUDIO_STREAM_CNT) {
        return BAD_VALUE;
    }

    AutoMutex lock(mLock);
    PlaybackThread *thread = NULL;
    if (output) {
        thread = checkPlaybackThread_l(output);
        if (thread == NULL) {
            return BAD_VALUE;
        }
    }

    mStreamTypes[stream].volume = value;

    if (thread == NULL) {
        for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) {
           mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);
        }
    } else {
        thread->setStreamVolume(stream, value);
    }
    return NO_ERROR;
}

还要写道子线程里。其实也是写到 mStreamTypes[stream].volume

好了这路先写到这,音量值已经写道audioflinger 内部的track


这个是统一音量,对于每种声音。 下面是另一个接口,是针对流的。



mydroid/frameworks/base/core/jni/android_media_AudioTrack.cpp

------------------------------------------------------------------------------------------------------------------------------


static void
android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol )
{
    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    if (lpTrack == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException",
            "Unable to retrieve AudioTrack pointer for setVolume()");
        return;
    }
    lpTrack->setVolume(leftVol, rightVol);
}


---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

JNI 函数直接调到AudioTrack.


---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

status_t AudioTrack::setVolume(float left, float right)
{
    if (left > 1.0f || right > 1.0f) {
        return BAD_VALUE;
    }


    AutoMutex lock(mLock);
    mVolume[LEFT] = left;
    mVolume[RIGHT] = right;


    // write must be atomic
    mCblk->volumeLR = (uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000);


    return NO_ERROR;
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

看见了吗,他直接把值付到了mCblk里了。  这里提一句,mCblk是用于共享内存与AudioTrack 和AudioFlinger之间的,AudioTrack 负责录入,AudioFlinger 作为服务

端起动了相对应的inner audiotrack 负责处理具体数据。  具体细节网上有很多文章,此处不在细说。


AudioTrack 该干的都干完了,然后就看AudioFlinger表现了~~~  希望读者明白audio的基本架构,AudioFlinger 在建立inner audiotrack时,为每一个track建了个线程,

放音一般为MixerThread. 


到这时,两种接口设置的音量都设完了,下面就看子线程的了 。 


---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

bool AudioFlinger::MixerThread::threadLoop()

{

....

mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);

....

 if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
            // mix buffers...
            if (outputsReady(outputTracks)) {
                 mAudioMixer->process();
            } else {
                memset(mMixBuffer, 0, mixBufferSize);
            }
            sleepTime = 0;
            writeFrames = mFrameCount;
        }

    }

}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

这两个函数为每个track子线程的核心函数:prepareTracks_l and process()

先来看prepareTracks_l


---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// prepareTracks_l() must be called with ThreadBase::mLock held
uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove)
{

//  Check each track....

           int param = AudioMixer::VOLUME;
            if (track->mFillingUpStatus == Track::FS_FILLED) {
                // no ramp for the first volume setting

// mFillingUpStatus 标志位用来避免ramp出现在 第一次声音配置
                track->mFillingUpStatus = Track::FS_ACTIVE;
                if (track->mState == TrackBase::RESUMING) {
                    track->mState = TrackBase::ACTIVE;
                    param = AudioMixer::RAMP_VOLUME;
                }

// 需要理解为什么设这两个,现在看起来像重置。

                mAudioMixer->setParameter(AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);
            } else if (cblk->server != 0) {
                // If the track is stopped before the first frame was mixed,
                // do not apply ramp
                param = AudioMixer::RAMP_VOLUME;
            }

这段代码主要负责计算音量
            // compute volume for this track
            uint32_t vl, vr, va;

      // 无声的情况
            if (track->isMuted() || track->isPausing() ||
                mStreamTypes[track->type()].mute) {
                vl = vr = va = 0;
                if (track->isPausing()) {
                    track->setPaused();

                }
            } else {

// 有声音
                // read original volumes with volume control

                // 这是我们第一个接口设置的类别型音量
                float typeVolume = mStreamTypes[track->type()].volume;

                // masterVolume  是实际值,typeVolume 和track volume是百分比。

                float v = masterVolume * typeVolume;
                vl = (uint32_t)(v * cblk->volume[0]) << 12;
                vr = (uint32_t)(v * cblk->volume[1]) << 12;


                va = (uint32_t)(v * cblk->sendLevel);
            }
            // Delegate volume control to effect in track effect chain if needed
            if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
                // Do not ramp volume if volume is controlled by effect
                param = AudioMixer::VOLUME;
                track->mHasVolumeController = true;
            } else {
                // force no volume ramp when volume controller was just disabled or removed
                // from effect chain to avoid volume spike
                if (track->mHasVolumeController) {
                    param = AudioMixer::VOLUME;

                }
                track->mHasVolumeController = false;
            }

            // 这两种格式没听说过,希望有人知道可以指教。
            // Convert volumes from 8.24 to 4.12 format
            int16_t left, right, aux;
            uint32_t v_clamped = (vl + (1 << 11)) >> 12;
            if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
            left = int16_t(v_clamped);
            v_clamped = (vr + (1 << 11)) >> 12;
            if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT;
            right = int16_t(v_clamped);


            if (va > MAX_GAIN_INT) va = MAX_GAIN_INT;
            aux = int16_t(va);


            // XXX: these things DON'T need to be done each time
            mAudioMixer->setBufferProvider(track);
            mAudioMixer->enable(AudioMixer::MIXING);


最终设置左右音量到mixer 里
            mAudioMixer->setParameter(param, AudioMixer::VOLUME0, (void *)left);
            mAudioMixer->setParameter(param, AudioMixer::VOLUME1, (void *)right);

            mAudioMixer->setParameter(param, AudioMixer::AUXLEVEL, (void *)aux);

}

Mixer 处理

    case RAMP_VOLUME:
    case VOLUME:
        if ((uint32_t(name-VOLUME0) < MAX_NUM_CHANNELS)) {
            track_t& track = mState.tracks[ mActiveTrack ];
            if (track.volume[name-VOLUME0] != valueInt) {
                LOGV("setParameter(VOLUME, VOLUME0/1: %04x)", valueInt);
                track.prevVolume[name-VOLUME0] = track.volume[name-VOLUME0] << 16;
                track.volume[name-VOLUME0] = valueInt;
                if (target == VOLUME) {
                    track.prevVolume[name-VOLUME0] = valueInt << 16;
                    track.volumeInc[name-VOLUME0] = 0;
                } else {
                    int32_t d = (valueInt<<16) - track.prevVolume[name-VOLUME0];
                    int32_t volInc = d / int32_t(mState.frameCount);
                    track.volumeInc[name-VOLUME0] = volInc;
                    if (volInc == 0) {
                        track.prevVolume[name-VOLUME0] = valueInt << 16;
                    }
                }
                invalidateState(1<<mActiveTrack);
            }
            return NO_ERROR;
        } else if (name == AUXLEVEL) {
            track_t& track = mState.tracks[ mActiveTrack ];
            if (track.auxLevel != valueInt) {
                LOGV("setParameter(VOLUME, AUXLEVEL: %04x)", valueInt);
                track.prevAuxLevel = track.auxLevel << 16;
                track.auxLevel = valueInt;
                if (target == VOLUME) {
                    track.prevAuxLevel = valueInt << 16;
                    track.auxInc = 0;
                } else {
                    int32_t d = (valueInt<<16) - track.prevAuxLevel;
                    int32_t volInc = d / int32_t(mState.frameCount);
                    track.auxInc = volInc;
                    if (volInc == 0) {
                        track.prevAuxLevel = valueInt << 16;
                    }
                }
                invalidateState(1<<mActiveTrack);
            }
            return NO_ERROR;
        }
        break;


-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

然后是process, 这是个指针, 在计算输出声音时,音量的因素已经加里了。一系列专业的音频换算,音量的因素就加里了。


这是Android 在ICS里的音量控制方式,基本和GB 区别不大,就是更饶了。  如果是做ALSA的同学,肯定奇怪了,底层明明也提供了一系列音量

配置接口,和Android这个没关系吗? 确实没关系。


我是做TI平台的,举个TI的例子 :

static void select_output_device(struct omap_audio_device *adev)
{

     set_output_volumes(adev);

}

static void set_output_volumes(struct omap_audio_device *adev)
{
    unsigned int channel;
    int speaker_volume;
    int headset_volume;


    speaker_volume = adev->mode == AUDIO_MODE_IN_CALL ? VOICE_CALL_SPEAKER_VOLUME :
                                                        NORMAL_SPEAKER_VOLUME;
    headset_volume = adev->devices & AUDIO_DEVICE_OUT_WIRED_HEADSET ?
                                                        HEADSET_VOLUME :
                                                        HEADPHONE_VOLUME;


    for (channel = 0; channel < 2; channel++) {
        mixer_ctl_set_value(adev->mixer_ctls.speaker_volume, channel,
            DB_TO_SPEAKER_VOLUME(speaker_volume));
        mixer_ctl_set_value(adev->mixer_ctls.headset_volume, channel,
            DB_TO_HEADSET_VOLUME(headset_volume));
    }
}

看到了吧,speaker volume和headset volume 都是写死的。


希望对大家有所帮助。如果有问题,或是有错误,敬请指教。





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
select t.id ,t.parent, t.name ,t.begin ,t.end , t.ACTUAL_START , t.ACTUAL_FINISH, t.TASK_UNIQUE_NO, t.NO, t.SUMMARY, t.DEPENDENCE, t.PRIORITY, t.EXEC_STAT, t.DURATION, t.COMP_PCT,ASSIGNER,POSITION,PRINCIPAL,PRINCIPAL_NAME,ORG_NAME,MGR_LINE,ERJIGUANXIAN,SFSJYS,SFNDJH, t.CRITICAL,t.PROJ_NO,t.SRC_TID,t.ASSIGNER_AUTH,t.POSITION_NAME,t.ASSIGNER_NAME,t.PRIN_ORG,t.ORG,t.SRC_SYS,t.CREATE_USER, t.TASK_NO,tp.id as typ,tp.name as typname,t.SETTLETYPECODE as SETTLETYPECODE,'' as remark,'' as type,t.OATASKID as OATASKID,t.QIQU,t.DESCRIBE, S.DESCRIPTION,ontheway_type,T.SHIFOUXXJDHB,up.update_date optime,submit,t.AUDITOR,t.AUDITOR_NO,t.proj_name,tp.filter_type,t.plan,t.MATTER_SRC,t.Prepose_task,t.shixiangbiaoqian, t.SETTLE_LEVEL,tp.specail_name,t.audit_unit,t.rectify_question_type,t.project_data_processor,t.project_data_processor_name, CASE WHEN t.EXEC_STAT not IN ('30','31','32','33','34','41','55') and ( up.STAT = 'OP' or up.STAT = 'I') then 'relay' when t.EXEC_STAT not IN ('30','31','32','33','34','41','55') and up.STAT = 'U' and up.FINISH ='Y' and up.rel = '1' AND up.RID is not null then 'finish' when t.EXEC_STAT not IN ('30','31','32','33','34','41','55') and up.STAT = 'U' and up.DELAY ='Y' and up.rel = '1' AND up.RID is not null then 'delay' when t.EXEC_STAT not IN ('30','31','32','33','34','41','55') and up.STAT = 'U' and up.TERMINATE ='P' and up.rel = '1' AND up.RID is not null then 'terminate' else null end as approve_type, up.user_id as approver from t_master_task t left join t_task_2_task_typ p on t.id = p.task left join t_task_typ tp on tp.id = p.typ left join t_slave_task s on s.id=t.id left join (select * from (select a.*,row_number() over(partition by task order by update_date desc) rm from t_user_task_upd_stat a) where rm = 1) up on t.id = up.task; 这sql怎么优化
02-07

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值