mtk android 4.4 audio framework 代码分析(未完成)

2023-05-16

mtk android 4.4 audio framework 代码分析(未完成),有需要的朋友可以参考下。


mtk android 4.4 audio framework 代码分析(未完成)

2/28/2015 3:01:24 PM

关于 audio_policy.conf 文件

这个文件 mediatek/config/$project 下, 在 audiomtkpolicymanager.cpp 中解析, 
解析出如下信息:

 1)  ATTACHED_OUTPUT_DEVICES_TAG "attached_output_devices" ,对应 类定义中的变量 mAttachedOutputDevices
 2 ) #define DEFAULT_OUTPUT_DEVICE_TAG "default_output_device"

对应类定义中的 mDefaultOutputDevice

#define ATTACHED_INPUT_DEVICES_TAG "attached_input_devices"

对应类定义中的 mAvailableInputDevices

    mHasA2dp = true; 根据文件解析出是否有此MODULE。
    mHasUsb = true; 根据文件接触出是否有此MODULE。
    mHasRemoteSubmix = true; 根据文件接触出是否有此MODULE。

3) 
最重要的解析出 mHwModules,而这个变量的定义在audiomtkpolicymanager.h 中,Vector

APS构造函数分析:

上面关于文件解析的东西其实也属于本节的内容,不过还是单独出去了。

-》mpClientInterface = clientInterface; 就是APS
-》  AudioMTKPolicyManager::LoadCustomVolume
-》GetVolumeVer1ParamFromNV 从NVRAM里读取参数,这个暂且不表。
-》initializeVolumeCurves();  // 初始化VOLUME曲线,SETVOLUME时会用到,以后分析。
-》if (loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE) != NO_ERROR) 

上面已经分析了,解析配置文件。

-》mHwModules[i]->mHandle = mpClientInterface->loadHwModule(mHwModules[i]->mName); 
mHandle 是 audio_module_handle_t类型,实际上是 AF中成员变量DefaultKeyedVector

AudioMTKPolicyManager::setOutputDevice 分析

原型: 
uint32_t AudioMTKPolicyManager::setOutputDevice(audio_io_handle_t output, 
audio_devices_t device, 
bool force, 
int delayMs)

-》

AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output); 根据传入参数取得 outputDesc 

-》

if (outputDesc->isDuplicated()) {  // 蓝牙,暂且不分析
    muteWaitMs = setOutputDevice(outputDesc->mOutput1->mId, device, force, delayMs);
    muteWaitMs += setOutputDevice(outputDesc->mOutput2->mId, device, force, delayMs);
    return muteWaitMs;
}  

-》

if (device != AUDIO_DEVICE_NONE) {
    outputDesc->mDevice = device;
}   // 设置 outputDesc  route to  传入的 device. 软件层面上的。

-》

muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevice, delayMs);

-》

param.addInt(String8(AudioParameter::keyRouting), (int)device);  设置keyroute 的PARA,

-》

mpClientInterface->setParameters(output, param.toString(), delayMs);  APS cmd thread 切换。 

-》 // update stream volumes according to new device

applyStreamVolumes(output, device, delayMs);  
分析见下面。
设备路由:
mpClientInterface->setParameters(output, param.toString(), delayMs);  APS cmd thread 切换。
定义 void AudioPolicyService::setParameters(audio_io_handle_t ioHandle,
                                       const char *keyValuePairs,
                                       int delayMs)
{
    mAudioCommandThread->parametersCommand(ioHandle, keyValuePairs,
                                           delayMs);
}



-》 AudioCommand *command = new AudioCommand();
-》 insertCommand_l(command, delayMs);
-》 AudioCommandThread::threadLoop()
-》case SET_PARAMETERS:  AudioSystem::setParameters
-》 af->setParameters
-》 thread = checkPlaybackThread_l(ioHandle); 找到 相应的 thread
-》 thread->setParameters(keyValuePairs);  
-》 ThreadBase::setParameters(const String8& keyValuePairs)
-》 mNewParameters.add(keyValuePairs);  
Vector<String8>         mNewParameters 是 ThreadBase的成员变量。
-》 PlaybackThread::threadLoop()
-》 status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
                                                    keyValuePair.string());

mOutput 是AudioStreamOut 类型:

struct AudioStreamOut {
    AudioHwDevice* const audioHwDev;
    audio_stream_out_t* const stream;

    audio_hw_device_t* hwDev() const { return audioHwDev->hwDevice(); }

    AudioStreamOut(AudioHwDevice *dev, audio_stream_out_t *out) :
        audioHwDev(dev), stream(out) {}
};

-》 out->stream.common.set_parameters = out_set_parameters; HAL层

-》 status_t AudioMTKStreamOut::setParameters(const String8 &keyValuePairs)

{
AudioParameter param = AudioParameter(keyValuePairs);
String8 keyRouting = String8(AudioParameter::keyRouting);
status_t status = NO_ERROR;
int devices = 0;
ALOGD("setParameters() %s", keyValuePairs.string());
if (param.getInt(keyRouting, devices) == NO_ERROR) {
    param.remove(keyRouting);
    dokeyRouting(devices);
    mAudioResourceManager->doSetMode();
}
if (param.size()) {
    status = BAD_VALUE;
}
return status;
}

-》 AudioMTKStreamOut::dokeyRouting(uint32_t new_device)

-》 mAudioResourceManager->SelectOutputDevice(new_device);

-》 AudioResourceManager::SelectOutputDevice(uint32_t new_device)

 AudioResourceManager::SelectOutputDevice 分析

pre_device = mDlOutputDevice;

-》 StopOutputDevice(); // 关掉 mDlOutputDevice,

-》 mDlOutputDevice = new_device; 设置NEW device。

-》 AudioResourceManager::StartOutputDevice()

AudioResourceManager::StartOutputDevice() 分析

定义:

switch (mAudioMode) {
    case AUDIO_MODE_NORMAL:
    case AUDIO_MODE_RINGTONE: {
        TurnonAudioDevice(mDlOutputDevice);
        break;
    }
    case AUDIO_MODE_IN_CALL:
    case AUDIO_MODE_IN_CALL_2: {
        TurnonAudioDeviceIncall(mDlOutputDevice);
        break;
    }

    case AUDIO_MODE_IN_COMMUNICATION: {
        TurnonAudioDevice(mDlOutputDevice);
        break;
    }
}

-》 AudioResourceManager::TurnonAudioDevice(unsigned int mDlOutputDevice)

-》 mAudioAnalogInstance->AnalogOpen(AudioAnalogType::DEVICE_OUT_EARPIECER, 
AudioAnalogType::DEVICE_PLATFORM_MACHINE); 打开对应的设备。

-》 AudioAnalogControl::AnalogOpen 
定义:

// analog open power , need to open by mux setting
status_t AudioAnalogControl::AnalogOpen(AudioAnalogType::DEVICE_TYPE DeviceType,    AudioAnalogType::DEVICE_TYPE_SETTING Type_setting)
{
ALOGD("AnalogOpen DeviceType = %s", kAudioAnalogDeviceTypeName[DeviceType]);
CheckDevicePolicy((uint32*)&DeviceType,AudioAnalogType::AUDIOANALOG_DEVICE);
mBlockAttribute[DeviceType].mEnable = true;
mAudioPlatformDevice->AnalogOpen(DeviceType);  直接操作KERNEL 接口,寄存器。
mAudioMachineDevice->AnalogOpen(DeviceType);
return NO_ERROR;
}
音量调节 AudioMTKPolicyManager::applyStreamVolumes 分析
void applyStreamVolumes(audio_io_handle_t output, audio_devices_t device, int delayMs = 0, bool force = false);   //  注意调用此传入的参数。

-》 
for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { 
checkAndSetVolume(stream, 
mStreams[stream].getVolumeIndex(device), 
output, 
device, 
delayMs, 
force); 
}

-》传入的参数 StreamDescriptor mStreams[AudioSystem::NUM_STREAM_TYPES]; // stream descriptors for volume control 定义, 实际上类似于一个二维数组,STREAM是第一维, DEVICE是第二维。 上述代码实际上是取出音量的index(int).

class StreamDescriptor
    {
    public:
        StreamDescriptor();

        int getVolumeIndex(audio_devices_t device);
        void dump(int fd);

        int mIndexMin;      // min volume index
        int mIndexMax;      // max volume index
        KeyedVector<audio_devices_t, int> mIndexCur;   // current volume index per device
        bool mCanBeMuted;   // true is the stream can be muted

        const VolumeCurvePoint *mVolumeCurve[DEVICE_CATEGORY_CNT];
        #ifdef MTK_AUDIO
        float mIndexRange;
        #endif
    };
checkAndSetVolume 分析

AudioMTKPolicyManager::checkAndSetVolume 
定义:

status_t checkAndSetVolume(int stream, int index, audio_io_handle_t output, audio_devices_t device, int delayMs = 0, bool force = false); //  注意 后面2个参数。

-》

if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) { 
    ALOGV("checkAndSetVolume() stream %d muted count %d",
          stream, mOutputs.valueFor(output)->mMuteCount[stream]);
    return NO_ERROR;
}  do not change actual stream volume if the stream is muted

-》

 // do not change in call volume if bluetooth is connected and vice versa
if ((stream == AudioSystem::VOICE_CALL && mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
    (stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) {
    ALOGD("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm",
         stream, mForceUse[AudioSystem::FOR_COMMUNICATION]);
    return INVALID_OPERATION;
}

-》

float volume = computeVolume(stream, index, output, device);  // 计算音量。 后面详细分析、

-》

 //for VT notify tone when incoming call. it's volume will be adusted in hardware.
 if((stream == AudioSystem::VOICE_CALL ||stream == AudioSystem::BLUETOOTH_SCO) && mOutputs.valueFor(output)->mRefCount[stream]!=0 && mPhoneState==AudioSystem::MODE_IN_CALL)
 {
    volume =1.0;
 }   处理特列 MODE_IN_CALL

-》

 // ALPS00554824 KH: If notifiaction is exist, FM should be mute
 if ((stream == AudioSystem::FM) &&
       (mOutputs.valueFor(output)->mRefCount[AudioSystem::NOTIFICATION]
         || mOutputs.valueFor(output)->mRefCount[AudioSystem::RING]
         || mOutputs.valueFor(output)->mRefCount[AudioSystem::ALARM]))
 {
    volume =0.0;
 }  

-》

if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
        force)    the float value returned by computeVolume() changed
// - the force flag is set ,两者有一个条件满足则可以 调节音量。

-》

mOutputs.valueFor(output)->mCurVolume[stream] = volume;   
float mCurVolume[AudioSystem::NUM_STREAM_TYPES];   // current stream volume,更新软件音量。

-》

mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output, delayMs);   //aps  set volume

-》

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

-》

 AudioCommandThread::volumeCommand

-》

AudioCommand *command = new AudioCommand();

-》

insertCommand_l(command, delayMs);

-》

 mAudioCommands.insertAt(command, i + 1);   加入到mAudioCommands的  CMD容器中。

-》

 AudioCommandThread::threadLoop
while (!mAudioCommands.isEmpty()) {
        nsecs_t curTime = systemTime();
        // commands are sorted by increasing time stamp: execute them from index 0 and up
        if (mAudioCommands[0]->mTime <= curTime) {
// 当mAudioCommands 不为空,时间来到,

-》

CASE  SET_VOLUME: {
                VolumeData *data = (VolumeData *)command->mParam;
                ALOGV("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);   //  

-》

af->setStreamVolume(stream, value, output);   下面分析

### AudioFlinger::setStreamVolume 分析 ###
status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value,
    audio_io_handle_t output)   定义 

-》

thread = checkPlaybackThread_l(output);

-》

 mStreamTypes[stream].volume = value;   设置软件音量 
stream_type_t  mStreamTypes[AUDIO_STREAM_CNT + 1];  AF中音量表示方法。

-》

thread->setStreamVolume(stream, value);   后面分析

-》

 if(stream == AUDIO_STREAM_FM)
{
    MTK_ALOG_D("setStreamVolume FM  value = %f",value);
#if defined(MT5192_FM) || defined(MT5193_FM)
    int FmVolume = (AudioSystem::logToLinear(value));
    char Volume[30];
    sprintf(Volume,"SetFmVolume=%d",FmVolume);
    String8 Key = String8(Volume);
#else
    int FmVolume = (AudioSystem::logToLinear(value)>>4);
    char Volume[30];
    sprintf(Volume,"SetFmVolume=%d",FmVolume);
    String8 Key = String8(Volume);
#endif
    audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
    dev->set_parameters (dev,Key);
}   如果是stream 是FM, 则直接dev->set_parameters (dev,Key); 设置硬件VOLUME.

### PlaybackThread::setStreamVolume 分析 ###
struct  stream_type_t {   
    stream_type_t()
        :   volume(1.0f),
            mute(false)
    {
    }
    float       volume;
    bool        mute;
};  // AF中  和  PLAYBACKTHREAD 中都有个这样的。
定义 void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{
    Mutex::Autolock _l(mLock);
    mStreamTypes[stream].volume = value;
}
插入耳机路由切换过程 WiredAccessoryManager ###

构造函数中 mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); 
-》 
mObserver = new WiredAccessoryObserver(); 
-》 
init 中 public String getSwitchStatePath() { 
return String.format(“/sys/class/switch/%s/state”, mDevName); 

-》 
mAudioManager.setWiredDeviceConnectionState(device, state, headsetName); 
-》 
AudioService中 public void setWiredDeviceConnectionState(int device, int state, String name) { 
synchronized (mConnectedDevices) { 
int delay = checkSendBecomingNoisyIntent(device, state); 
queueMsgUnderWakeLock(mAudioHandler, 
MSG_SET_WIRED_DEVICE_CONNECTION_STATE, 
device, 
state, 
name, 
delay); 


-》 
checkSendBecomingNoisyIntent 中 
sendMsg(mAudioHandler , MSG_BROADCAST_AUDIO_BECOMING_NOISY, 
SENDMSG_REPLACE, 
0, 
0, 
null, 
0); 
delay = 1000; 
Senmsg在handler中处理

sendBroadcastToAll(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
这个广播哪些地方有处理,举例子:MUSIC应用中

public void onReceive(Context context, Intent intent) {
    String intentAction = intent.getAction();
    MusicLogUtils.d("MediaButtonIntentReceiver", "intentAction " + intentAction);
    if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intentAction)) {

-》 
i.setAction(MediaPlaybackService.SERVICECMD); 
i.putExtra(MediaPlaybackService.CMDNAME, MediaPlaybackService.CMDPAUSE); 
context.startService(i); 
-》 
在 mediaplayerservice 中处理 
else if (CMDPAUSE.equals(cmd) || PAUSE_ACTION.equals(action) 
|| AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(action)) { 
pause(); 
mPausedByTransientLossOfFocus = false; 

-》 audioservice 中

case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
                onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj);

-》 
private void onSetWiredDeviceConnectionState(int device, int state, String name) 

synchronized (mConnectedDevices) { 
Log.d(TAG,”onSetWiredDeviceConnectionState:” + “device:” + device + “,state:” + state); 
if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||(device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) { // 耳机 
/// M: Change for sound output from device when a2dp conneted @ { 
//setBluetoothA2dpOnInt(true); AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,AudioSystem.FORCE_NONE); 
///@} 

boolean isUsb = ((device & AudioSystem.DEVICE_OUT_ALL_USB) != 0); 
handleDeviceConnection((state == 1), device, (isUsb ? name : “”)); 
if (state != 0) { 
if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) || 
(device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE)) { 
setBluetoothA2dpOnInt(false); 

if ((device & mSafeMediaVolumeDevices) != 0) { 
sendMsg(mAudioHandler, 
MSG_CHECK_MUSIC_ACTIVE, 
SENDMSG_REPLACE, 
0, 
0, 
null, 
MUSIC_ACTIVE_POLL_PERIOD_MS); 


if (!isUsb) { 
sendDeviceConnectionIntent(device, state, name); 


}

AudioSystem.setForceUse 分析 handleDeviceConnection(audioservice) 分析

boolean isUsb = ((device & AudioSystem.DEVICE_OUT_ALL_USB) != 0); 
handleDeviceConnection((state == 1), device, (isUsb ? name : “”)); 
下面是定义:

private boolean handleDeviceConnection(boolean connected, int device, String params) {
        synchronized (mConnectedDevices) {
            boolean isConnected = (mConnectedDevices.containsKey(device) &&
                    (params.isEmpty() || mConnectedDevices.get(device).equals(params)));
            Log.d(TAG,"handleDeviceConnection:isConnected" + isConnected);

            if (isConnected && !connected) { 、、 拔出
                AudioSystem.setDeviceConnectionState(device,  设置状态
                              AudioSystem.DEVICE_STATE_UNAVAILABLE,
                                              mConnectedDevices.get(device));
             Log.d(TAG,"handleDeviceConnection remove:" + "connected:" + connected + ",device:" + device);
                 mConnectedDevices.remove(device);  移除设备。
                 return true;
            } else if (!isConnected && connected) {
                 AudioSystem.setDeviceConnectionState(device,
                                                      AudioSystem.DEVICE_STATE_AVAILABLE,
                                                      params);
             Log.d(TAG,"handleDeviceConnection connect:" + "connected:" + connected + ",device:" + device);
                 mConnectedDevices.put(new Integer(device), params);
                 return true;
            }
        }
        return false;
}

-》

AudioSystem::setDeviceConnectionState

-》

aps->setDeviceConnectionState(

-》

AudioMTKPolicyManager::setDeviceConnectionState(audio_devices_t device,
AudioSystem::device_connection_state state,const char *device_address)

-》

case AudioSystem::DEVICE_STATE_AVAILABLE:
if (checkOutputsForDevice(device, state, outputs) != NO_ERROR) {
                return INVALID_OPERATION;
}
            ALOGD("setDeviceConnectionState() checkOutputsForDevice() returned %d outputs",
                  outputs.size());
            // register new device as available
            mAvailableOutputDevices = (audio_devices_t)(mAvailableOutputDevices | device);     输出设备。

 checkOutputForAllStrategies();  更新strategy
if ((state == AudioSystem::DEVICE_STATE_UNAVAILABLE) ||
                        (mOutputs.valueFor(outputs[i])->mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {
                    closeOutput(outputs[i]);   关掉 output
                } 
 updateDevicesAndOutputs();  
for (size_t i = 0; i < mOutputs.size(); i++) {
            // do not force device change on duplicated output because if device is 0, it will
            // also force a device 0 for the two outputs it is duplicated to which may override
            // a valid device selection on those outputs.
            setOutputDevice(mOutputs.keyAt(i),
                            getNewDevice(mOutputs.keyAt(i), true /*fromCache*/),
                            !mOutputs.valueAt(i)->isDuplicated(),
                            0);
        }

### AudioMTKPolicyManager::checkOutputForAllStrategies() 分析 ###

-》

调用  AudioMTKPolicyManager::checkOutputForStrategy(routing_strategy strategy)

-》

audio_devices_t oldDevice = getDeviceForStrategy(strategy, true /*fromCache*/);
    audio_devices_t newDevice = getDeviceForStrategy(strategy, false /*fromCache*/);
    SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevice(oldDevice, mPreviousOutputs);
SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevice(newDevice, mOutputs);

-》

if (desc->strategyRefCount(strategy) != 0) {  //   
            #ifdef MTK_AUDIO    //ALPS00446176 .ex: Speaker->Speaker,Don't move track and mute. Only change to dstOutputs[0]
            if(dstOutputs[0]!=srcOutputs[i])  如果现在的dstOutputs不在 srcOutputs中
            {
                setStrategyMute(strategy, true, srcOutputs[i]); // 立即MUTE 
                setStrategyMute(strategy, false, srcOutputs[i], MUTE_TIME_MS, newDevice);  // 2秒后unmute  newDevice
            }
            #else
            setStrategyMute(strategy, true, srcOutputs[i]);
            setStrategyMute(strategy, false, srcOutputs[i], MUTE_TIME_MS, newDevice);
            #endif

        }

-》

AudioMTKPolicyManager::setStrategyMute   

-》

setStreamMute(stream, on, output, delayMs, device);   

-》

AudioMTKPolicyManager::setStreamMute(int stream,
                                       bool on,
                                       audio_io_handle_t output,
                                       int delayMs,
                                       audio_devices_t device)

-》

if (on) { //  mute on
        if (outputDesc->mMuteCount[stream] == 0) {
            if (streamDesc.mCanBeMuted &&
                    ((stream != AudioSystem::ENFORCED_AUDIBLE) ||
                     (mForceUse[AudioSystem::FOR_SYSTEM] == AudioSystem::FORCE_NONE))) {
                checkAndSetVolume(stream, 0, output, device, delayMs);
            }
        }
        // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
        outputDesc->mMuteCount[stream]++;
}
checkAndSetVolume // 上面已经分析过了,最后会设置音量到 af对应的threads
接上面函数继续分析

-》

// Move tracks associated to this strategy from previous output to new output
    for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
        if (getStrategy((AudioSystem::stream_type)i) == strategy) {
            //FIXME see fixme on name change
            mpClientInterface->setStreamOutput((AudioSystem::stream_type)i,
                                               dstOutputs[0] /* ignored */);
        }
    }

-》

status_t AudioPolicyCompatClient::setStreamOutput(AudioSystem::stream_type stream,
                                                  audio_io_handle_t output)
{
    return mServiceOps->set_stream_output(mService, (audio_stream_type_t)stream,
                                          output);
}

-》

aps: set_stream_output

-》

af->setStreamOutput

-》

status_t AudioFlinger::setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output)
{
    Mutex::Autolock _l(mLock);
    ALOGV("setStreamOutput() stream %d to output %d", stream, output);

    for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
        PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
        thread->invalidateTracks(stream);
    }

    return NO_ERROR;
}

-》

PlaybackThread::cacheParameters_l()

-》

PlaybackThread:  t->mCblk->cv.signal(); 发信号, 哪里在等还没找到。


uint32_t mRefCount[AudioSystem::NUM_STREAM_TYPES]; // number of streams of each type using this output



DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mPreviousOutputs;

Audioservice中重要变量:

private final HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
AudioTrack 跟踪
AudioTrack.java
blic AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
            int bufferSizeInBytes, int mode, int sessionId)

-》 
int initResult = native_setup(new WeakReference(this), 
mStreamType, mSampleRate, mChannels, mAudioFormat, 
mNativeBufferSizeInBytes, mDataLoadMode, session);

-》

android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
        jint streamType, jint sampleRateInHertz, jint javaChannelMask,
        jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession)
sp<AudioTrack> lpTrack = new AudioTrack();

-》

## AudioFlinger::openOutput  分析 ##
DefaultKeyedVector< audio_io_handle_t, sp<PlaybackThread> >  mPlaybackThreads;
在 AudioFlinger::openOutput  中 返回的就是这个audio_io_handle_t  KEY值。 

AudioFlinger::openOutput 

->

outHwDev = findSuitableHwDev_l(module, *pDevices); // 找到  AudioHwDevice, 对应HAL 下的一个设备。

-> 
status = hwDevHal->open_output_stream(hwDevHal, 
id, 
*pDevices, 
(audio_output_flags_t)flags, 
&config, 
&outStream); 
outStream 是在HAL层分配的,然后返回到AF中。

-》

AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream);  // 用上述2个NEW一个 AudioStreamOut。

->

if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||
            (config.format != AUDIO_FORMAT_PCM_16_BIT) ||
            (config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) {
            thread = new DirectOutputThread(this, output, id, *pDevices);
            ALOGV("openOutput() created direct output: ID %d thread %p", id, thread);
        } else {
            thread = new MixerThread(this, output, id, *pDevices);
            ALOGV("openOutput() created mixer output: ID %d thread %p", id, thread);
附录LOG:(开机过程中)
Line 2397: 01-01 03:59:20.971207   211   211 D AudioFlinger: openOutput(), module 1 Device 2, SamplingRate 44100, Format 0x000003, Channels 3, flags 2
Line 2398: 01-01 03:59:20.971246   211   211 D AudioFlinger: openOutput(), offloadInfo 0x0 version 0xffffffff
Line 2399: 01-01 03:59:20.971291   211   211 D AudioALSAStreamManager: +openOutputStream()
Line 2888: 01-01 03:59:21.028671   211   211 D AudioALSAStreamManager: -openOutputStream(), out = 0xb7ba8250, status = 0x0, mStreamOutVector.size() = 1
Line 2889: 01-01 03:59:21.028791   211   211 D AudioFlinger: openOutput() openOutputStream returned output 0xb7ba81e0, SamplingRate 44100, Format 0x000003, Channels 3, status 0, flags 2
Line 2889: 01-01 03:59:21.028791   211   211 D AudioFlinger: openOutput() openOutputStream returned output 0xb7ba81e0, SamplingRate 44100, Format 0x000003, Channels 3, status 0, flags 2
Line 2905: 01-01 03:59:21.048650   211   211 D AudioFlinger: openOutput() created mixer output: ID 2 thread 0xb4b26008
(在 AudioFlinger 构造函数中初始化了 mNextUniqueId(1) )

-》

AudioTrack::AudioTrack(
        audio_stream_type_t streamType,
        uint32_t sampleRate,
        audio_format_t format,
        audio_channel_mask_t channelMask,
        int frameCount,
        audio_output_flags_t flags,
        callback_t cbf,
        void* user,
        int notificationFrames,
        int sessionId,
        transfer_type transferType,
        const audio_offload_info_t *offloadInfo,
        int uid)
    : mStatus(NO_INIT),
      mIsTimed(false),
      mPreviousPriority(ANDROID_PRIORITY_NORMAL),
      mPreviousSchedulingGroup(SP_DEFAULT),
      mPausedPosition(0)
{
    mStatus = set(streamType, sampleRate, format, channelMask,
            frameCount, flags, cbf, user, notificationFrames,
            0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType,
            offloadInfo, uid);
}

    }
    mPlaybackThreads.add(id, thread);

-》

audio_io_handle_t output = AudioSystem::getOutput(
                                    streamType,
                                    sampleRate, format, channelMask,
                                    flags,
                                    offloadInfo);
if (cbf != NULL) {
        mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
        mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);
    }

    // create the IAudioTrack
    status_t status = createTrack_l(streamType,
                                  sampleRate,
                                  format,
                                  frameCount,
                                  flags,
                                  sharedBuffer,
                                  output,
                                  0 /*epoch*/);

-》

sp<IAudioTrack> track = audioFlinger->createTrack(streamType,
                                                  sampleRate,
                                                  // AudioFlinger only sees 16-bit PCM
                                                  format == AUDIO_FORMAT_PCM_8_BIT ?
                                                          AUDIO_FORMAT_PCM_16_BIT : format,
                                                  mChannelMask,
                                                  frameCount,
                                                  &trackFlags,
                                                  sharedBuffer,
                                                  output,
                                                  tid,
                                                  &mSessionId,
                                                  mName,
                                                  mClientUid,
                                                  &status);

-》

track = thread->createTrack_l(client, streamType, sampleRate, format,
            channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, clientUid, &lStatus);

-》

// PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held
sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(
    const sp<AudioFlinger::Client>& client,
    audio_stream_type_t streamType,
    uint32_t sampleRate,
    audio_format_t format,
    audio_channel_mask_t channelMask,
    size_t frameCount,
    const sp<IMemory>& sharedBuffer,
    int sessionId,
    IAudioFlinger::track_flags_t *flags,
    pid_t tid,
    int uid,
    status_t *status)
{
sp<Track> track;
status_t lStatus;

bool isTimed = (*flags & IAudioFlinger::TRACK_TIMED) != 0;

// client expresses a preference for FAST, but we get the final say
if (*flags & IAudioFlinger::TRACK_FAST) {
  if (
        // not timed
        (!isTimed) &&
        // either of these use cases:
        (
          // use case 1: shared buffer with any frame count
          (
            (sharedBuffer != 0)
          ) ||
          // use case 2: callback handler and frame count is default or at least as large as HAL
          (
            (tid != -1) &&
            ((frameCount == 0) ||
            (frameCount >= mFrameCount))
          )
        ) &&
        // PCM data
        audio_is_linear_pcm(format) &&
        // mono or stereo
        ( (channelMask == AUDIO_CHANNEL_OUT_MONO) ||
          (channelMask == AUDIO_CHANNEL_OUT_STEREO) ) &&
#ifndef FAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE
        // hardware sample rate
        (sampleRate == mSampleRate) &&
#endif
        // normal mixer has an associated fast mixer
        hasFastMixer() &&
        // there are sufficient fast track slots available
        (mFastTrackAvailMask != 0)
        // FIXME test that MixerThread for this fast track has a capable output HAL
        // FIXME add a permission test also?
    ) {
    // if frameCount not specified, then it defaults to fast mixer (HAL) frame count
    if (frameCount == 0) {
        frameCount = mFrameCount * kFastTrackMultiplier;
    }
    ALOGV("AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d",
            frameCount, mFrameCount);
  } else {
    ALOGV("AUDIO_OUTPUT_FLAG_FAST denied: isTimed=%d sharedBuffer=%p frameCount=%d "
            "mFrameCount=%d format=%d isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u "
            "hasFastMixer=%d tid=%d fastTrackAvailMask=%#x",
            isTimed, sharedBuffer.get(), frameCount, mFrameCount, format,
            audio_is_linear_pcm(format),
            channelMask, sampleRate, mSampleRate, hasFastMixer(), tid, mFastTrackAvailMask);
    *flags &= ~IAudioFlinger::TRACK_FAST;
    // For compatibility with AudioTrack calculation, buffer depth is forced
    // to be at least 2 x the normal mixer frame count and cover audio hardware latency.
    // This is probably too conservative, but legacy application code may depend on it.
    // If you change this calculation, also review the start threshold which is related.
    uint32_t latencyMs = mOutput->stream->get_latency(mOutput->stream);
    uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
    if (minBufCount < 2) {
        minBufCount = 2;
    }

 #ifdef MTK_AUDIO
    size_t minFrameCount = (mFrameCount*sampleRate*minBufCount)/mSampleRate;
#else
    size_t minFrameCount = mNormalFrameCount * minBufCount;
#endif
    if (frameCount < minFrameCount) {
        frameCount = minFrameCount;
    }
  }
}

if (mType == DIRECT) {
    if ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM) {
        if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) {
            ALOGE("createTrack_l() Bad parameter: sampleRate %u format %d, channelMask 0x%08x "
                    "for output %p with format %d",
                    sampleRate, format, channelMask, mOutput, mFormat);
            lStatus = BAD_VALUE;
            goto Exit;
        }
    }
} else if (mType == OFFLOAD) {
    if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) {
        ALOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelMask 0x%08x \""
                "for output %p with format %d",
                sampleRate, format, channelMask, mOutput, mFormat);
        lStatus = BAD_VALUE;
        goto Exit;
    }
} else {
    if ((format & AUDIO_FORMAT_MAIN_MASK) != AUDIO_FORMAT_PCM) {
            ALOGE("createTrack_l() Bad parameter: format %d \""
                    "for output %p with format %d",
                    format, mOutput, mFormat);
            lStatus = BAD_VALUE;
            goto Exit;
    }
    // Resampler implementation limits input sampling rate to 2 x output sampling rate.
    if (sampleRate > mSampleRate*2) {
        ALOGE("Sample rate out of range: %u mSampleRate %u", sampleRate, mSampleRate);
#ifndef MTK_AUDIO
        lStatus = BAD_VALUE;
        goto Exit;
#endif
    }
}

lStatus = initCheck();
if (lStatus != NO_ERROR) {
    ALOGE("Audio driver not initialized.");
    goto  Exit;
}

{ // scope for mLock
    Mutex::Autolock _l(mLock);

    // all tracks in same audio session must share the same routing strategy otherwise
    // conflicts will happen when tracks are moved from one output to another by audio policy
    // manager
    uint32_t strategy = AudioSystem::getStrategyForStream(streamType);
    for (size_t i = 0; i < mTracks.size(); ++i) {
        sp<Track> t = mTracks[i];
        if (t != 0 && !t->isOutputTrack()) {
            uint32_t actual = AudioSystem::getStrategyForStream(t->streamType());
            if (sessionId == t->sessionId() && strategy != actual) {
                ALOGE("createTrack_l() mismatched strategy; expected %u but found %u",
                        strategy, actual);
                lStatus = BAD_VALUE;
                goto Exit;
            }
        }
    }

    if (!isTimed) {
        track = new Track(this, client, streamType, sampleRate, format,
                channelMask, frameCount, sharedBuffer, sessionId, uid, *flags);
    } else {
        track = TimedTrack::create(this, client, streamType, sampleRate, format,
                channelMask, frameCount, sharedBuffer, sessionId, uid);
    }

    if (track == 0 || track->getCblk() == NULL || track->name() < 0) {
        lStatus = NO_MEMORY;
        // track must be cleared from the caller as the caller has the AF lock
        goto Exit;
    }

    mTracks.add(track);
    ALOGD("%s, mTracks.add(), track 0x%x, this 0x%x", __FUNCTION__, track.get(), this);

    sp<EffectChain> chain = getEffectChain_l(sessionId);
    if (chain != 0) {
        ALOGV("createTrack_l() setting main buffer %p", chain->inBuffer());
        track->setMainBuffer(chain->inBuffer());
        chain->setStrategy(AudioSystem::getStrategyForStream(track->streamType()));
        chain->incTrackCnt();
    }

    if ((*flags & IAudioFlinger::TRACK_FAST) && (tid != -1)) {
        pid_t callingPid = IPCThreadState::self()->getCallingPid();
        // we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful,
        // so ask activity manager to do this on our behalf
        sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp);
    }
}

lStatus = NO_ERROR;

Exit:
if (status) {
    *status = lStatus;
}
return track;
}

-》 
uint32_t actual = AudioSystem::getStrategyForStream(t->streamType()); 
track = new Track(this, client, streamType, sampleRate, format, 
channelMask, frameCount, sharedBuffer, sessionId, uid, *flags); 
-》 
AudioFlinger::PlaybackThread::Track::Track 
-》 
AudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, 
size_t frameSize, bool clientInServer = false) 
: ServerProxy(cblk, buffers, frameCount, frameSize, true /isOut/, clientInServer) { } 
-》

ServerProxy::ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
    size_t frameSize, bool isOut, bool clientInServer)
: Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer),
  mAvailToClient(0), mFlush(0)
{
}

然后根据这个 streamout 和ID, NEW threads. 然后ADD 到 AF中的 mPlaybackThreads。 
-》hwDevHal->set_mode(hwDevHal, mMode); // 大概是 设置MODE, NORMAL , CALL MODE? 条件是 (mPrimaryHardwareDev == NULL) ,只有第一次AudioFlinger::openOutput 时会设置模式。

HAL 层 open_output_stream

Audio_hw_hal.cpp 中关于 
legacy_adev_open 中将 
ladev->device.open_output_stream = adev_open_output_stream; 
-》 adev_open_output_stream

-》 
static int adev_open_output_stream(struct audio_hw_device *dev, 
audio_io_handle_t handle, 
audio_devices_t devices, 
audio_output_flags_t flags, 
struct audio_config *config, 
struct audio_stream_out **stream_out) 

struct legacy_audio_device *ladev = to_ladev(dev); 
status_t status; 
struct legacy_stream_out *out; 
int ret;

    out = (struct legacy_stream_out *)calloc(1, sizeof(*out));  分配内存,然后由下面设备填充。
    if (!out)
        return -ENOMEM;
  // 打开设备,填充
    out->legacy_out = ladev->hwif->openOutputStreamWithFlag(devices, (int *) &config->format,
                                                    &config->channel_mask,
                                                    &config->sample_rate, &status, flags);
    if (!out->legacy_out) {
        ret = status;
        goto err_open;
    }

    out->stream.common.get_sample_rate = out_get_sample_rate;
    out->stream.common.set_sample_rate = out_set_sample_rate;
    out->stream.common.get_buffer_size = out_get_buffer_size;
    out->stream.common.get_channels = out_get_channels;
    out->stream.common.get_format = out_get_format;
    out->stream.common.set_format = out_set_format;
    out->stream.common.standby = out_standby;
    out->stream.common.dump = out_dump;
    out->stream.common.set_parameters = out_set_parameters;
    out->stream.common.get_parameters = out_get_parameters;
    out->stream.common.add_audio_effect = out_add_audio_effect;
    out->stream.common.remove_audio_effect = out_remove_audio_effect;
    out->stream.get_latency = out_get_latency;
    out->stream.set_volume = out_set_volume;
    out->stream.write = out_write;
    out->stream.get_render_position = out_get_render_position;
    out->stream.get_next_write_timestamp = out_get_next_write_timestamp;

    out->stream.set_callback = out_set_callback;
    out->stream.get_presentation_position = out_get_presentation_position;

    *stream_out = &out->stream;
    return 0;

err_open:
    free(out);
    *stream_out = NULL;
    return ret;
}

-》AudioHardwareALSA::openOutputStream

-》

android_audio_legacy::AudioStreamOut *AudioALSAHardware::openOutputStream(
    uint32_t devices,
    int *format,
    uint32_t *channels,
    uint32_t *sampleRate,
    status_t *status)
{
    return mStreamManager->openOutputStream(devices, format, channels, sampleRate, status);
}

/*============================================================================== 
* Implementations 
============================================================================/

-》 
android_audio_legacy::AudioStreamOut *AudioALSAStreamManager::openOutputStream( 
uint32_t devices, 
int *format, 
uint32_t *channels, 
uint32_t *sampleRate, 
status_t *status) 

ALOGD(“+%s()”, FUNCTION); 
AudioAutoTimeoutLock streamVectorAutoTimeoutLock(mStreamVectorLock); 
AudioAutoTimeoutLock _l(mLock);

if (format == NULL || channels == NULL || sampleRate == NULL || status == NULL)
{
    ALOGE("%s(), NULL pointer!! format = %p, channels = %p, sampleRate = %p, status = %p",
          __FUNCTION__, format, channels, sampleRate, status);
    if (status != NULL) { *status = INVALID_OPERATION; }
    return NULL;
}


// stream out flags
const uint32_t flags = (uint32_t)(*status);

// create stream out
AudioALSAStreamOut *pAudioALSAStreamOut = new AudioALSAStreamOut();
pAudioALSAStreamOut->set(devices, format, channels, sampleRate, status, flags);
if (*status != NO_ERROR)
{
    ALOGE("-%s(), set fail, return NULL", __FUNCTION__);
    delete pAudioALSAStreamOut;
    pAudioALSAStreamOut = NULL;
    return NULL;
}

// save stream out object in vector
pAudioALSAStreamOut->setIdentity(mStreamOutIndex);
mStreamOutVector.add(mStreamOutIndex, pAudioALSAStreamOut); // 加入到streammanager 容器中。
mStreamOutIndex++;

// setup Filter for ACF/HCF/AudEnh/VibSPK // TODO Check return status of pAudioALSAStreamOut->set
AudioMTKFilterManager *pAudioFilterManagerHandler = new AudioMTKFilterManager(*sampleRate, android_audio_legacy::AudioSystem::popCount(*channels), *format, pAudioALSAStreamOut->bufferSize());
mFilterManagerVector.add(mFilterManagerNumber, pAudioFilterManagerHandler);
mFilterManagerNumber++;

ALOGD("-%s(), out = %p, status = 0x%x, mStreamOutVector.size() = %d",
      __FUNCTION__, pAudioALSAStreamOut, *status, mStreamOutVector.size());


return pAudioALSAStreamOut;
}

-》audioAStreamManager::openOutputStream 
-》audioALSAStreamOut::open 
-》audioALSAStreamOut::open() 
-》playbackHandler = mStreamManager->createPlaybackHandler(&mStreamAttributeSource); 
-> // open codec driver 
mHardwareResourceManager->startOutputDevice(mStreamAttributeSource->output_devices, mStreamAttributeTarget.sample_rate);

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

mtk android 4.4 audio framework 代码分析(未完成) 的相关文章

  • 主题以编程方式设置。如何重新加载 Activity 来应用

    如何在不重新启动整个应用程序的情况下应用主题 如果我这样做startActivity getIntent finish 活动退出并且不重新启动 是否可以简单地重新启动 重新创建活动来应用主题 它的顺序不正确 finish intent ne
  • 在Android中将半径边框绘制到imageview或textview的一个角落

    我需要在我的应用程序中为图像视图或文本视图绘制边框 但我只需要在一个角落绘制它 就像图像一样 我做了一个形状 但我在所有 4 个边上都有边框
  • Android 从键盘读取

    我的登录屏幕根本没有文本字段 当用户使用 RFID 扫描仪扫描他的 id 令牌时 我会得到一个 8 个字符长的字符串 其原理与使用键盘相同 只是更快 我希望我的登录活动在用户扫描其令牌时而不是之前执行 有一个聪明的方法来实现这个吗 我不能有
  • Android Accessibility 执行触摸操作

    我想知道是否可以使用 Android 辅助功能服务在屏幕上的位置执行触摸操作 例如 Bundle arguments new Bundle arguments putInt coord X X value arguments putInt
  • 如何更改对话框的默认黑色暗淡背景“颜色”(而不是暗淡量)?

    这是随机显示的图像Dialog在网上找到的 我一直在实施一个自定义Dialog 我可以处理对话框上的几乎所有内容 除了对话框本身下方的默认黑色昏暗背景之外 但在其后面的整个屏幕上 基本上我想改变它color和阿尔法值 我一直在 StackO
  • Android:拍照后调用裁剪活动

    我在解析拍摄照片的 uri 来裁剪活动时遇到问题 在我的应用程序中 用户可以拍摄一张照片或从图库中选择一张照片 然后裁剪并上传 一切听起来都很简单 从图库中选择时 图库应用程序会返回所选照片的 uri 如下所示 content media
  • 有没有办法替代Android中的标准Log?

    有没有办法以某种方式拦截对 android 中标准 Log 的调用并执行其他操作 在桌面 Java 中 人们通常会得到一些记录器 因此有多种方法可以安装不同的日志处理程序 实现 但是 Android似乎对Log有静态调用 我找不到任何有关替
  • 如何找到特定路线上两点之间的距离?

    我正在为我的大学开发一个 Android 应用程序 可以帮助学生跟踪大学巴士的当前位置 并为他们提供巴士到达他们的预计时间 截至目前 我获取了公交车的当前位置 通过公交车上的设备 和学生的位置 我陷入了必须找到两个 GPS 坐标之间的距离的
  • Android 性能:SharedPreferences 的成本

    当我的应用程序启动时 我使用分片首选项中的值填充容器类 这个想法是处理 SharedPreferences 和 PreferenceManager 一次 因为我猜它们很重 这是一个示例 SharedPreferences prefs Pre
  • Android 中带有透明背景的 ImageButton [重复]

    这个问题在这里已经有答案了 我已经按照这篇文章在android中制作ImageButton 安卓图像按钮 https stackoverflow com questions 2283444 android image button 图像出现
  • 以编程方式应用样式资源

    我没有找到一种以编程方式做到这一点的方法 所以我在这里发布这个问题 我也没有找到与此相关的任何问题 我有一个资源样式 在 res values styles xml 中定义 我想做的是使用 java 将这种样式应用到我正在操作的 View
  • 在尝试使用 GPS 之前如何检查 GPS 是否已启用

    我有以下代码 但效果不好 因为有时 GPS 需要很长时间 我该如何执行以下操作 检查GPS是否启用 如果启用了 GPS 请使用 GPS 否则请使用网络提供商 如果 GPS 时间超过 30 秒 请使用网络 我可以使用时间或 Thread sl
  • 与 Dagger 一起使用时,Espresso 生成 FileNotFoundException

    我一直在研究旧版 Android 应用程序 尝试为其添加测试和适当的架构 该应用程序有一个主要LaunchActivity它在启动时运行一系列检查 最初 该活动使用 Dagger 来 注入依赖项 活动将使用它来运行检查 但效果相当糟糕 我转
  • 确定视图是否在屏幕上 - Android

    我对这个有点困惑 首先也是最重要的是 以下链接很有用 但是我提出了一些可见性问题 链接 检查视图可见性 https stackoverflow com questions 4628800 android how to check if a
  • 为什么 ExpandableListView 更改 ChildView 设置(Android)?

    我对使用 ExpandableListView 有疑问 就我而言 我有两个组视图和两个子视图 而子视图由一个带有多个按钮 文本视图等的相对布局组成 例如 当首先扩展第二组并对视图持有者进行一些更改并随后扩展第一组时 先前所做的更改也会自动应
  • 从多个 TextView 中选择文本

    如何在android中从多个文本视图中选择文本 我已经尝试过以下代码 该代码一次仅适用于一个文本视图 我想一次性从许多文本视图中复制文本 android textIsSelectable true 你不能同时这样做 您需要在单个文本视图中设
  • 在 Qt 中播放通知(频率 x)声音 - 最简单的方法?

    Qt 5 1 或更高版本 我需要播放频率为 x 的通知声音 n 毫秒 如果我能像这样组合音调那就太好了 1000Hz 持续 2 秒 然后 3000Hz 持续 1 秒 最简单的方法是使用文件 WAV MP3 例如如此处所述 如何用Qt播放声音
  • Android:ANT 构建失败,并显示 google-play-services-lib:“解析为没有项目的 project.properties 文件的路径”

    我正在尝试使用 ANT 构建我的应用程序 但在包含 google play services lib 库项目后 我惨遭失败 Step 1 我在 project properties 文件中设置了对库项目的引用 android library
  • Android:如何在布局中放置纯色矩形?

    我有一个可以很好地膨胀的relativelayout 我想在顶部添加一个跨越布局宽度的纯色矩形 我尝试将以下内容放入我的 xml 中
  • Android中绑定适配器有什么用?

    我一直在阅读有关Android中绑定适配器的文章 但我似乎不明白它 何时使用绑定适配器 有人可以用一个简单的例子来解释它吗 我读过的一篇文章在主活动中有一个绑定适配器 绑定适配器有一个参数 toastMessage 显然 只要 toastM

随机推荐

  • 错误./hello: error while loading shared libraries: libQtGui.so.4: cannot open shared object file:

    之前一直想在ARM 上跑qt xff0c 但都出现错误 xff1a hello error while loading shared libraries libQtGui so 4 cannot open shared object fil
  • linux eth0设置

    命令行设定IP地址 ifconfig eth0 192 168 1 12 将eth0IP设置为192 168 1 12 ifconfig eth0 up 使eth0使能 如果开发板与路由器连接 xff0c 并且路由器能够自动分配IP地址 x
  • printk打印不能显示到终端的解决方法

    printk与printf有个不同的地方 xff0c 就是printk有打印级别 使用printk时 xff0c Linux内核根据日志级别 xff0c 可能把消息打印到当前控制台上 xff0c 这个控制台是一个字符设备 这些消息从终端输出
  • qt socket通信中接收client发送是十六进制数据包

    在QT的服务端接收客户端发送的十六进制收据包 xff0c 经转换后显示在LineEdit上 xff0c 并把接收到的数据包转化为char 类型 xff0c 为后期数据处理做准备 recbuf在头文件类中一定义 xff1a QByteArra
  • 两个双口ram之间数据的传递

    1 如果两个双口ram数据位宽相同 xff0c 则采用时钟快的ram等待时钟慢的ram来完成从一个ram中读取数据并存储到另一个ram中 xff1b 例如从ram A中读取数据到ram xff22 中 xff0c xff52 xff41 x
  • + - 与>> <<运算优先级

    43 运算符的优先级高于 lt lt gt gt 位移运算符 span class hljs keyword int span mian span class hljs keyword int span a 61 span class hl
  • linux col 过滤控制字符

    参考http blog 51cto com jim123 1833502 使用过Unix系统的人肯定会知道man帮助的功能强大 xff0c 是官方的帮助文档 xff0c 我们平时可以通过它来查询不知道如何使用的命令或者查询linux的系统C
  • gcc 参数

    gcc gcc与 g 43 43 分别是GNU的C与 C 43 43 的编译器 xff0c 在编译工作中分4步 xff1a 1 预处理 xff0c 生成 i文件 2 编译器 xff0c 编译后停下来 xff0c 生成 o的目标文件 3 汇编
  • gdb 调试

    原文http linuxtools rst readthedocs io zh CN latest tool gdb html span class hljs variable span span class hljs number 1 s
  • Linux-C语言 网络TCP单次通信、多次通信、多线程通信逐步实现

    一 TCP通信 xff0c 只发送一次就结束程序 功能描述 xff1a 1 服务端一次只能连接一个客户端 2 客户端只能向服务端发送一次消息 xff0c 消息返回后客户端和服务器程序结束 3 客户端向服务端发送一个字符串 xff0c 服务端
  • 奇偶校验通俗易懂

    简介 xff1a 奇偶校验是奇校验和偶校验的统称 xff0c 就是在最低位或最高位添加一个校验位 xff0c 应用于主存储器信息的校验及字节传输的出错校验 原理 xff1a 奇校验 连同校验位使得所有位上的1相加为奇数 偶校验 xff1a
  • Ubuntu系统rosdep update报错的解决办法(2022.10.3亲测有效)

    目录 一 问题 xff1a Ubuntu22 04系统下面 xff0c rosdep update总是报错 二 方法一一道来 1 直接访问raw githubusercontent com是不行的 按照网上的解决办法先把ip地址找到 xff
  • Socket通信实验总结

    在实验设计的过程中遇到了不少困难 xff0c 先是服务器监听时怎么保持已有的socket 连接 xff0c 又能接受新的连接 在此用了 C 的 Dictionary lt string Socket gt socketDic 61 new
  • [Excel]Excel函数和用法(4)——查找字符串,SEARCH和FIND函数

    区别 xff1a SEARCH大小写不敏感 xff0c FIND大小写敏感 作用 xff1a 都是从左到右返回搜索到的字符串的起始位置 SEARCH语法 xff1a SEARCH find text within text start nu
  • Error: L6200E: Symbol B_DisCnctRelayTime multiply defined (by cdma_gps_hc.o and main.o).

    现象 xff1a 最近调试MDK的程序 xff0c 老是报这样的错误 L6200E Symbol B DisCnctRelayTime multiply defined by cdma gps o and main o 记录下来 xff1a
  • STM32 ADC用到的 抗脉冲滤波算法

    先介绍一下算法的基本思想 xff1a 在一组采样值中 xff0c 去掉 abandonMaxNum 个最大数据 xff0c 去掉 abandonMinNum 个最小数据 xff0c 余下的数据求平均值 函数功能 xff1a 抗脉冲滤波法 输
  • STM32使用内部振荡器及其和外部晶体振荡器的区别

    转自 xff1a http blog csdn net meic51 article details 8778518 在STM32上如果不使用外部晶振 xff0c OSC IN和OSC OUT的接法 如果使用内部RC振荡器而不使用外部晶振
  • Android的Audio系统

    转自 xff1a http blog csdn net gowyz article details 6019314 Android的Audio 系统 第一部分 Audio 系统综述 第二部分 Audio 系统和上层接口 第三部分 Audio
  • Android Audio代码分析 - Audio Strategy

    frameworks base services AudioFlinger cpp status t AudioFlinger PlaybackThread Track start status t status 61 NO ERROR L
  • mtk android 4.4 audio framework 代码分析(未完成)

    mtk android 4 4 audio framework 代码分析 未完成 xff0c 有需要的朋友可以参考下 mtk android 4 4 audio framework 代码分析 未完成 2 28 2015 3 01 24 PM