发现以前写的东西,对调用函数的展开放在了函数的前面,导致不方便找到原来代码及设置的函数参数。
以后打算稍作改动,把对被调函数的展开放在原代码的后面,这样看起来应该方便些。
闲言少叙,跳入代码。
前两天看AudioTrack创建的时候,我们看到了AudioHardwareALSA::openOutputStream,并没有继续往下看。
今天就看看函数AudioHardwareALSA::openOutputStream的实现。
*****************************************源码*************************************************
AudioStreamOut *
AudioHardwareALSA::openOutputStream(uint32_t devices,
int *format,
uint32_t *channels,
uint32_t *sampleRate,
status_t *status)
{
AutoMutex lock(mLock);
LOGD("openOutputStream called for devices: 0x%08x", devices);
status_t err = BAD_VALUE;
AudioStreamOutALSA *out = 0;
if (devices & (devices - 1)) {
if (status) *status = err;
LOGD("openOutputStream called with bad devices");
return out;
}
// Find the appropriate alsa device
for(ALSAHandleList::iterator it = mDeviceList.begin();
it != mDeviceList.end(); ++it)
if (it->devices & devices) {
err = mALSADevice->open(&(*it), devices, mode());
if (err) break;
if (devices & AudioSystem::DEVICE_OUT_WIRED_HDMI){
strcpy(mCurCard ,SPDIF);
mMixer = mMixerSpdif;
} else {
strcpy(mCurCard,SGTL5000);
mMixer = mMixerSgtl5000;
}
out = new AudioStreamOutALSA(this, &(*it));
err = out->set(format, channels, sampleRate);
break;
}
if (status) *status = err;
return out;
}
**********************************************************************************************
源码路径:
hardware\alsa_sound\AudioHardwareALSA.cpp
###########################################说明################################################
AudioStreamOut *
AudioHardwareALSA::openOutputStream(uint32_t devices,
int *format,
uint32_t *channels,
uint32_t *sampleRate,
status_t *status)
{
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
先回忆一下各个参数都是嘛意思。
devices
设备编号。也就是说打开的是第几个ALSA声卡。该参数是在函数AudioPolicyManagerBase::getOutput中产生的。
至于如何调到了函数AudioHardwareALSA::openOutputStream,可以参考文章:Android Audio代码分析4 - AudioSystem::getOutputSamplingRate。
函数AudioPolicyManagerBase::getOutput中直接调用的是AudioPolicyService::openOutput函数。调用代码如下:
output = mpClientInterface->openOutput(&outputDesc->mDevice,
&outputDesc->mSamplingRate,
&outputDesc->mFormat,
&outputDesc->mChannels,
&outputDesc->mLatency,
outputDesc->mFlags);
其中,outputDesc->mDevice就是devices。
outputDesc->mDevice是怎么来的呢?
继续往前看,outputDesc->mDevice = device;
device的出处在哪?
uint32_t device = getDeviceForStrategy(strategy);
strategy的来头是:
routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
看看这两个函数吧。
先看看getStrategy。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy(
AudioSystem::stream_type stream) {
// stream to strategy mapping
switch (stream) {
case AudioSystem::VOICE_CALL:
case AudioSystem::BLUETOOTH_SCO:
return STRATEGY_PHONE;
case AudioSystem::RING:
case AudioSystem::NOTIFICATION:
case AudioSystem::ALARM:
case AudioSystem::ENFORCED_AUDIBLE:
return STRATEGY_SONIFICATION;
case AudioSystem::DTMF:
return STRATEGY_DTMF;
default:
LOGE("unknown stream type");
case AudioSystem::SYSTEM:
// NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs
// while key clicks are played produces a poor result
case AudioSystem::TTS:
case AudioSystem::MUSIC:
return STRATEGY_MEDIA;
}
}
实现比较简单,根据stream类型,返回特定的策略。
----------------------------------------------------------------
再看看函数getDeviceForStrategy。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
看看其英文注释:
// return appropriate device for streams handled by the specified strategy according to current
// phone state, connected devices...
// if fromCache is true, the device is returned from mDeviceForStrategy[], otherwise it is determined
// by current state (device connected, phone state, force use, a2dp output...)
// This allows to:
// 1 speed up process when the state is stable (when starting or stopping an output)
// 2 access to either current device selection (fromCache == true) or
// "future" device selection (fromCache == false) when called from a context
// where conditions are changing (setDeviceConnectionState(), setPhoneState()...) AND
// before updateDeviceForStrategy() is called.
virtual uint32_t getDeviceForStrategy(routing_strategy strategy, bool fromCache = true);
顺便说一句,头文件AudioPolicyManagerBase.h不在frameworks中,而是在hardware中。
----------------------------------------------------------------
{
uint32_t device = 0;
// 函数注释中也有说,如果fromCache为true,直接返回内存中保存的信息。
// 我们调用的时候,只给了一个参数,所以fromCache肯定为true。
if (fromCache) {
LOGV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]);
return mDeviceForStrategy[strategy];
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
mDeviceForStrategy又是什么时候被赋值的呢?
在函数updateDeviceForStrategy中。。。
void AudioPolicyManagerBase::updateDeviceForStrategy()
{
for (int i = 0; i < NUM_STRATEGIES; i++) {
mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false);
}
}
还是调用的getDeviceForStrategy函数,只不过不是从cache中取,而是根据当前状态来取。
函数updateDeviceForStrategy在以下几个地方被调用:
AudioPolicyManagerBase::setDeviceConnectionState函数。
AudioPolicyManagerBase::setPhoneState函数。
AudioPolicyManagerBase::setForceUse函数。
AudioPolicyManagerBase的构造函数。
也就是在可能发生改变的地方。
----------------------------------------------------------------
}
switch (strategy) {
case STRATEGY_DTMF:
if (!isInCall()) {
// 如果是in call,采用的是STRATEGY_PHONE的策略。如果是off call,采用的是STRATEGY_MEDIA的策略。
// 如何判断in call / off call的?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bool AudioPolicyManagerBase::isInCall()
{
return isStateInCall(mPhoneState);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bool AudioPolicyManagerBase::isStateInCall(int state) {
return ((state == AudioSystem::MODE_IN_CALL) ||
(state == AudioSystem::MODE_IN_COMMUNICATION));
}
----------------------------------------------------------------
// 看看mPhoneState是在哪儿赋值的:
构造函数中有个对其的初始化: mPhoneState(AudioSystem::MODE_NORMAL)
setPhoneState函数中有对其赋值。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void AudioPolicyManagerBase::setPhoneState(int state)
{
LOGV("setPhoneState() state %d", state);
uint32_t newDevice = 0;
if (state < 0 || state >= AudioSystem::NUM_MODES) {
LOGW("setPhoneState() invalid state %d", state);
return;
}
if (state == mPhoneState ) {
LOGW("setPhoneState() setting same state %d", state);
return;
}
// if leaving call state, handle special case of active streams
// pertaining to sonification strategy see handleIncallSonification()
if (isInCall()) {
LOGV("setPhoneState() in call state management: new state is %d", state);
for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
handleIncallSonification(stream, false, true);
}
}
// store previous phone state for management of sonification strategy below
int oldState = mPhoneState;
mPhoneState = state;
bool force = false;
// are we entering or starting a call
if (!isStateInCall(oldState) && isStateInCall(state)) {
LOGV(" Entering call in setPhoneState()");
// force routing command to audio hardware when starting a call
// even if no device change is needed
force = true;
} else if (isStateInCall(oldState) && !isStateInCall(state)) {
LOGV(" Exiting call in setPhoneState()");
// force routing command to audio hardware when exiting a call
// even if no device change is needed
force = true;
} else if (isStateInCall(state) && (state != oldState)) {
LOGV(" Switching between telephony and VoIP in setPhoneState()");
// force routing command to audio hardware when switching between telephony and VoIP
// even if no device change is needed
force = true;
}
// check for device and output changes triggered by new phone state
newDevice = getNewDevice(mHardwareOutput, false);
#ifdef WITH_A2DP
checkOutputForAllStrategies();
checkA2dpSuspend();
#endif
updateDeviceForStrategy();
AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
// force routing command to audio hardware when ending call
// even if no device change is needed
if (isStateInCall(oldState) && newDevice == 0) {
newDevice = hwOutputDesc->device();
}
// when changing from ring tone to in call mode, mute the ringing tone
// immediately and delay the route change to avoid sending the ring tone
// tail into the earpiece or headset.
int delayMs = 0;
if (isStateInCall(state) && oldState == AudioSystem::MODE_RINGTONE) {
// delay the device change command by twice the output latency to have some margin
// and be sure that audio buffers not yet affected by the mute are out when
// we actually apply the route change
delayMs = hwOutputDesc->mLatency*2;
setStreamMute(AudioSystem::RING, true, mHardwareOutput);
}
// change routing is necessary
setOutputDevice(mHardwareOutput, newDevice, force, delayMs);
// if entering in call state, handle special case of active streams
// pertaining to sonification strategy see handleIncallSonification()
if (isStateInCall(state)) {
LOGV("setPhoneState() in call state management: new state is %d", state);
// unmute the ringing tone after a sufficient delay if it was muted before
// setting output device above
if (oldState == AudioSystem::MODE_RINGTONE) {
setStreamMute(AudioSystem::RING, false, mHardwareOutput, MUTE_TIME_MS);
}
for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
handleIncallSonification(stream, true, true);
}
}
// Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE
if (state == AudioSystem::MODE_RINGTONE &&
(hwOutputDesc->mRefCount[AudioSystem::MUSIC] ||
(systemTime() - mMusicStopTime) < seconds(SONIFICATION_HEADSET_MUSIC_DELAY))) {
mLimitRingtoneVolume = true;
} else {
mLimitRingtoneVolume = false;
}
}
----------------------------------------------------------------
函数AudioPolicyService::setPhoneState调用了函数AudioPolicyManagerBase::setPhoneState:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
status_t AudioPolicyService::setPhoneState(int state)
{
if (mpPolicyManager == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
if (state < 0 || state >= AudioSystem::NUM_MODES) {
return BAD_VALUE;
}
LOGV("setPhoneState() tid %d", gettid());
// TODO: check if it is more appropriate to do it in platform specific policy manager
AudioSystem::setMode(state);
Mutex::Autolock _l(mLock);
mpPolicyManager->setPhoneState(state);
return NO_ERROR;
}
----------------------------------------------------------------
函数AudioSystem::setPhoneState调用了函数AudioPolicyService::setPhoneState:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
status_t AudioSystem::setPhoneState(int state)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->setPhoneState(state);
}
----------------------------------------------------------------
函数android_media_AudioSystem_setPhoneState调用了函数AudioSystem::setPhoneState。
从名称可以看出,函数android_media_AudioSystem_setPhoneState应该是通过JNI提供给Java层的函数。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int
android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state)
{
return check_AudioSystem_Command(AudioSystem::setPhoneState(state));
}
java侧的函数handleMessage中遇到MSG_MEDIA_SERVER_STARTED消息,以及函数binderDied中有对该函数的调用。
----------------------------------------------------------------
}
----------------------------------------------------------------
// when off call, DTMF strategy follows the same rules as MEDIA strategy
device = getDeviceForStrategy(STRATEGY_MEDIA, false);
break;
}
// when in call, DTMF and PHONE strategies follow the same rules
// FALL THROUGH
case STRATEGY_PHONE:
// for phone strategy, we first consider the forced use and then the available devices by order
// of priority
switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) {
case AudioSystem::FORCE_BT_SCO:
if (!isInCall() || strategy != STRATEGY_DTMF) {
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
if (device) break;
}
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
if (device) break;
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO;
if (device) break;
// if SCO device is requested but no SCO device is available, fall back to default case
// FALL THROUGH
default: // FORCE_NONE
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
if (device) break;
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
if (device) break;
#ifdef WITH_A2DP
// when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
if (!isInCall()) {
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
if (device) break;
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
if (device) break;
}
#endif
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE;
if (device == 0) {
LOGE("getDeviceForStrategy() earpiece device not found");
}
break;
case AudioSystem::FORCE_SPEAKER:
if (!isInCall() || strategy != STRATEGY_DTMF) {
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
if (device) break;
}
#ifdef WITH_A2DP
// when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
// A2DP speaker when forcing to speaker output
if (!isInCall()) {
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
if (device) break;
}
#endif
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
if (device == 0) {
LOGE("getDeviceForStrategy() speaker device not found");
}
break;
}
break;
case STRATEGY_SONIFICATION:
// If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by
// handleIncallSonification().
if (isInCall()) {
device = getDeviceForStrategy(STRATEGY_PHONE, false);
break;
}
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
if (device == 0) {
LOGE("getDeviceForStrategy() speaker device not found");
}
// The second device used for sonification is the same as the device used by media strategy
// FALL THROUGH
case STRATEGY_MEDIA: {
// 先看看MEDIA策略相关的
uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
mAvailableOutputDevices又是怎么来地?
函数AudioPolicyManagerBase::setDeviceConnectionState中有对其赋值。
也就是说设备连接状态改变时,会对其赋值。
AudioPolicyManagerBase的构造函数中也有对其赋值。
默认情况下,有三个设备可用。
两个输出,一个输入。
// devices available by default are speaker, ear piece and microphone
mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |
AudioSystem::DEVICE_OUT_SPEAKER;
mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;
audio device定义如下:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
enum audio_devices {
// output devices
DEVICE_OUT_EARPIECE = 0x1,
DEVICE_OUT_SPEAKER = 0x2,
DEVICE_OUT_WIRED_HEADSET = 0x4,
DEVICE_OUT_WIRED_HEADPHONE = 0x8,
DEVICE_OUT_BLUETOOTH_SCO = 0x10,
DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20,
DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40,
DEVICE_OUT_BLUETOOTH_A2DP = 0x80,
DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,
DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200,
DEVICE_OUT_AUX_DIGITAL = 0x400,
DEVICE_OUT_FM_HEADPHONE = 0x800,
DEVICE_OUT_FM_SPEAKER = 0x1000,
DEVICE_OUT_TTY = 0x2000,
DEVICE_OUT_WIRED_HDMI = 0x4000,
DEVICE_OUT_DEFAULT = 0x8000,
DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE | DEVICE_OUT_SPEAKER | DEVICE_OUT_WIRED_HEADSET |
DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
DEVICE_OUT_BLUETOOTH_SCO_CARKIT | DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL | DEVICE_OUT_FM_HEADPHONE | DEVICE_OUT_FM_SPEAKER | DEVICE_OUT_TTY | DEVICE_OUT_WIRED_HDMI | DEVICE_OUT_DEFAULT),
DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
// input devices
DEVICE_IN_COMMUNICATION = 0x10000,
DEVICE_IN_AMBIENT = 0x20000,
DEVICE_IN_BUILTIN_MIC = 0x40000,
DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x80000,
DEVICE_IN_WIRED_HEADSET = 0x100000,
DEVICE_IN_AUX_DIGITAL = 0x200000,
DEVICE_IN_VOICE_CALL = 0x400000,
DEVICE_IN_BACK_MIC = 0x800000,
DEVICE_IN_DEFAULT = 0x80000000,
DEVICE_IN_ALL = (DEVICE_IN_COMMUNICATION | DEVICE_IN_AMBIENT | DEVICE_IN_BUILTIN_MIC |
DEVICE_IN_BLUETOOTH_SCO_HEADSET | DEVICE_IN_WIRED_HEADSET | DEVICE_IN_AUX_DIGITAL |
DEVICE_IN_VOICE_CALL | DEVICE_IN_BACK_MIC | DEVICE_IN_DEFAULT)
};
----------------------------------------------------------------
----------------------------------------------------------------
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HDMI;
}
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
}
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
}
#ifdef WITH_A2DP
if (mA2dpOutput != 0) {
if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) {
break;
}
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
}
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
}
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
}
}
#endif
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
}
// device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION, 0 otherwise
device |= device2;
if (device == 0) {
LOGE("getDeviceForStrategy() speaker device not found");
}
} break;
default:
LOGW("getDeviceForStrategy() unknown strategy: %d", strategy);
break;
}
LOGV("getDeviceForStrategy() strategy %d, device %x", strategy, device);
return device;
}
----------------------------------------------------------------
回到函数头:
AudioStreamOut *
AudioHardwareALSA::openOutputStream(uint32_t devices,
int *format,
uint32_t *channels,
uint32_t *sampleRate,
status_t *status)
参数devices看完了。
后面的几个参数就比较简单了。
format是指音频格式,当前支持的有8PCMBIT和16PCMBIT.
channels是声道。
sampleRate是采样率。
status,用于返回错误信息。
----------------------------------------------------------------
AutoMutex lock(mLock);
LOGD("openOutputStream called for devices: 0x%08x", devices);
status_t err = BAD_VALUE;
AudioStreamOutALSA *out = 0;
// 从函数getDeviceForStrategy可知,得到只可能是一种设备。
// 而每种audio device只占一个bit。
// 此处是判断,若devices占一个以上的bit,则认为是参数错误。
if (devices & (devices - 1)) {
if (status) *status = err;
LOGD("openOutputStream called with bad devices");
return out;
}
// Find the appropriate alsa device
// 遍历mDeviceList,寻找合适的device。寻找的依据就是传进来的参数devices。
for(ALSAHandleList::iterator it = mDeviceList.begin();
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
mDeviceList是在何时被初始化的?
在类AudioHardwareALSA的构造函数中。
AudioHardwareALSA::AudioHardwareALSA() :
mMixer(0),
mMixerSpdif(0),
mMixerSgtl5000(0),
mALSADevice(0),
mAcousticDevice(0)
{
snd_lib_error_set_handler(&ALSAErrorHandler);
hw_module_t *module;
char snd_sgtl5000[32], snd_spdif[32];
char **cardname = new char*[MAXCARDSNUM];
for (int i = 0; i < MAXCARDSNUM; i++) {
cardname[i] = new char[128];
memset(cardname[i],0,128);
}
int id;
// 加载库
int err = hw_get_module(ALSA_HARDWARE_MODULE_ID,
(hw_module_t const**)&module);
// ALSA_HARDWARE_MODULE_ID的定义:#define ALSA_HARDWARE_MODULE_ID "alsa"
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
int hw_get_module(const char *id, const struct hw_module_t **module)
{
int status;
int i;
const struct hw_module_t *hmi = NULL;
char prop[PATH_MAX];
char path[PATH_MAX];
/*
* Here we rely on the fact that calling dlopen multiple times on
* the same .so will simply increment a refcount (and not load
* a new copy of the library).
* We also assume that dlopen() is thread-safe.
*/
// 从上面的注释可知,如果要加载的.so已经被加载,则只是对已加载的.so增加一个引用,而不是重新加载。
// 函数dlopen也是线程安全的。
/* Loop through the configuration variants looking for a module */
// 寻找合适的module,找不到,就用默认的。
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
if (i < HAL_VARIANT_KEYS_COUNT) {
if (property_get(variant_keys[i], prop, NULL) == 0) {
continue;
}
snprintf(path, sizeof(path), "%s/%s.%s.so",
HAL_LIBRARY_PATH1, id, prop);
// HAL_LIBRARY_PATH1的定义:#define HAL_LIBRARY_PATH1 "/system/lib/hw"
if (access(path, R_OK) == 0) break;
snprintf(path, sizeof(path), "%s/%s.%s.so",
HAL_LIBRARY_PATH2, id, prop);
// HAL_LIBRARY_PATH2的定义:#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
if (access(path, R_OK) == 0) break;
} else {
snprintf(path, sizeof(path), "%s/%s.default.so",
HAL_LIBRARY_PATH1, id);
if (access(path, R_OK) == 0) break;
}
}
status = -ENOENT;
if (i < HAL_VARIANT_KEYS_COUNT+1) {
/* load the module, if this fails, we're doomed, and we should not try
* to load a different variant. */
status = load(id, path, module);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
load函数的实现:
/**
* Load the file defined by the variant and if successful
* return the dlopen handle and the hmi.
* @return 0 = success, !0 = failure.
*/
static int load(const char *id,
const char *path,
const struct hw_module_t **pHmi)
{
int status;
void *handle;
struct hw_module_t *hmi;
/*
* load the symbols resolving undefined symbols before
* dlopen returns. Since RTLD_GLOBAL is not or'd in with
* RTLD_NOW the external symbols will not be global
*/
handle = dlopen(path, RTLD_NOW);
if (handle == NULL) {
char const *err_str = dlerror();
LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
status = -EINVAL;
goto done;
}
/* Get the address of the struct hal_module_info. */
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (struct hw_module_t *)dlsym(handle, sym);
if (hmi == NULL) {
LOGE("load: couldn't find symbol %s", sym);
status = -EINVAL;
goto done;
}
/* Check that the id matches */
if (strcmp(id, hmi->id) != 0) {
LOGE("load: id=%s != hmi->id=%s", id, hmi->id);
status = -EINVAL;
goto done;
}
hmi->dso = handle;
/* success */
status = 0;
done:
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
} else {
LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
----------------------------------------------------------------
}
return status;
}
----------------------------------------------------------------
if (err == 0) {
hw_device_t* device;
err = module->methods->open(module, ALSA_HARDWARE_NAME, &device);
// module的定义:hw_module_t *module;
// ALSA_HARDWARE_NAME的定义:#define ALSA_HARDWARE_NAME "alsa"
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构体hw_module_t的定义:
/**
* Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
* and the fields of this data structure must begin with hw_module_t
* followed by module specific information.
*/
typedef struct hw_module_t {
/** tag must be initialized to HARDWARE_MODULE_TAG */
uint32_t tag;
/** major version number for the module */
uint16_t version_major;
/** minor version number of the module */
uint16_t version_minor;
/** Identifier of module */
const char *id;
/** Name of this module */
const char *name;
/** Author/owner/implementor of the module */
const char *author;
/** Modules methods */
struct hw_module_methods_t* methods;
/** module's dso */
void* dso;
/** padding to 128 bytes, reserved for future use */
uint32_t reserved[32-7];
} hw_module_t;
----------------------------------------------------------------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
其中包含的hw_module_methods_t结构体:
typedef struct hw_module_methods_t {
/** Open a specific device */
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
} hw_module_methods_t;
----------------------------------------------------------------
看看对结构体hw_module_t赋值的一个地方:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static hw_module_methods_t s_module_methods = {
open : s_device_open
};
extern "C" const hw_module_t HAL_MODULE_INFO_SYM = {
tag : HARDWARE_MODULE_TAG,
version_major : 1,
version_minor : 0,
id : ALSA_HARDWARE_MODULE_ID,
name : "i.MX51 ALSA module",
author : "Freescale Semiconductor",
methods : &s_module_methods,
dso : 0,
reserved : {0,},
};
----------------------------------------------------------------
由此可见,我们调用的应该是函数s_device_open:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int s_device_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
alsa_device_t *dev;
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构体alsa_device_t的定义:
struct alsa_device_t {
hw_device_t common;
status_t (*init)(alsa_device_t *, ALSAHandleList &);
status_t (*open)(alsa_handle_t *, uint32_t, int);
status_t (*close)(alsa_handle_t *);
status_t (*route)(alsa_handle_t *, uint32_t, int);
};
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构体hw_device_t的定义:
/**
* Every device data structure must begin with hw_device_t
* followed by module specific public methods and attributes.
*/
typedef struct hw_device_t {
/** tag must be initialized to HARDWARE_DEVICE_TAG */
uint32_t tag;
/** version number for hw_device_t */
uint32_t version;
/** reference to the module this device belongs to */
// 此处保存了一个module的引用。
// 通过module,打开一个device,在设备中,保存其所属module的引用
struct hw_module_t* module;
/** padding reserved for future use */
uint32_t reserved[12];
/** Close this device */
int (*close)(struct hw_device_t* device);
} hw_device_t;
----------------------------------------------------------------
----------------------------------------------------------------
dev = (alsa_device_t *) malloc(sizeof(*dev));
if (!dev) return -ENOMEM;
memset(dev, 0, sizeof(*dev));
/* initialize the procs */
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (hw_module_t *) module;
dev->common.close = s_device_close;
// 这个函数,就是用来初始化mDeviceList的
dev->init = s_init;
// 这个函数马上就会被调用到
dev->open = s_open;
dev->close = s_close;
dev->route = s_route;
*device = &dev->common;
LOGD("i.MX51 ALSA module opened");
return 0;
}
----------------------------------------------------------------
if (err == 0) {
mALSADevice = (alsa_device_t *)device;
// 此处对mDeviceList进行了初始化。
// 我们已经知道,此处其实是调用的s_init函数
mALSADevice->init(mALSADevice, mDeviceList);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static status_t s_init(alsa_device_t *module, ALSAHandleList &list)
{
LOGD("Initializing devices for IMX51 ALSA module");
list.clear();
for (size_t i = 0; i < ARRAY_SIZE(_defaults); i++) {
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
_defaults的定义:
static alsa_handle_t _defaults[] = {
{
module : 0,
devices : IMX51_OUT_DEFAULT,
curDev : 0,
curMode : 0,
handle : 0,
format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
channels : 2,
sampleRate : DEFAULT_SAMPLE_RATE,
latency : 200000, // Desired Delay in usec
bufferSize : 6144, // Desired Number of samples
modPrivate : (void *)&setDefaultControls,
},
{
module : 0,
devices : IMX51_IN_DEFAULT,
curDev : 0,
curMode : 0,
handle : 0,
format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
channels : 2,
sampleRate : DEFAULT_SAMPLE_RATE,
latency : 250000, // Desired Delay in usec
bufferSize : 6144, // Desired Number of samples
modPrivate : (void *)&setDefaultControls,
},
};
----------------------------------------------------------------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构体alsa_handle_t的定义:
struct alsa_handle_t {
alsa_device_t * module;
uint32_t devices;
uint32_t curDev;
int curMode;
snd_pcm_t * handle;
snd_pcm_format_t format;
uint32_t channels;
uint32_t sampleRate;
unsigned int latency; // Delay in usec
unsigned int bufferSize; // Size of sample buffer
void * modPrivate;
int mmap; // mmap flags
};
----------------------------------------------------------------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构体snd_pcm_t的定义:
其实就是结构体_snd_pcm:
struct _snd_pcm {
437 snd_card_t *card;
438 unsigned int device; /* device number */
439 unsigned int info_flags;
440 unsigned short dev_class;
441 unsigned short dev_subclass;
442 char id[64];
443 char name[80];
444 snd_pcm_str_t streams[2];
445 struct semaphore open_mutex;
446 wait_queue_head_t open_wait;
447 void *private_data;
448 void (*private_free) (snd_pcm_t *pcm);
449 #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
450 snd_pcm_oss_t oss;
451 #endif
452 };
----------------------------------------------------------------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构体snd_card_t的定义:
----------------------------------------------------------------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构体snd_pcm_format_t的定义:
这个东东不是结构体,是个枚举,表示pcm 采样格式。
----------------------------------------------------------------
// 将传入的module引用保存到_defaults中
_defaults[i].module = module;
// 将_defaults中的成员push到mDeviceList中
list.push_back(_defaults[i]);
}
return NO_ERROR;
}
----------------------------------------------------------------
} else
LOGE("ALSA Module could not be opened!!!");
} else
LOGE("ALSA Module not found!!!");
/* found out sound cards in the system and new mixer controller for them*/
// cardname是一个指针数组,每一个成员指向一个char[128]数组。
err = findSoundCards(cardname);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
函数findSoundCards的实现:
static int findSoundCards(char **cardname)
{
int idx, dev, err;
snd_ctl_t *handle;
snd_hctl_t *hctlhandle;
snd_ctl_card_info_t *cardinfo;
snd_pcm_info_t *pcminfo;
char str[32];
snd_ctl_card_info_alloca(&cardinfo);
snd_pcm_info_alloca(&pcminfo);
snd_hctl_elem_t *elem;
snd_ctl_elem_id_t *id;
snd_ctl_elem_info_t *info;
snd_ctl_elem_id_alloca(&id);
snd_ctl_elem_info_alloca(&info);
idx = -1;
while (1) {
if ((err = snd_card_next(&idx)) < 0) {
LOGE("Card next error: %s\n", snd_strerror(err));
break;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
路径:external\alsa-lib\src\control\Cards.c
/**
* \brief Try to determine the next card.
* \param rcard pointer to card number
* \result zero if success, otherwise a negative error code
*
* Tries to determine the next card from given card number.
* If card number is -1, then the first available card is
* returned. If the result card number is -1, no more cards
* are available.
*/
int snd_card_next(int *rcard)
{
int card;
if (rcard == NULL)
return -EINVAL;
card = *rcard;
card = card < 0 ? 0 : card + 1;
for (; card < 32; card++) {
if (snd_card_load(card)) {
*rcard = card;
return 0;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Try to load the driver for a card.
* \param card Card number.
* \return 1 if driver is present, zero if driver is not present
*/
int snd_card_load(int card)
{
return !!(snd_card_load1(card) >= 0);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int snd_card_load1(int card)
{
int res;
char control[sizeof(SND_FILE_CONTROL) + 10];
// SND_FILE_CONTROL的定义:#define SND_FILE_CONTROL ALSA_DEVICE_DIRECTORY "controlC%i"
sprintf(control, SND_FILE_CONTROL, card);
res = snd_card_load2(control);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int snd_card_load2(const char *control)
{
int open_dev;
snd_ctl_card_info_t info;
open_dev = snd_open_device(control, O_RDONLY);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
路径:external\alsa-lib\include\Local.h
static inline int snd_open_device(const char *filename, int fmode)
{
int fd;
#ifdef O_CLOEXEC
fmode |= O_CLOEXEC;
#endif
fd = open(filename, fmode);
/* open with resmgr */
#ifdef SUPPORT_RESMGR
if (fd < 0) {
if (errno == EAGAIN || errno == EBUSY)
return fd;
if (! access(filename, F_OK))
fd = rsm_open_device(filename, fmode);
}
#endif
if (fd >= 0)
fcntl(fd, F_SETFD, FD_CLOEXEC);
return fd;
}
----------------------------------------------------------------
if (open_dev >= 0) {
// 调用的应该是kernel中的snd_ctl_ioctl函数
if (ioctl(open_dev, SNDRV_CTL_IOCTL_CARD_INFO, &info) < 0) {
int err = -errno;
close(open_dev);
return err;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct snd_ctl_file *ctl;
struct snd_card *card;
struct snd_kctl_ioctl *p;
void __user *argp = (void __user *)arg;
int __user *ip = argp;
int err;
ctl = file->private_data;
card = ctl->card;
if (snd_BUG_ON(!card))
return -ENXIO;
switch (cmd) {
case SNDRV_CTL_IOCTL_PVERSION:
return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
case SNDRV_CTL_IOCTL_CARD_INFO:
return snd_ctl_card_info(card, ctl, cmd, argp);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
unsigned int cmd, void __user *arg)
{
struct snd_ctl_card_info *info;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (! info)
return -ENOMEM;
down_read(&snd_ioctl_rwsem);
info->card = card->number;
strlcpy(info->id, card->id, sizeof(info->id));
strlcpy(info->driver, card->driver, sizeof(info->driver));
strlcpy(info->name, card->shortname, sizeof(info->name));
strlcpy(info->longname, card->longname, sizeof(info->longname));
strlcpy(info->mixername, card->mixername, sizeof(info->mixername));
strlcpy(info->components, card->components, sizeof(info->components));
up_read(&snd_ioctl_rwsem);
if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {
kfree(info);
return -EFAULT;
}
kfree(info);
return 0;
}
----------------------------------------------------------------
case SNDRV_CTL_IOCTL_ELEM_LIST:
return snd_ctl_elem_list(card, argp);
case SNDRV_CTL_IOCTL_ELEM_INFO:
return snd_ctl_elem_info_user(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_READ:
return snd_ctl_elem_read_user(card, argp);
case SNDRV_CTL_IOCTL_ELEM_WRITE:
return snd_ctl_elem_write_user(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_LOCK:
return snd_ctl_elem_lock(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
return snd_ctl_elem_unlock(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_ADD:
return snd_ctl_elem_add_user(ctl, argp, 0);
case SNDRV_CTL_IOCTL_ELEM_REPLACE:
return snd_ctl_elem_add_user(ctl, argp, 1);
case SNDRV_CTL_IOCTL_ELEM_REMOVE:
return snd_ctl_elem_remove(ctl, argp);
case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
return snd_ctl_subscribe_events(ctl, ip);
case SNDRV_CTL_IOCTL_TLV_READ:
return snd_ctl_tlv_ioctl(ctl, argp, 0);
case SNDRV_CTL_IOCTL_TLV_WRITE:
return snd_ctl_tlv_ioctl(ctl, argp, 1);
case SNDRV_CTL_IOCTL_TLV_COMMAND:
return snd_ctl_tlv_ioctl(ctl, argp, -1);
case SNDRV_CTL_IOCTL_POWER:
return -ENOPROTOOPT;
case SNDRV_CTL_IOCTL_POWER_STATE:
#ifdef CONFIG_PM
return put_user(card->power_state, ip) ? -EFAULT : 0;
#else
return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
#endif
}
down_read(&snd_ioctl_rwsem);
list_for_each_entry(p, &snd_control_ioctls, list) {
err = p->fioctl(card, ctl, cmd, arg);
if (err != -ENOIOCTLCMD) {
up_read(&snd_ioctl_rwsem);
return err;
}
}
up_read(&snd_ioctl_rwsem);
snd_printdd("unknown ioctl = 0x%x\n", cmd);
return -ENOTTY;
}
----------------------------------------------------------------
close(open_dev);
return info.card;
} else {
return -errno;
}
}
----------------------------------------------------------------
#ifdef SUPPORT_ALOAD
if (res < 0) {
// SND_FILE_LOAD的定义:#define SND_FILE_LOAD ALOAD_DEVICE_DIRECTORY "aloadC%i"
char aload[sizeof(SND_FILE_LOAD) + 10];
sprintf(aload, SND_FILE_LOAD, card);
res = snd_card_load2(aload);
}
#endif
return res;
}
----------------------------------------------------------------
}
----------------------------------------------------------------
}
*rcard = -1;
return 0;
}
----------------------------------------------------------------
if (idx < 0)
break;
sprintf(str, "hw:CARD=%i", idx);
if ((err = snd_ctl_open(&handle, str, 0)) < 0) {
LOGE("Open error: %s\n", snd_strerror(err));
continue;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Opens a CTL
* \param ctlp Returned CTL handle
* \param name ASCII identifier of the CTL handle
* \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC)
* \return 0 on success otherwise a negative error code
*/
int snd_ctl_open(snd_ctl_t **ctlp, const char *name, int mode)
{
int err;
assert(ctlp && name);
err = snd_config_update();
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Updates #snd_config by rereading the global configuration files (if needed).
* \return 0 if #snd_config was up to date, 1 if #snd_config was
* updated, otherwise a negative error code.
*
* \warning Whenever #snd_config is updated, all string pointers and
* configuration node handles previously obtained from it may become
* invalid.
*
* \par Errors:
* Any errors encountered when parsing the input or returned by hooks or
* functions.
*
* \par Conforming to:
* LSB 3.2
*/
int snd_config_update(void)
{
int err;
#ifdef HAVE_LIBPTHREAD
pthread_mutex_lock(&snd_config_update_mutex);
#endif
err = snd_config_update_r(&snd_config, &snd_config_global_update, NULL);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Updates a configuration tree by rereading the configuration files (if needed).
* \param[in,out] _top Address of the handle to the top-level node.
* \param[in,out] _update Address of a pointer to private update information.
* \param[in] cfgs A list of configuration file names, delimited with ':'.
* If \p cfgs is \c NULL, the default global
* configuration file is used.
* \return 0 if \a _top was up to date, 1 if the configuration files
* have been reread, otherwise a negative error code.
*
* The variables pointed to by \a _top and \a _update can be initialized
* to \c NULL before the first call to this function. The private
* update information holds information about all used configuration
* files that allows this function to detects changes to them; this data
* can be freed with #snd_config_update_free.
*
* The global configuration files are specified in the environment variable
* \c ALSA_CONFIG_PATH.
*
* \warning If the configuration tree is reread, all string pointers and
* configuration node handles previously obtained from this tree become
* invalid.
*
* \par Errors:
* Any errors encountered when parsing the input or returned by hooks or
* functions.
*/
int snd_config_update_r(snd_config_t **_top, snd_config_update_t **_update, const char *cfgs)
{
int err;
const char *configs, *c;
unsigned int k;
size_t l;
snd_config_update_t *local;
snd_config_update_t *update;
snd_config_t *top;
assert(_top && _update);
top = *_top;
update = *_update;
configs = cfgs;
if (!configs) {
configs = getenv(ALSA_CONFIG_PATH_VAR);
if (!configs || !*configs)
configs = ALSA_CONFIG_PATH_DEFAULT;
}
for (k = 0, c = configs; (l = strcspn(c, ": ")) > 0; ) {
c += l;
k++;
if (!*c)
break;
c++;
}
if (k == 0) {
local = NULL;
goto _reread;
}
local = (snd_config_update_t *)calloc(1, sizeof(snd_config_update_t));
if (!local)
return -ENOMEM;
local->count = k;
local->finfo = calloc(local->count, sizeof(struct finfo));
if (!local->finfo) {
free(local);
return -ENOMEM;
}
for (k = 0, c = configs; (l = strcspn(c, ": ")) > 0; ) {
char name[l + 1];
memcpy(name, c, l);
name[l] = 0;
err = snd_user_file(name, &local->finfo[k].name);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#ifdef HAVE_WORDEXP_H
#include <wordexp.h>
#include <assert.h>
int snd_user_file(const char *file, char **result)
{
wordexp_t we;
int err;
assert(file && result);
err = wordexp(file, &we, WRDE_NOCMD);
switch (err) {
case WRDE_NOSPACE:
return -ENOMEM;
case 0:
if (we.we_wordc == 1)
break;
/* fall thru */
default:
wordfree(&we);
return -EINVAL;
}
*result = strdup(we.we_wordv[0]);
if (*result == NULL)
return -ENOMEM;
wordfree(&we);
return 0;
}
#else /* !HAVE_WORDEXP_H */
/* just copy the string - would be nicer to expand by ourselves, though... */
int snd_user_file(const char *file, char **result)
{
*result = strdup(file);
if (! *result)
return -ENOMEM;
return 0;
}
#endif /* HAVE_WORDEXP_H */
----------------------------------------------------------------
if (err < 0)
goto _end;
c += l;
k++;
if (!*c)
break;
c++;
}
for (k = 0; k < local->count; ++k) {
struct stat st;
struct finfo *lf = &local->finfo[k];
if (stat(lf->name, &st) >= 0) {
lf->dev = st.st_dev;
lf->ino = st.st_ino;
lf->mtime = st.st_mtime;
} else {
SNDERR("Cannot access file %s", lf->name);
free(lf->name);
memmove(&local->finfo[k], &local->finfo[k+1], sizeof(struct finfo) * (local->count - k - 1));
k--;
local->count--;
}
}
if (!update)
goto _reread;
if (local->count != update->count)
goto _reread;
for (k = 0; k < local->count; ++k) {
struct finfo *lf = &local->finfo[k];
struct finfo *uf = &update->finfo[k];
if (strcmp(lf->name, uf->name) != 0 ||
lf->dev != uf->dev ||
lf->ino != uf->ino ||
lf->mtime != uf->mtime)
goto _reread;
}
err = 0;
_end:
if (err < 0) {
if (top) {
snd_config_delete(top);
*_top = NULL;
}
if (update) {
snd_config_update_free(update);
*_update = NULL;
}
}
if (local)
snd_config_update_free(local);
return err;
_reread:
*_top = NULL;
*_update = NULL;
if (update) {
snd_config_update_free(update);
update = NULL;
}
if (top) {
snd_config_delete(top);
top = NULL;
}
err = snd_config_top(&top);
if (err < 0)
goto _end;
if (!local)
goto _skip;
for (k = 0; k < local->count; ++k) {
snd_input_t *in;
err = snd_input_stdio_open(&in, local->finfo[k].name, "r");
if (err >= 0) {
err = snd_config_load(top, in);
snd_input_close(in);
if (err < 0) {
SNDERR("%s may be old or corrupted: consider to remove or fix it", local->finfo[k].name);
goto _end;
}
} else {
SNDERR("cannot access file %s", local->finfo[k].name);
}
}
_skip:
err = snd_config_hooks(top, NULL);
if (err < 0) {
SNDERR("hooks failed, removing configuration");
goto _end;
}
*_top = top;
*_update = local;
return 1;
}
----------------------------------------------------------------
#ifdef HAVE_LIBPTHREAD
pthread_mutex_unlock(&snd_config_update_mutex);
#endif
return err;
}
----------------------------------------------------------------
if (err < 0)
return err;
return snd_ctl_open_noupdate(ctlp, snd_config, name, mode);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int snd_ctl_open_noupdate(snd_ctl_t **ctlp, snd_config_t *root, const char *name, int mode)
{
int err;
snd_config_t *ctl_conf;
err = snd_config_search_definition(root, "ctl", name, &ctl_conf);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Searches for a definition in a configuration tree, using
* aliases and expanding hooks and arguments.
* \param[in] config Handle to the configuration (sub)tree to search.
* \param[in] base Implicit key base, or \c NULL for none.
* \param[in] name Key suffix, optionally with arguments.
* \param[out] result The function puts the handle to the expanded found
* node at the address specified by \a result.
* \return A non-negative value if successful, otherwise a negative error code.
*
* This functions searches for a child node of \a config, allowing
* aliases and expanding hooks, like #snd_config_search_alias_hooks.
*
* If \a name contains a colon (:), the rest of the string after the
* colon contains arguments that are expanded as with
* #snd_config_expand.
*
* In any case, \a result is a new node that must be freed by the
* caller.
*
* \par Errors:
* <dl>
* <dt>-ENOENT<dd>An id in \a key or an alias id does not exist.
* <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is
* not a compound node.
* </dl>
* Additionally, any errors encountered when parsing the hook
* definitions or arguments, or returned by (hook) functions.
*/
int snd_config_search_definition(snd_config_t *config,
const char *base, const char *name,
snd_config_t **result)
{
snd_config_t *conf;
char *key;
const char *args = strchr(name, ':');
int err;
if (args) {
args++;
key = alloca(args - name);
memcpy(key, name, args - name - 1);
key[args - name - 1] = '\0';
} else {
key = (char *) name;
}
/*
* if key contains dot (.), the implicit base is ignored
* and the key starts from root given by the 'config' parameter
*/
err = snd_config_search_alias_hooks(config, strchr(key, '.') ? NULL : base, key, &conf);
if (err < 0)
return err;
return snd_config_expand(conf, config, args, NULL, result);
}
----------------------------------------------------------------
if (err < 0) {
SNDERR("Invalid CTL %s", name);
return err;
}
err = snd_ctl_open_conf(ctlp, name, root, ctl_conf, mode);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int snd_ctl_open_conf(snd_ctl_t **ctlp, const char *name,
snd_config_t *ctl_root, snd_config_t *ctl_conf, int mode)
{
const char *str;
char *buf = NULL, *buf1 = NULL;
int err;
snd_config_t *conf, *type_conf = NULL;
snd_config_iterator_t i, next;
const char *lib = NULL, *open_name = NULL;
const char *id;
int (*open_func)(snd_ctl_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
#ifndef PIC
extern void *snd_control_open_symbols(void);
#endif
void *h = NULL;
if (snd_config_get_type(ctl_conf) != SND_CONFIG_TYPE_COMPOUND) {
if (name)
SNDERR("Invalid type for CTL %s definition", name);
else
SNDERR("Invalid type for CTL definition");
return -EINVAL;
}
err = snd_config_search(ctl_conf, "type", &conf);
if (err < 0) {
SNDERR("type is not defined");
return err;
}
err = snd_config_get_id(conf, &id);
if (err < 0) {
SNDERR("unable to get id");
return err;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", id);
return err;
}
err = snd_config_search_definition(ctl_root, "ctl_type", str, &type_conf);
if (err >= 0) {
if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) {
SNDERR("Invalid type for CTL type %s definition", str);
goto _err;
}
snd_config_for_each(i, next, type_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {
err = snd_config_get_string(n, &lib);
if (err < 0) {
SNDERR("Invalid type for %s", id);
goto _err;
}
continue;
}
if (strcmp(id, "open") == 0) {
err = snd_config_get_string(n, &open_name);
if (err < 0) {
SNDERR("Invalid type for %s", id);
goto _err;
}
continue;
}
SNDERR("Unknown field %s", id);
err = -EINVAL;
goto _err;
}
}
if (!open_name) {
buf = malloc(strlen(str) + 32);
if (buf == NULL) {
err = -ENOMEM;
goto _err;
}
open_name = buf;
sprintf(buf, "_snd_ctl_%s_open", str);
}
if (!lib) {
const char *const *build_in = build_in_ctls;
while (*build_in) {
if (!strcmp(*build_in, str))
break;
build_in++;
}
if (*build_in == NULL) {
buf1 = malloc(strlen(str) + sizeof(ALSA_PLUGIN_DIR) + 32);
if (buf1 == NULL) {
err = -ENOMEM;
goto _err;
}
lib = buf1;
sprintf(buf1, "%s/libasound_module_ctl_%s.so", ALSA_PLUGIN_DIR, str);
}
}
#ifndef PIC
snd_control_open_symbols();
#endif
open_func = snd_dlobj_cache_lookup(open_name);
if (open_func) {
err = 0;
goto _err;
}
h = snd_dlopen(lib, RTLD_NOW);
if (h)
open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_CONTROL_DLSYM_VERSION));
err = 0;
if (!h) {
SNDERR("Cannot open shared library %s", lib);
err = -ENOENT;
} else if (!open_func) {
SNDERR("symbol %s is not defined inside %s", open_name, lib);
snd_dlclose(h);
err = -ENXIO;
}
_err:
if (type_conf)
snd_config_delete(type_conf);
if (err >= 0) {
err = open_func(ctlp, name, ctl_root, ctl_conf, mode);
if (err >= 0) {
if (h /*&& (mode & SND_CTL_KEEP_ALIVE)*/) {
snd_dlobj_cache_add(open_name, h, open_func);
h = NULL;
}
(*ctlp)->dl_handle = h;
err = 0;
} else {
if (h)
snd_dlclose(h);
}
}
free(buf);
free(buf1);
return err;
}
----------------------------------------------------------------
snd_config_delete(ctl_conf);
return err;
}
----------------------------------------------------------------
}
----------------------------------------------------------------
// 函数snd_ctl_card_info前面已经见过
if ((err = snd_ctl_card_info(handle, cardinfo)) < 0) {
LOGE("HW info error: %s\n", snd_strerror(err));
continue;
}
LOGD("Soundcard #%i:\n", idx + 1);
LOGD(" card - %i\n", snd_ctl_card_info_get_card(cardinfo));
LOGD(" id - '%s'\n", snd_ctl_card_info_get_id(cardinfo));
LOGD(" driver - '%s'\n", snd_ctl_card_info_get_driver(cardinfo));
LOGD(" name - '%s'\n", snd_ctl_card_info_get_name(cardinfo));
LOGD(" longname - '%s'\n", snd_ctl_card_info_get_longname(cardinfo));
LOGD(" mixername - '%s'\n", snd_ctl_card_info_get_mixername(cardinfo));
LOGD(" components - '%s'\n", snd_ctl_card_info_get_components(cardinfo));
strcpy(cardname[idx], snd_ctl_card_info_get_name(cardinfo));
LOGD("\n\n-----get cart name and id: %s : %d",cardname[idx],idx);
snd_ctl_close(handle);
if ((err = snd_hctl_open(&hctlhandle, str, 0)) < 0) {
LOGE("Control %s open error: %s", str, snd_strerror(err));
return err;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Opens an HCTL
* \param hctlp Returned HCTL handle
* \param name ASCII identifier of the underlying CTL handle
* \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC)
* \return 0 on success otherwise a negative error code
*/
int snd_hctl_open(snd_hctl_t **hctlp, const char *name, int mode)
{
snd_ctl_t *ctl;
int err;
if ((err = snd_ctl_open(&ctl, name, mode)) < 0)
return err;
err = snd_hctl_open_ctl(hctlp, ctl);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Opens an HCTL
* \param hctlp Returned HCTL handle
* \param ctl underlying CTL handle
* \return 0 on success otherwise a negative error code
*/
int snd_hctl_open_ctl(snd_hctl_t **hctlp, snd_ctl_t *ctl)
{
snd_hctl_t *hctl;
assert(hctlp);
*hctlp = NULL;
if ((hctl = (snd_hctl_t *)calloc(1, sizeof(snd_hctl_t))) == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&hctl->elems);
hctl->ctl = ctl;
*hctlp = hctl;
return 0;
}
----------------------------------------------------------------
if (err < 0)
snd_ctl_close(ctl);
return err;
}
----------------------------------------------------------------
if ((err = snd_hctl_load(hctlhandle)) < 0) {
LOGE("Control %s local error: %s\n", str, snd_strerror(err));
return err;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Load an HCTL with all elements and sort them
* \param hctl HCTL handle
* \return 0 on success otherwise a negative error code
*/
int snd_hctl_load(snd_hctl_t *hctl)
{
snd_ctl_elem_list_t list;
int err = 0;
unsigned int idx;
assert(hctl);
assert(hctl->ctl);
assert(hctl->count == 0);
assert(list_empty(&hctl->elems));
memset(&list, 0, sizeof(list));
if ((err = snd_ctl_elem_list(hctl->ctl, &list)) < 0)
goto _end;
while (list.count != list.used) {
err = snd_ctl_elem_list_alloc_space(&list, list.count);
if (err < 0)
goto _end;
if ((err = snd_ctl_elem_list(hctl->ctl, &list)) < 0)
goto _end;
}
if (hctl->alloc < list.count) {
hctl->alloc = list.count;
free(hctl->pelems);
hctl->pelems = malloc(hctl->alloc * sizeof(*hctl->pelems));
if (!hctl->pelems) {
err = -ENOMEM;
goto _end;
}
}
for (idx = 0; idx < list.count; idx++) {
snd_hctl_elem_t *elem;
elem = calloc(1, sizeof(snd_hctl_elem_t));
if (elem == NULL) {
snd_hctl_free(hctl);
err = -ENOMEM;
goto _end;
}
elem->id = list.pids[idx];
elem->hctl = hctl;
elem->compare_weight = get_compare_weight(&elem->id);
hctl->pelems[idx] = elem;
list_add_tail(&elem->list, &hctl->elems);
hctl->count++;
}
if (!hctl->compare)
hctl->compare = snd_hctl_compare_default;
snd_hctl_sort(hctl);
for (idx = 0; idx < hctl->count; idx++) {
int res = snd_hctl_throw_event(hctl, SNDRV_CTL_EVENT_MASK_ADD,
hctl->pelems[idx]);
if (res < 0)
return res;
}
err = snd_ctl_subscribe_events(hctl->ctl, 1);
_end:
free(list.pids);
return err;
}
----------------------------------------------------------------
for (elem = snd_hctl_first_elem(hctlhandle); elem; elem = snd_hctl_elem_next(elem)) {
if ((err = snd_hctl_elem_info(elem, info)) < 0) {
LOGE("Control %s snd_hctl_elem_info error: %s\n", str, snd_strerror(err));
return err;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Get information for an HCTL element
* \param elem HCTL element
* \param info HCTL element information
* \return 0 otherwise a negative error code on failure
*/
int snd_hctl_elem_info(snd_hctl_elem_t *elem, snd_ctl_elem_info_t *info)
{
assert(elem);
assert(elem->hctl);
assert(info);
info->id = elem->id;
return snd_ctl_elem_info(elem->hctl->ctl, info);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Get CTL element information
* \param ctl CTL handle
* \param info CTL element id/information pointer
* \return 0 on success otherwise a negative error code
*/
int snd_ctl_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info)
{
assert(ctl && info && (info->id.name[0] || info->id.numid));
return ctl->ops->element_info(ctl, info);
}
----------------------------------------------------------------
}
----------------------------------------------------------------
snd_hctl_elem_get_id(elem, id);
show_control_id(id);
}
snd_hctl_close(hctlhandle);
}
snd_config_update_free_global();
return 0;
}
----------------------------------------------------------------
// 根据cardname创建一个mixer
if (err == 0) {
for (id = 0; id < MAXCARDSNUM; id++) {
if(cardname[id] && strstr(cardname[id],SPDIF)){
LOGD(" CARD NAME: %s ID %d", cardname[id],id);
sprintf(snd_spdif,"hw:0%d",id);
sprintf(snd_spdif,"hw:CARD=%d",id);
mMixerSpdif = new ALSAMixer(snd_spdif);
}else if (cardname[id] && strstr(cardname[id],SGTL5000)){
LOGD(" CARD NAME: %s ID %d", cardname[id],id);
sprintf(snd_sgtl5000,"hw:0%d",id);
sprintf(snd_sgtl5000,"hw:CARD=%d",id);
mMixerSgtl5000 = new ALSAMixer(snd_sgtl5000);
}
}
} else {
LOGE("Don't find any Sound cards, use default");
mMixerSgtl5000 = new ALSAMixer("hw:00");
mMixerSpdif = new ALSAMixer("hw:00");
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ALSAMixer的构造函数:
ALSAMixer::ALSAMixer(const char *sndcard)
{
int err;
LOGI(" Init ALSAMIXER for SNDCARD : %s",sndcard);
mixerMasterProp =
ALSA_PROP(AudioSystem::DEVICE_OUT_ALL, "master", "PCM", "Capture");
mixerProp = {
ALSA_PROP(AudioSystem::DEVICE_OUT_EARPIECE, "earpiece", "Earpiece", "Capture"),
ALSA_PROP(AudioSystem::DEVICE_OUT_SPEAKER, "speaker", "Speaker", ""),
ALSA_PROP(AudioSystem::DEVICE_OUT_WIRED_HEADSET, "headset", "Headphone", "Capture"),
ALSA_PROP(AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, "bluetooth.sco", "Bluetooth", "Bluetooth Capture"),
ALSA_PROP(AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, "bluetooth.a2dp", "Bluetooth A2DP", "Bluetooth A2DP Capture"),
ALSA_PROP(static_cast<AudioSystem::audio_devices>(0), "", NULL, NULL)
};
initMixer (&mMixer[SND_PCM_STREAM_PLAYBACK], sndcard);
initMixer (&mMixer[SND_PCM_STREAM_CAPTURE], sndcard);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
函数initMixer的实现:
static int initMixer (snd_mixer_t **mixer, const char *name)
{
int err;
if ((err = snd_mixer_open(mixer, 0)) < 0) {
LOGE("Unable to open mixer: %s", snd_strerror(err));
return err;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Opens an empty mixer
* \param mixerp Returned mixer handle
* \param mode Open mode
* \return 0 on success otherwise a negative error code
*/
int snd_mixer_open(snd_mixer_t **mixerp, int mode ATTRIBUTE_UNUSED)
{
snd_mixer_t *mixer;
assert(mixerp);
mixer = calloc(1, sizeof(*mixer));
if (mixer == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&mixer->slaves);
INIT_LIST_HEAD(&mixer->classes);
INIT_LIST_HEAD(&mixer->elems);
mixer->compare = snd_mixer_compare_default;
*mixerp = mixer;
return 0;
}
----------------------------------------------------------------
if ((err = snd_mixer_attach(*mixer, name)) < 0) {
LOGW("Unable to attach mixer to device %s: %s",
name, snd_strerror(err));
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Attach an HCTL specified with the CTL device name to an opened mixer
* \param mixer Mixer handle
* \param name HCTL name (see #snd_hctl_open)
* \return 0 on success otherwise a negative error code
*/
int snd_mixer_attach(snd_mixer_t *mixer, const char *name)
{
snd_hctl_t *hctl;
int err;
err = snd_hctl_open(&hctl, name, 0);
if (err < 0)
return err;
err = snd_mixer_attach_hctl(mixer, hctl);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Attach an HCTL to an opened mixer
* \param mixer Mixer handle
* \param hctl the HCTL to be attached
* \return 0 on success otherwise a negative error code
*/
int snd_mixer_attach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl)
{
snd_mixer_slave_t *slave;
int err;
assert(hctl);
slave = calloc(1, sizeof(*slave));
if (slave == NULL)
return -ENOMEM;
err = snd_hctl_nonblock(hctl, 1);
if (err < 0) {
snd_hctl_close(hctl);
free(slave);
return err;
}
snd_hctl_set_callback(hctl, hctl_event_handler);
snd_hctl_set_callback_private(hctl, mixer);
slave->hctl = hctl;
list_add_tail(&slave->list, &mixer->slaves);
return 0;
}
----------------------------------------------------------------
if (err < 0) {
snd_hctl_close(hctl);
return err;
}
return 0;
}
----------------------------------------------------------------
if ((err = snd_mixer_attach(*mixer, "hw:00")) < 0) {
LOGE("Unable to attach mixer to device default: %s",
snd_strerror(err));
snd_mixer_close (*mixer);
*mixer = NULL;
return err;
}
}
if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0) {
LOGE("Unable to register mixer elements: %s", snd_strerror(err));
snd_mixer_close (*mixer);
*mixer = NULL;
return err;
}
// Get the mixer controls from the kernel
if ((err = snd_mixer_load(*mixer)) < 0) {
LOGE("Unable to load mixer elements: %s", snd_strerror(err));
snd_mixer_close (*mixer);
*mixer = NULL;
return err;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Load a mixer elements
* \param mixer Mixer handle
* \return 0 on success otherwise a negative error code
*/
int snd_mixer_load(snd_mixer_t *mixer)
{
struct list_head *pos;
list_for_each(pos, &mixer->slaves) {
int err;
snd_mixer_slave_t *s;
s = list_entry(pos, snd_mixer_slave_t, list);
err = snd_hctl_load(s->hctl);
if (err < 0)
return err;
}
return 0;
}
----------------------------------------------------------------
return 0;
}
----------------------------------------------------------------
snd_mixer_selem_id_t *sid;
snd_mixer_selem_id_alloca(&sid);
for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {
mixer_info_t *info = mixerMasterProp[i].mInfo = new mixer_info_t;
property_get (mixerMasterProp[i].propName,
info->name,
mixerMasterProp[i].propDefault);
for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
elem;
elem = snd_mixer_elem_next(elem)) {
if (!snd_mixer_selem_is_active(elem))
continue;
snd_mixer_selem_get_id(elem, sid);
// Find PCM playback volume control element.
const char *elementName = snd_mixer_selem_id_get_name(sid);
if (info->elem == NULL &&
strcmp(elementName, info->name) == 0 &&
hasVolume[i] (elem)) {
info->elem = elem;
getVolumeRange[i] (elem, &info->min, &info->max);
info->volume = info->max;
setVol[i] (elem, info->volume);
if (i == SND_PCM_STREAM_PLAYBACK &&
snd_mixer_selem_has_playback_switch (elem))
snd_mixer_selem_set_playback_switch_all (elem, 1);
break;
}
}
LOGV("Mixer: master '%s' %s.", info->name, info->elem ? "found" : "not found");
for (int j = 0; mixerProp[j][i].device; j++) {
mixer_info_t *info = mixerProp[j][i].mInfo = new mixer_info_t;
property_get (mixerProp[j][i].propName,
info->name,
mixerProp[j][i].propDefault);
for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
elem;
elem = snd_mixer_elem_next(elem)) {
if (!snd_mixer_selem_is_active(elem))
continue;
snd_mixer_selem_get_id(elem, sid);
// Find PCM playback volume control element.
const char *elementName = snd_mixer_selem_id_get_name(sid);
if (info->elem == NULL &&
strcmp(elementName, info->name) == 0 &&
hasVolume[i] (elem)) {
info->elem = elem;
getVolumeRange[i] (elem, &info->min, &info->max);
info->volume = info->max;
setVol[i] (elem, info->volume);
if (i == SND_PCM_STREAM_PLAYBACK &&
snd_mixer_selem_has_playback_switch (elem))
snd_mixer_selem_set_playback_switch_all (elem, 1);
break;
}
}
LOGV("Mixer: route '%s' %s.", info->name, info->elem ? "found" : "not found");
}
}
LOGV("mixer initialized.");
}
----------------------------------------------------------------
for (int i = 0; i < MAXCARDSNUM; i++) {
delete []cardname[i];
}
delete []cardname;
mCurCard = new char[128];
if (!mCurCard)
LOGE("allocate memeory to store current sound card name fail");
memset(mCurCard,0,sizeof(mCurCard));
/* set current card as sgtl5000 default */
if(mMixerSgtl5000)
{
strcpy(mCurCard,SGTL5000);
mMixer = mMixerSgtl5000;
}else if(mMixerSpdif)
{
strcpy(mCurCard,SPDIF);
mMixer = mMixerSpdif;
}
err = hw_get_module(ACOUSTICS_HARDWARE_MODULE_ID,
(hw_module_t const**)&module);
if (err == 0) {
hw_device_t* device;
err = module->methods->open(module, ACOUSTICS_HARDWARE_NAME, &device);
if (err == 0)
mAcousticDevice = (acoustic_device_t *)device;
else
LOGE("Acoustics Module not found.");
}
}
----------------------------------------------------------------
it != mDeviceList.end(); ++it)
if (it->devices & devices) {
// 此处调用的其实是函数s_open
// devices就是我们刚开始看参数时看的那个东东
err = mALSADevice->open(&(*it), devices, mode());
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static status_t s_open(alsa_handle_t *handle, uint32_t devices, int mode)
{
// Close off previously opened device.
// It would be nice to determine if the underlying device actually
// changes, but we might be recovering from an error or manipulating
// mixer settings (see asound.conf).
//
s_close(handle);
LOGD("open called for devices %08x in mode %d...", devices, mode);
const char *stream = streamName(handle);
// 在这儿使用到了devices
const char *devName = deviceName(handle, devices, mode, 1);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//card_device =0, return the card name, card_device=1, return the card device name
const char *deviceName(alsa_handle_t *alsa_handle, uint32_t device, int mode, int card_device)
{
snd_ctl_t *handle;
int card, err, dev, idx;
snd_ctl_card_info_t *info;
snd_pcm_info_t *pcminfo;
snd_ctl_card_info_alloca(&info);
snd_pcm_info_alloca(&pcminfo);
int cardnum = 0;
char value[PROPERTY_VALUE_MAX];
snd_pcm_stream_t stream = direction(alsa_handle);
bool havespdifdevice = false;
bool havesgtldevice = false;
card = -1;
if (snd_card_next(&card) < 0 || card < 0) {
LOGD("no soundcards found...");
return "default";
}
LOGD("**** List of %s Hardware Devices ****\n",
snd_pcm_stream_name(stream));
while (card >= 0) {
char name[32];
sprintf(name, "hw:%d", card);
if ((err = snd_ctl_open(&handle, name, 0)) < 0) {
LOGD("control open (%i): %s", card, snd_strerror(err));
goto next_card;
}
if ((err = snd_ctl_card_info(handle, info)) < 0) {
LOGD("control hardware info (%i): %s", card, snd_strerror(err));
snd_ctl_close(handle);
goto next_card;
}
dev = -1;
while (1) {
unsigned int count;
if (snd_ctl_pcm_next_device(handle, &dev)<0)
LOGD("snd_ctl_pcm_next_device");
if (dev < 0)
break;
snd_pcm_info_set_device(pcminfo, dev);
snd_pcm_info_set_subdevice(pcminfo, 0);
snd_pcm_info_set_stream(pcminfo, stream);
if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
if (err != -ENOENT)
LOGD("control digital audio info (%i): %s", card, snd_strerror(err));
continue;
}
LOGD("card %i: %s [%s], device %i: %s [%s]\n",
card, snd_ctl_card_info_get_id(info), snd_ctl_card_info_get_name(info),
dev,
snd_pcm_info_get_id(pcminfo),
snd_pcm_info_get_name(pcminfo));
if(strcmp(snd_pcm_info_get_id(pcminfo),"IMX SPDIF mxc spdif-0")==0) {
if(card_device==0) sprintf(spdifcardname, "hw:0%d", card);
else sprintf(spdifcardname, "hw:%d,%d", card, dev);
havespdifdevice = true;
}
if(strcmp(snd_pcm_info_get_id(pcminfo),"SGTL5000 SGTL5000-0")==0) {
if(card_device==0) sprintf(sgtlcardname, "hw:0%d", card);
else sprintf(sgtlcardname, "hw:%d,%d", card, dev);
havesgtldevice = true;
}
cardnum++;
}
snd_ctl_close(handle);
next_card:
if (snd_card_next(&card) < 0) {
LOGD("snd_card_next");
break;
}
}
property_get("ro.HDMI_AUDIO_OUTPUT", value, "");
// 只是在这儿用了一下
if((device & AudioSystem::DEVICE_OUT_WIRED_HDMI) && havespdifdevice && (strcmp(value, "1") == 0))
{
return spdifcardname;
}else if(havesgtldevice)
{
return sgtlcardname;
}
return "default";
}
----------------------------------------------------------------
// The PCM stream is opened in blocking mode, per ALSA defaults. The
// AudioFlinger seems to assume blocking mode too, so asynchronous mode
// should not be used.
int err = snd_pcm_open(&handle->handle, devName, direction(handle), 0);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Opens a PCM
* \param pcmp Returned PCM handle
* \param name ASCII identifier of the PCM handle
* \param stream Wanted stream
* \param mode Open mode (see #SND_PCM_NONBLOCK, #SND_PCM_ASYNC)
* \return 0 on success otherwise a negative error code
*/
int snd_pcm_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_stream_t stream, int mode)
{
int err;
assert(pcmp && name);
// 函数snd_config_update前面已经见过
err = snd_config_update();
if (err < 0)
return err;
// 前面已经见过函数snd_ctl_open_noupdate
return snd_pcm_open_noupdate(pcmp, snd_config, name, stream, mode, 0);
}
----------------------------------------------------------------
if (err < 0) {
LOGE("Failed to Initialize any ALSA %s device: %s", stream, strerror(err));
return NO_INIT;
}
err = setHardwareParams(handle);
if (err == NO_ERROR) err = setSoftwareParams(handle);
LOGI("Initialized ALSA %s device %s", stream, devName);
handle->curDev = devices;
handle->curMode = mode;
return err;
}
----------------------------------------------------------------
if (err) break;
if (devices & AudioSystem::DEVICE_OUT_WIRED_HDMI){
strcpy(mCurCard ,SPDIF);
mMixer = mMixerSpdif;
} else {
strcpy(mCurCard,SGTL5000);
mMixer = mMixerSgtl5000;
}
out = new AudioStreamOutALSA(this, &(*it));
err = out->set(format, channels, sampleRate);
break;
}
if (status) *status = err;
return out;
}
###############################################################################################
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&总结&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
函数openOutputStream的功能:
1、从mDeviceList中寻找匹配的设备。
寻找的依据是传入的参数devices。
参数devices是首先根据stream type获得strategy,然后再根据strategy取得的。
mDeviceList的内容是从_defaults[]数组中copy过来的。
若要使自己的声卡工作,需要在_defaults[]中加入自己声卡的信息,或者修改函数s_init的实现方式。
2、调用函数s_open,得到一个alsa_handle_t指针。
3、根据得到的alsa_handle_t指针创建一个AudioStreamOutALSA对象。
4、调用AudioStreamOutALSA对象的set函数设置stream的格式,声道和采样率。
5、至此,就完成了output stream的创建。
6、调用AudioStreamOutALSA对象的write函数即可实现播放。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
以后打算稍作改动,把对被调函数的展开放在原代码的后面,这样看起来应该方便些。
闲言少叙,跳入代码。
前两天看AudioTrack创建的时候,我们看到了AudioHardwareALSA::openOutputStream,并没有继续往下看。
今天就看看函数AudioHardwareALSA::openOutputStream的实现。
*****************************************源码*************************************************
AudioStreamOut *
AudioHardwareALSA::openOutputStream(uint32_t devices,
int *format,
uint32_t *channels,
uint32_t *sampleRate,
status_t *status)
{
AutoMutex lock(mLock);
LOGD("openOutputStream called for devices: 0x%08x", devices);
status_t err = BAD_VALUE;
AudioStreamOutALSA *out = 0;
if (devices & (devices - 1)) {
if (status) *status = err;
LOGD("openOutputStream called with bad devices");
return out;
}
// Find the appropriate alsa device
for(ALSAHandleList::iterator it = mDeviceList.begin();
it != mDeviceList.end(); ++it)
if (it->devices & devices) {
err = mALSADevice->open(&(*it), devices, mode());
if (err) break;
if (devices & AudioSystem::DEVICE_OUT_WIRED_HDMI){
strcpy(mCurCard ,SPDIF);
mMixer = mMixerSpdif;
} else {
strcpy(mCurCard,SGTL5000);
mMixer = mMixerSgtl5000;
}
out = new AudioStreamOutALSA(this, &(*it));
err = out->set(format, channels, sampleRate);
break;
}
if (status) *status = err;
return out;
}
**********************************************************************************************
源码路径:
hardware\alsa_sound\AudioHardwareALSA.cpp
###########################################说明################################################
AudioStreamOut *
AudioHardwareALSA::openOutputStream(uint32_t devices,
int *format,
uint32_t *channels,
uint32_t *sampleRate,
status_t *status)
{
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
先回忆一下各个参数都是嘛意思。
devices
设备编号。也就是说打开的是第几个ALSA声卡。该参数是在函数AudioPolicyManagerBase::getOutput中产生的。
至于如何调到了函数AudioHardwareALSA::openOutputStream,可以参考文章:Android Audio代码分析4 - AudioSystem::getOutputSamplingRate。
函数AudioPolicyManagerBase::getOutput中直接调用的是AudioPolicyService::openOutput函数。调用代码如下:
output = mpClientInterface->openOutput(&outputDesc->mDevice,
&outputDesc->mSamplingRate,
&outputDesc->mFormat,
&outputDesc->mChannels,
&outputDesc->mLatency,
outputDesc->mFlags);
其中,outputDesc->mDevice就是devices。
outputDesc->mDevice是怎么来的呢?
继续往前看,outputDesc->mDevice = device;
device的出处在哪?
uint32_t device = getDeviceForStrategy(strategy);
strategy的来头是:
routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);
看看这两个函数吧。
先看看getStrategy。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AudioPolicyManagerBase::routing_strategy AudioPolicyManagerBase::getStrategy(
AudioSystem::stream_type stream) {
// stream to strategy mapping
switch (stream) {
case AudioSystem::VOICE_CALL:
case AudioSystem::BLUETOOTH_SCO:
return STRATEGY_PHONE;
case AudioSystem::RING:
case AudioSystem::NOTIFICATION:
case AudioSystem::ALARM:
case AudioSystem::ENFORCED_AUDIBLE:
return STRATEGY_SONIFICATION;
case AudioSystem::DTMF:
return STRATEGY_DTMF;
default:
LOGE("unknown stream type");
case AudioSystem::SYSTEM:
// NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs
// while key clicks are played produces a poor result
case AudioSystem::TTS:
case AudioSystem::MUSIC:
return STRATEGY_MEDIA;
}
}
实现比较简单,根据stream类型,返回特定的策略。
----------------------------------------------------------------
再看看函数getDeviceForStrategy。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
uint32_t AudioPolicyManagerBase::getDeviceForStrategy(routing_strategy strategy, bool fromCache)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
看看其英文注释:
// return appropriate device for streams handled by the specified strategy according to current
// phone state, connected devices...
// if fromCache is true, the device is returned from mDeviceForStrategy[], otherwise it is determined
// by current state (device connected, phone state, force use, a2dp output...)
// This allows to:
// 1 speed up process when the state is stable (when starting or stopping an output)
// 2 access to either current device selection (fromCache == true) or
// "future" device selection (fromCache == false) when called from a context
// where conditions are changing (setDeviceConnectionState(), setPhoneState()...) AND
// before updateDeviceForStrategy() is called.
virtual uint32_t getDeviceForStrategy(routing_strategy strategy, bool fromCache = true);
顺便说一句,头文件AudioPolicyManagerBase.h不在frameworks中,而是在hardware中。
----------------------------------------------------------------
{
uint32_t device = 0;
// 函数注释中也有说,如果fromCache为true,直接返回内存中保存的信息。
// 我们调用的时候,只给了一个参数,所以fromCache肯定为true。
if (fromCache) {
LOGV("getDeviceForStrategy() from cache strategy %d, device %x", strategy, mDeviceForStrategy[strategy]);
return mDeviceForStrategy[strategy];
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
mDeviceForStrategy又是什么时候被赋值的呢?
在函数updateDeviceForStrategy中。。。
void AudioPolicyManagerBase::updateDeviceForStrategy()
{
for (int i = 0; i < NUM_STRATEGIES; i++) {
mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false);
}
}
还是调用的getDeviceForStrategy函数,只不过不是从cache中取,而是根据当前状态来取。
函数updateDeviceForStrategy在以下几个地方被调用:
AudioPolicyManagerBase::setDeviceConnectionState函数。
AudioPolicyManagerBase::setPhoneState函数。
AudioPolicyManagerBase::setForceUse函数。
AudioPolicyManagerBase的构造函数。
也就是在可能发生改变的地方。
----------------------------------------------------------------
}
switch (strategy) {
case STRATEGY_DTMF:
if (!isInCall()) {
// 如果是in call,采用的是STRATEGY_PHONE的策略。如果是off call,采用的是STRATEGY_MEDIA的策略。
// 如何判断in call / off call的?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bool AudioPolicyManagerBase::isInCall()
{
return isStateInCall(mPhoneState);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bool AudioPolicyManagerBase::isStateInCall(int state) {
return ((state == AudioSystem::MODE_IN_CALL) ||
(state == AudioSystem::MODE_IN_COMMUNICATION));
}
----------------------------------------------------------------
// 看看mPhoneState是在哪儿赋值的:
构造函数中有个对其的初始化: mPhoneState(AudioSystem::MODE_NORMAL)
setPhoneState函数中有对其赋值。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
void AudioPolicyManagerBase::setPhoneState(int state)
{
LOGV("setPhoneState() state %d", state);
uint32_t newDevice = 0;
if (state < 0 || state >= AudioSystem::NUM_MODES) {
LOGW("setPhoneState() invalid state %d", state);
return;
}
if (state == mPhoneState ) {
LOGW("setPhoneState() setting same state %d", state);
return;
}
// if leaving call state, handle special case of active streams
// pertaining to sonification strategy see handleIncallSonification()
if (isInCall()) {
LOGV("setPhoneState() in call state management: new state is %d", state);
for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
handleIncallSonification(stream, false, true);
}
}
// store previous phone state for management of sonification strategy below
int oldState = mPhoneState;
mPhoneState = state;
bool force = false;
// are we entering or starting a call
if (!isStateInCall(oldState) && isStateInCall(state)) {
LOGV(" Entering call in setPhoneState()");
// force routing command to audio hardware when starting a call
// even if no device change is needed
force = true;
} else if (isStateInCall(oldState) && !isStateInCall(state)) {
LOGV(" Exiting call in setPhoneState()");
// force routing command to audio hardware when exiting a call
// even if no device change is needed
force = true;
} else if (isStateInCall(state) && (state != oldState)) {
LOGV(" Switching between telephony and VoIP in setPhoneState()");
// force routing command to audio hardware when switching between telephony and VoIP
// even if no device change is needed
force = true;
}
// check for device and output changes triggered by new phone state
newDevice = getNewDevice(mHardwareOutput, false);
#ifdef WITH_A2DP
checkOutputForAllStrategies();
checkA2dpSuspend();
#endif
updateDeviceForStrategy();
AudioOutputDescriptor *hwOutputDesc = mOutputs.valueFor(mHardwareOutput);
// force routing command to audio hardware when ending call
// even if no device change is needed
if (isStateInCall(oldState) && newDevice == 0) {
newDevice = hwOutputDesc->device();
}
// when changing from ring tone to in call mode, mute the ringing tone
// immediately and delay the route change to avoid sending the ring tone
// tail into the earpiece or headset.
int delayMs = 0;
if (isStateInCall(state) && oldState == AudioSystem::MODE_RINGTONE) {
// delay the device change command by twice the output latency to have some margin
// and be sure that audio buffers not yet affected by the mute are out when
// we actually apply the route change
delayMs = hwOutputDesc->mLatency*2;
setStreamMute(AudioSystem::RING, true, mHardwareOutput);
}
// change routing is necessary
setOutputDevice(mHardwareOutput, newDevice, force, delayMs);
// if entering in call state, handle special case of active streams
// pertaining to sonification strategy see handleIncallSonification()
if (isStateInCall(state)) {
LOGV("setPhoneState() in call state management: new state is %d", state);
// unmute the ringing tone after a sufficient delay if it was muted before
// setting output device above
if (oldState == AudioSystem::MODE_RINGTONE) {
setStreamMute(AudioSystem::RING, false, mHardwareOutput, MUTE_TIME_MS);
}
for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
handleIncallSonification(stream, true, true);
}
}
// Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE
if (state == AudioSystem::MODE_RINGTONE &&
(hwOutputDesc->mRefCount[AudioSystem::MUSIC] ||
(systemTime() - mMusicStopTime) < seconds(SONIFICATION_HEADSET_MUSIC_DELAY))) {
mLimitRingtoneVolume = true;
} else {
mLimitRingtoneVolume = false;
}
}
----------------------------------------------------------------
函数AudioPolicyService::setPhoneState调用了函数AudioPolicyManagerBase::setPhoneState:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
status_t AudioPolicyService::setPhoneState(int state)
{
if (mpPolicyManager == NULL) {
return NO_INIT;
}
if (!checkPermission()) {
return PERMISSION_DENIED;
}
if (state < 0 || state >= AudioSystem::NUM_MODES) {
return BAD_VALUE;
}
LOGV("setPhoneState() tid %d", gettid());
// TODO: check if it is more appropriate to do it in platform specific policy manager
AudioSystem::setMode(state);
Mutex::Autolock _l(mLock);
mpPolicyManager->setPhoneState(state);
return NO_ERROR;
}
----------------------------------------------------------------
函数AudioSystem::setPhoneState调用了函数AudioPolicyService::setPhoneState:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
status_t AudioSystem::setPhoneState(int state)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
return aps->setPhoneState(state);
}
----------------------------------------------------------------
函数android_media_AudioSystem_setPhoneState调用了函数AudioSystem::setPhoneState。
从名称可以看出,函数android_media_AudioSystem_setPhoneState应该是通过JNI提供给Java层的函数。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int
android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state)
{
return check_AudioSystem_Command(AudioSystem::setPhoneState(state));
}
java侧的函数handleMessage中遇到MSG_MEDIA_SERVER_STARTED消息,以及函数binderDied中有对该函数的调用。
----------------------------------------------------------------
}
----------------------------------------------------------------
// when off call, DTMF strategy follows the same rules as MEDIA strategy
device = getDeviceForStrategy(STRATEGY_MEDIA, false);
break;
}
// when in call, DTMF and PHONE strategies follow the same rules
// FALL THROUGH
case STRATEGY_PHONE:
// for phone strategy, we first consider the forced use and then the available devices by order
// of priority
switch (mForceUse[AudioSystem::FOR_COMMUNICATION]) {
case AudioSystem::FORCE_BT_SCO:
if (!isInCall() || strategy != STRATEGY_DTMF) {
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
if (device) break;
}
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
if (device) break;
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO;
if (device) break;
// if SCO device is requested but no SCO device is available, fall back to default case
// FALL THROUGH
default: // FORCE_NONE
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
if (device) break;
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
if (device) break;
#ifdef WITH_A2DP
// when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
if (!isInCall()) {
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
if (device) break;
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
if (device) break;
}
#endif
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_EARPIECE;
if (device == 0) {
LOGE("getDeviceForStrategy() earpiece device not found");
}
break;
case AudioSystem::FORCE_SPEAKER:
if (!isInCall() || strategy != STRATEGY_DTMF) {
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
if (device) break;
}
#ifdef WITH_A2DP
// when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
// A2DP speaker when forcing to speaker output
if (!isInCall()) {
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
if (device) break;
}
#endif
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
if (device == 0) {
LOGE("getDeviceForStrategy() speaker device not found");
}
break;
}
break;
case STRATEGY_SONIFICATION:
// If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by
// handleIncallSonification().
if (isInCall()) {
device = getDeviceForStrategy(STRATEGY_PHONE, false);
break;
}
device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
if (device == 0) {
LOGE("getDeviceForStrategy() speaker device not found");
}
// The second device used for sonification is the same as the device used by media strategy
// FALL THROUGH
case STRATEGY_MEDIA: {
// 先看看MEDIA策略相关的
uint32_t device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_AUX_DIGITAL;
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
mAvailableOutputDevices又是怎么来地?
函数AudioPolicyManagerBase::setDeviceConnectionState中有对其赋值。
也就是说设备连接状态改变时,会对其赋值。
AudioPolicyManagerBase的构造函数中也有对其赋值。
默认情况下,有三个设备可用。
两个输出,一个输入。
// devices available by default are speaker, ear piece and microphone
mAvailableOutputDevices = AudioSystem::DEVICE_OUT_EARPIECE |
AudioSystem::DEVICE_OUT_SPEAKER;
mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;
audio device定义如下:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
enum audio_devices {
// output devices
DEVICE_OUT_EARPIECE = 0x1,
DEVICE_OUT_SPEAKER = 0x2,
DEVICE_OUT_WIRED_HEADSET = 0x4,
DEVICE_OUT_WIRED_HEADPHONE = 0x8,
DEVICE_OUT_BLUETOOTH_SCO = 0x10,
DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20,
DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40,
DEVICE_OUT_BLUETOOTH_A2DP = 0x80,
DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,
DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200,
DEVICE_OUT_AUX_DIGITAL = 0x400,
DEVICE_OUT_FM_HEADPHONE = 0x800,
DEVICE_OUT_FM_SPEAKER = 0x1000,
DEVICE_OUT_TTY = 0x2000,
DEVICE_OUT_WIRED_HDMI = 0x4000,
DEVICE_OUT_DEFAULT = 0x8000,
DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE | DEVICE_OUT_SPEAKER | DEVICE_OUT_WIRED_HEADSET |
DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
DEVICE_OUT_BLUETOOTH_SCO_CARKIT | DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL | DEVICE_OUT_FM_HEADPHONE | DEVICE_OUT_FM_SPEAKER | DEVICE_OUT_TTY | DEVICE_OUT_WIRED_HDMI | DEVICE_OUT_DEFAULT),
DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
// input devices
DEVICE_IN_COMMUNICATION = 0x10000,
DEVICE_IN_AMBIENT = 0x20000,
DEVICE_IN_BUILTIN_MIC = 0x40000,
DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x80000,
DEVICE_IN_WIRED_HEADSET = 0x100000,
DEVICE_IN_AUX_DIGITAL = 0x200000,
DEVICE_IN_VOICE_CALL = 0x400000,
DEVICE_IN_BACK_MIC = 0x800000,
DEVICE_IN_DEFAULT = 0x80000000,
DEVICE_IN_ALL = (DEVICE_IN_COMMUNICATION | DEVICE_IN_AMBIENT | DEVICE_IN_BUILTIN_MIC |
DEVICE_IN_BLUETOOTH_SCO_HEADSET | DEVICE_IN_WIRED_HEADSET | DEVICE_IN_AUX_DIGITAL |
DEVICE_IN_VOICE_CALL | DEVICE_IN_BACK_MIC | DEVICE_IN_DEFAULT)
};
----------------------------------------------------------------
----------------------------------------------------------------
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HDMI;
}
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADPHONE;
}
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
}
#ifdef WITH_A2DP
if (mA2dpOutput != 0) {
if (strategy == STRATEGY_SONIFICATION && !a2dpUsedForSonification()) {
break;
}
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
}
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
}
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
}
}
#endif
if (device2 == 0) {
device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_SPEAKER;
}
// device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION, 0 otherwise
device |= device2;
if (device == 0) {
LOGE("getDeviceForStrategy() speaker device not found");
}
} break;
default:
LOGW("getDeviceForStrategy() unknown strategy: %d", strategy);
break;
}
LOGV("getDeviceForStrategy() strategy %d, device %x", strategy, device);
return device;
}
----------------------------------------------------------------
回到函数头:
AudioStreamOut *
AudioHardwareALSA::openOutputStream(uint32_t devices,
int *format,
uint32_t *channels,
uint32_t *sampleRate,
status_t *status)
参数devices看完了。
后面的几个参数就比较简单了。
format是指音频格式,当前支持的有8PCMBIT和16PCMBIT.
channels是声道。
sampleRate是采样率。
status,用于返回错误信息。
----------------------------------------------------------------
AutoMutex lock(mLock);
LOGD("openOutputStream called for devices: 0x%08x", devices);
status_t err = BAD_VALUE;
AudioStreamOutALSA *out = 0;
// 从函数getDeviceForStrategy可知,得到只可能是一种设备。
// 而每种audio device只占一个bit。
// 此处是判断,若devices占一个以上的bit,则认为是参数错误。
if (devices & (devices - 1)) {
if (status) *status = err;
LOGD("openOutputStream called with bad devices");
return out;
}
// Find the appropriate alsa device
// 遍历mDeviceList,寻找合适的device。寻找的依据就是传进来的参数devices。
for(ALSAHandleList::iterator it = mDeviceList.begin();
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
mDeviceList是在何时被初始化的?
在类AudioHardwareALSA的构造函数中。
AudioHardwareALSA::AudioHardwareALSA() :
mMixer(0),
mMixerSpdif(0),
mMixerSgtl5000(0),
mALSADevice(0),
mAcousticDevice(0)
{
snd_lib_error_set_handler(&ALSAErrorHandler);
hw_module_t *module;
char snd_sgtl5000[32], snd_spdif[32];
char **cardname = new char*[MAXCARDSNUM];
for (int i = 0; i < MAXCARDSNUM; i++) {
cardname[i] = new char[128];
memset(cardname[i],0,128);
}
int id;
// 加载库
int err = hw_get_module(ALSA_HARDWARE_MODULE_ID,
(hw_module_t const**)&module);
// ALSA_HARDWARE_MODULE_ID的定义:#define ALSA_HARDWARE_MODULE_ID "alsa"
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
int hw_get_module(const char *id, const struct hw_module_t **module)
{
int status;
int i;
const struct hw_module_t *hmi = NULL;
char prop[PATH_MAX];
char path[PATH_MAX];
/*
* Here we rely on the fact that calling dlopen multiple times on
* the same .so will simply increment a refcount (and not load
* a new copy of the library).
* We also assume that dlopen() is thread-safe.
*/
// 从上面的注释可知,如果要加载的.so已经被加载,则只是对已加载的.so增加一个引用,而不是重新加载。
// 函数dlopen也是线程安全的。
/* Loop through the configuration variants looking for a module */
// 寻找合适的module,找不到,就用默认的。
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
if (i < HAL_VARIANT_KEYS_COUNT) {
if (property_get(variant_keys[i], prop, NULL) == 0) {
continue;
}
snprintf(path, sizeof(path), "%s/%s.%s.so",
HAL_LIBRARY_PATH1, id, prop);
// HAL_LIBRARY_PATH1的定义:#define HAL_LIBRARY_PATH1 "/system/lib/hw"
if (access(path, R_OK) == 0) break;
snprintf(path, sizeof(path), "%s/%s.%s.so",
HAL_LIBRARY_PATH2, id, prop);
// HAL_LIBRARY_PATH2的定义:#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
if (access(path, R_OK) == 0) break;
} else {
snprintf(path, sizeof(path), "%s/%s.default.so",
HAL_LIBRARY_PATH1, id);
if (access(path, R_OK) == 0) break;
}
}
status = -ENOENT;
if (i < HAL_VARIANT_KEYS_COUNT+1) {
/* load the module, if this fails, we're doomed, and we should not try
* to load a different variant. */
status = load(id, path, module);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
load函数的实现:
/**
* Load the file defined by the variant and if successful
* return the dlopen handle and the hmi.
* @return 0 = success, !0 = failure.
*/
static int load(const char *id,
const char *path,
const struct hw_module_t **pHmi)
{
int status;
void *handle;
struct hw_module_t *hmi;
/*
* load the symbols resolving undefined symbols before
* dlopen returns. Since RTLD_GLOBAL is not or'd in with
* RTLD_NOW the external symbols will not be global
*/
handle = dlopen(path, RTLD_NOW);
if (handle == NULL) {
char const *err_str = dlerror();
LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
status = -EINVAL;
goto done;
}
/* Get the address of the struct hal_module_info. */
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (struct hw_module_t *)dlsym(handle, sym);
if (hmi == NULL) {
LOGE("load: couldn't find symbol %s", sym);
status = -EINVAL;
goto done;
}
/* Check that the id matches */
if (strcmp(id, hmi->id) != 0) {
LOGE("load: id=%s != hmi->id=%s", id, hmi->id);
status = -EINVAL;
goto done;
}
hmi->dso = handle;
/* success */
status = 0;
done:
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
} else {
LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
----------------------------------------------------------------
}
return status;
}
----------------------------------------------------------------
if (err == 0) {
hw_device_t* device;
err = module->methods->open(module, ALSA_HARDWARE_NAME, &device);
// module的定义:hw_module_t *module;
// ALSA_HARDWARE_NAME的定义:#define ALSA_HARDWARE_NAME "alsa"
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构体hw_module_t的定义:
/**
* Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
* and the fields of this data structure must begin with hw_module_t
* followed by module specific information.
*/
typedef struct hw_module_t {
/** tag must be initialized to HARDWARE_MODULE_TAG */
uint32_t tag;
/** major version number for the module */
uint16_t version_major;
/** minor version number of the module */
uint16_t version_minor;
/** Identifier of module */
const char *id;
/** Name of this module */
const char *name;
/** Author/owner/implementor of the module */
const char *author;
/** Modules methods */
struct hw_module_methods_t* methods;
/** module's dso */
void* dso;
/** padding to 128 bytes, reserved for future use */
uint32_t reserved[32-7];
} hw_module_t;
----------------------------------------------------------------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
其中包含的hw_module_methods_t结构体:
typedef struct hw_module_methods_t {
/** Open a specific device */
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
} hw_module_methods_t;
----------------------------------------------------------------
看看对结构体hw_module_t赋值的一个地方:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static hw_module_methods_t s_module_methods = {
open : s_device_open
};
extern "C" const hw_module_t HAL_MODULE_INFO_SYM = {
tag : HARDWARE_MODULE_TAG,
version_major : 1,
version_minor : 0,
id : ALSA_HARDWARE_MODULE_ID,
name : "i.MX51 ALSA module",
author : "Freescale Semiconductor",
methods : &s_module_methods,
dso : 0,
reserved : {0,},
};
----------------------------------------------------------------
由此可见,我们调用的应该是函数s_device_open:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int s_device_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
alsa_device_t *dev;
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构体alsa_device_t的定义:
struct alsa_device_t {
hw_device_t common;
status_t (*init)(alsa_device_t *, ALSAHandleList &);
status_t (*open)(alsa_handle_t *, uint32_t, int);
status_t (*close)(alsa_handle_t *);
status_t (*route)(alsa_handle_t *, uint32_t, int);
};
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构体hw_device_t的定义:
/**
* Every device data structure must begin with hw_device_t
* followed by module specific public methods and attributes.
*/
typedef struct hw_device_t {
/** tag must be initialized to HARDWARE_DEVICE_TAG */
uint32_t tag;
/** version number for hw_device_t */
uint32_t version;
/** reference to the module this device belongs to */
// 此处保存了一个module的引用。
// 通过module,打开一个device,在设备中,保存其所属module的引用
struct hw_module_t* module;
/** padding reserved for future use */
uint32_t reserved[12];
/** Close this device */
int (*close)(struct hw_device_t* device);
} hw_device_t;
----------------------------------------------------------------
----------------------------------------------------------------
dev = (alsa_device_t *) malloc(sizeof(*dev));
if (!dev) return -ENOMEM;
memset(dev, 0, sizeof(*dev));
/* initialize the procs */
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (hw_module_t *) module;
dev->common.close = s_device_close;
// 这个函数,就是用来初始化mDeviceList的
dev->init = s_init;
// 这个函数马上就会被调用到
dev->open = s_open;
dev->close = s_close;
dev->route = s_route;
*device = &dev->common;
LOGD("i.MX51 ALSA module opened");
return 0;
}
----------------------------------------------------------------
if (err == 0) {
mALSADevice = (alsa_device_t *)device;
// 此处对mDeviceList进行了初始化。
// 我们已经知道,此处其实是调用的s_init函数
mALSADevice->init(mALSADevice, mDeviceList);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static status_t s_init(alsa_device_t *module, ALSAHandleList &list)
{
LOGD("Initializing devices for IMX51 ALSA module");
list.clear();
for (size_t i = 0; i < ARRAY_SIZE(_defaults); i++) {
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
_defaults的定义:
static alsa_handle_t _defaults[] = {
{
module : 0,
devices : IMX51_OUT_DEFAULT,
curDev : 0,
curMode : 0,
handle : 0,
format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
channels : 2,
sampleRate : DEFAULT_SAMPLE_RATE,
latency : 200000, // Desired Delay in usec
bufferSize : 6144, // Desired Number of samples
modPrivate : (void *)&setDefaultControls,
},
{
module : 0,
devices : IMX51_IN_DEFAULT,
curDev : 0,
curMode : 0,
handle : 0,
format : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
channels : 2,
sampleRate : DEFAULT_SAMPLE_RATE,
latency : 250000, // Desired Delay in usec
bufferSize : 6144, // Desired Number of samples
modPrivate : (void *)&setDefaultControls,
},
};
----------------------------------------------------------------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构体alsa_handle_t的定义:
struct alsa_handle_t {
alsa_device_t * module;
uint32_t devices;
uint32_t curDev;
int curMode;
snd_pcm_t * handle;
snd_pcm_format_t format;
uint32_t channels;
uint32_t sampleRate;
unsigned int latency; // Delay in usec
unsigned int bufferSize; // Size of sample buffer
void * modPrivate;
int mmap; // mmap flags
};
----------------------------------------------------------------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构体snd_pcm_t的定义:
其实就是结构体_snd_pcm:
struct _snd_pcm {
437 snd_card_t *card;
438 unsigned int device; /* device number */
439 unsigned int info_flags;
440 unsigned short dev_class;
441 unsigned short dev_subclass;
442 char id[64];
443 char name[80];
444 snd_pcm_str_t streams[2];
445 struct semaphore open_mutex;
446 wait_queue_head_t open_wait;
447 void *private_data;
448 void (*private_free) (snd_pcm_t *pcm);
449 #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
450 snd_pcm_oss_t oss;
451 #endif
452 };
----------------------------------------------------------------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构体snd_card_t的定义:
----------------------------------------------------------------
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
结构体snd_pcm_format_t的定义:
这个东东不是结构体,是个枚举,表示pcm 采样格式。
----------------------------------------------------------------
// 将传入的module引用保存到_defaults中
_defaults[i].module = module;
// 将_defaults中的成员push到mDeviceList中
list.push_back(_defaults[i]);
}
return NO_ERROR;
}
----------------------------------------------------------------
} else
LOGE("ALSA Module could not be opened!!!");
} else
LOGE("ALSA Module not found!!!");
/* found out sound cards in the system and new mixer controller for them*/
// cardname是一个指针数组,每一个成员指向一个char[128]数组。
err = findSoundCards(cardname);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
函数findSoundCards的实现:
static int findSoundCards(char **cardname)
{
int idx, dev, err;
snd_ctl_t *handle;
snd_hctl_t *hctlhandle;
snd_ctl_card_info_t *cardinfo;
snd_pcm_info_t *pcminfo;
char str[32];
snd_ctl_card_info_alloca(&cardinfo);
snd_pcm_info_alloca(&pcminfo);
snd_hctl_elem_t *elem;
snd_ctl_elem_id_t *id;
snd_ctl_elem_info_t *info;
snd_ctl_elem_id_alloca(&id);
snd_ctl_elem_info_alloca(&info);
idx = -1;
while (1) {
if ((err = snd_card_next(&idx)) < 0) {
LOGE("Card next error: %s\n", snd_strerror(err));
break;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
路径:external\alsa-lib\src\control\Cards.c
/**
* \brief Try to determine the next card.
* \param rcard pointer to card number
* \result zero if success, otherwise a negative error code
*
* Tries to determine the next card from given card number.
* If card number is -1, then the first available card is
* returned. If the result card number is -1, no more cards
* are available.
*/
int snd_card_next(int *rcard)
{
int card;
if (rcard == NULL)
return -EINVAL;
card = *rcard;
card = card < 0 ? 0 : card + 1;
for (; card < 32; card++) {
if (snd_card_load(card)) {
*rcard = card;
return 0;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Try to load the driver for a card.
* \param card Card number.
* \return 1 if driver is present, zero if driver is not present
*/
int snd_card_load(int card)
{
return !!(snd_card_load1(card) >= 0);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int snd_card_load1(int card)
{
int res;
char control[sizeof(SND_FILE_CONTROL) + 10];
// SND_FILE_CONTROL的定义:#define SND_FILE_CONTROL ALSA_DEVICE_DIRECTORY "controlC%i"
sprintf(control, SND_FILE_CONTROL, card);
res = snd_card_load2(control);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int snd_card_load2(const char *control)
{
int open_dev;
snd_ctl_card_info_t info;
open_dev = snd_open_device(control, O_RDONLY);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
路径:external\alsa-lib\include\Local.h
static inline int snd_open_device(const char *filename, int fmode)
{
int fd;
#ifdef O_CLOEXEC
fmode |= O_CLOEXEC;
#endif
fd = open(filename, fmode);
/* open with resmgr */
#ifdef SUPPORT_RESMGR
if (fd < 0) {
if (errno == EAGAIN || errno == EBUSY)
return fd;
if (! access(filename, F_OK))
fd = rsm_open_device(filename, fmode);
}
#endif
if (fd >= 0)
fcntl(fd, F_SETFD, FD_CLOEXEC);
return fd;
}
----------------------------------------------------------------
if (open_dev >= 0) {
// 调用的应该是kernel中的snd_ctl_ioctl函数
if (ioctl(open_dev, SNDRV_CTL_IOCTL_CARD_INFO, &info) < 0) {
int err = -errno;
close(open_dev);
return err;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct snd_ctl_file *ctl;
struct snd_card *card;
struct snd_kctl_ioctl *p;
void __user *argp = (void __user *)arg;
int __user *ip = argp;
int err;
ctl = file->private_data;
card = ctl->card;
if (snd_BUG_ON(!card))
return -ENXIO;
switch (cmd) {
case SNDRV_CTL_IOCTL_PVERSION:
return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
case SNDRV_CTL_IOCTL_CARD_INFO:
return snd_ctl_card_info(card, ctl, cmd, argp);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
unsigned int cmd, void __user *arg)
{
struct snd_ctl_card_info *info;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (! info)
return -ENOMEM;
down_read(&snd_ioctl_rwsem);
info->card = card->number;
strlcpy(info->id, card->id, sizeof(info->id));
strlcpy(info->driver, card->driver, sizeof(info->driver));
strlcpy(info->name, card->shortname, sizeof(info->name));
strlcpy(info->longname, card->longname, sizeof(info->longname));
strlcpy(info->mixername, card->mixername, sizeof(info->mixername));
strlcpy(info->components, card->components, sizeof(info->components));
up_read(&snd_ioctl_rwsem);
if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {
kfree(info);
return -EFAULT;
}
kfree(info);
return 0;
}
----------------------------------------------------------------
case SNDRV_CTL_IOCTL_ELEM_LIST:
return snd_ctl_elem_list(card, argp);
case SNDRV_CTL_IOCTL_ELEM_INFO:
return snd_ctl_elem_info_user(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_READ:
return snd_ctl_elem_read_user(card, argp);
case SNDRV_CTL_IOCTL_ELEM_WRITE:
return snd_ctl_elem_write_user(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_LOCK:
return snd_ctl_elem_lock(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
return snd_ctl_elem_unlock(ctl, argp);
case SNDRV_CTL_IOCTL_ELEM_ADD:
return snd_ctl_elem_add_user(ctl, argp, 0);
case SNDRV_CTL_IOCTL_ELEM_REPLACE:
return snd_ctl_elem_add_user(ctl, argp, 1);
case SNDRV_CTL_IOCTL_ELEM_REMOVE:
return snd_ctl_elem_remove(ctl, argp);
case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
return snd_ctl_subscribe_events(ctl, ip);
case SNDRV_CTL_IOCTL_TLV_READ:
return snd_ctl_tlv_ioctl(ctl, argp, 0);
case SNDRV_CTL_IOCTL_TLV_WRITE:
return snd_ctl_tlv_ioctl(ctl, argp, 1);
case SNDRV_CTL_IOCTL_TLV_COMMAND:
return snd_ctl_tlv_ioctl(ctl, argp, -1);
case SNDRV_CTL_IOCTL_POWER:
return -ENOPROTOOPT;
case SNDRV_CTL_IOCTL_POWER_STATE:
#ifdef CONFIG_PM
return put_user(card->power_state, ip) ? -EFAULT : 0;
#else
return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
#endif
}
down_read(&snd_ioctl_rwsem);
list_for_each_entry(p, &snd_control_ioctls, list) {
err = p->fioctl(card, ctl, cmd, arg);
if (err != -ENOIOCTLCMD) {
up_read(&snd_ioctl_rwsem);
return err;
}
}
up_read(&snd_ioctl_rwsem);
snd_printdd("unknown ioctl = 0x%x\n", cmd);
return -ENOTTY;
}
----------------------------------------------------------------
close(open_dev);
return info.card;
} else {
return -errno;
}
}
----------------------------------------------------------------
#ifdef SUPPORT_ALOAD
if (res < 0) {
// SND_FILE_LOAD的定义:#define SND_FILE_LOAD ALOAD_DEVICE_DIRECTORY "aloadC%i"
char aload[sizeof(SND_FILE_LOAD) + 10];
sprintf(aload, SND_FILE_LOAD, card);
res = snd_card_load2(aload);
}
#endif
return res;
}
----------------------------------------------------------------
}
----------------------------------------------------------------
}
*rcard = -1;
return 0;
}
----------------------------------------------------------------
if (idx < 0)
break;
sprintf(str, "hw:CARD=%i", idx);
if ((err = snd_ctl_open(&handle, str, 0)) < 0) {
LOGE("Open error: %s\n", snd_strerror(err));
continue;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Opens a CTL
* \param ctlp Returned CTL handle
* \param name ASCII identifier of the CTL handle
* \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC)
* \return 0 on success otherwise a negative error code
*/
int snd_ctl_open(snd_ctl_t **ctlp, const char *name, int mode)
{
int err;
assert(ctlp && name);
err = snd_config_update();
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Updates #snd_config by rereading the global configuration files (if needed).
* \return 0 if #snd_config was up to date, 1 if #snd_config was
* updated, otherwise a negative error code.
*
* \warning Whenever #snd_config is updated, all string pointers and
* configuration node handles previously obtained from it may become
* invalid.
*
* \par Errors:
* Any errors encountered when parsing the input or returned by hooks or
* functions.
*
* \par Conforming to:
* LSB 3.2
*/
int snd_config_update(void)
{
int err;
#ifdef HAVE_LIBPTHREAD
pthread_mutex_lock(&snd_config_update_mutex);
#endif
err = snd_config_update_r(&snd_config, &snd_config_global_update, NULL);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Updates a configuration tree by rereading the configuration files (if needed).
* \param[in,out] _top Address of the handle to the top-level node.
* \param[in,out] _update Address of a pointer to private update information.
* \param[in] cfgs A list of configuration file names, delimited with ':'.
* If \p cfgs is \c NULL, the default global
* configuration file is used.
* \return 0 if \a _top was up to date, 1 if the configuration files
* have been reread, otherwise a negative error code.
*
* The variables pointed to by \a _top and \a _update can be initialized
* to \c NULL before the first call to this function. The private
* update information holds information about all used configuration
* files that allows this function to detects changes to them; this data
* can be freed with #snd_config_update_free.
*
* The global configuration files are specified in the environment variable
* \c ALSA_CONFIG_PATH.
*
* \warning If the configuration tree is reread, all string pointers and
* configuration node handles previously obtained from this tree become
* invalid.
*
* \par Errors:
* Any errors encountered when parsing the input or returned by hooks or
* functions.
*/
int snd_config_update_r(snd_config_t **_top, snd_config_update_t **_update, const char *cfgs)
{
int err;
const char *configs, *c;
unsigned int k;
size_t l;
snd_config_update_t *local;
snd_config_update_t *update;
snd_config_t *top;
assert(_top && _update);
top = *_top;
update = *_update;
configs = cfgs;
if (!configs) {
configs = getenv(ALSA_CONFIG_PATH_VAR);
if (!configs || !*configs)
configs = ALSA_CONFIG_PATH_DEFAULT;
}
for (k = 0, c = configs; (l = strcspn(c, ": ")) > 0; ) {
c += l;
k++;
if (!*c)
break;
c++;
}
if (k == 0) {
local = NULL;
goto _reread;
}
local = (snd_config_update_t *)calloc(1, sizeof(snd_config_update_t));
if (!local)
return -ENOMEM;
local->count = k;
local->finfo = calloc(local->count, sizeof(struct finfo));
if (!local->finfo) {
free(local);
return -ENOMEM;
}
for (k = 0, c = configs; (l = strcspn(c, ": ")) > 0; ) {
char name[l + 1];
memcpy(name, c, l);
name[l] = 0;
err = snd_user_file(name, &local->finfo[k].name);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#ifdef HAVE_WORDEXP_H
#include <wordexp.h>
#include <assert.h>
int snd_user_file(const char *file, char **result)
{
wordexp_t we;
int err;
assert(file && result);
err = wordexp(file, &we, WRDE_NOCMD);
switch (err) {
case WRDE_NOSPACE:
return -ENOMEM;
case 0:
if (we.we_wordc == 1)
break;
/* fall thru */
default:
wordfree(&we);
return -EINVAL;
}
*result = strdup(we.we_wordv[0]);
if (*result == NULL)
return -ENOMEM;
wordfree(&we);
return 0;
}
#else /* !HAVE_WORDEXP_H */
/* just copy the string - would be nicer to expand by ourselves, though... */
int snd_user_file(const char *file, char **result)
{
*result = strdup(file);
if (! *result)
return -ENOMEM;
return 0;
}
#endif /* HAVE_WORDEXP_H */
----------------------------------------------------------------
if (err < 0)
goto _end;
c += l;
k++;
if (!*c)
break;
c++;
}
for (k = 0; k < local->count; ++k) {
struct stat st;
struct finfo *lf = &local->finfo[k];
if (stat(lf->name, &st) >= 0) {
lf->dev = st.st_dev;
lf->ino = st.st_ino;
lf->mtime = st.st_mtime;
} else {
SNDERR("Cannot access file %s", lf->name);
free(lf->name);
memmove(&local->finfo[k], &local->finfo[k+1], sizeof(struct finfo) * (local->count - k - 1));
k--;
local->count--;
}
}
if (!update)
goto _reread;
if (local->count != update->count)
goto _reread;
for (k = 0; k < local->count; ++k) {
struct finfo *lf = &local->finfo[k];
struct finfo *uf = &update->finfo[k];
if (strcmp(lf->name, uf->name) != 0 ||
lf->dev != uf->dev ||
lf->ino != uf->ino ||
lf->mtime != uf->mtime)
goto _reread;
}
err = 0;
_end:
if (err < 0) {
if (top) {
snd_config_delete(top);
*_top = NULL;
}
if (update) {
snd_config_update_free(update);
*_update = NULL;
}
}
if (local)
snd_config_update_free(local);
return err;
_reread:
*_top = NULL;
*_update = NULL;
if (update) {
snd_config_update_free(update);
update = NULL;
}
if (top) {
snd_config_delete(top);
top = NULL;
}
err = snd_config_top(&top);
if (err < 0)
goto _end;
if (!local)
goto _skip;
for (k = 0; k < local->count; ++k) {
snd_input_t *in;
err = snd_input_stdio_open(&in, local->finfo[k].name, "r");
if (err >= 0) {
err = snd_config_load(top, in);
snd_input_close(in);
if (err < 0) {
SNDERR("%s may be old or corrupted: consider to remove or fix it", local->finfo[k].name);
goto _end;
}
} else {
SNDERR("cannot access file %s", local->finfo[k].name);
}
}
_skip:
err = snd_config_hooks(top, NULL);
if (err < 0) {
SNDERR("hooks failed, removing configuration");
goto _end;
}
*_top = top;
*_update = local;
return 1;
}
----------------------------------------------------------------
#ifdef HAVE_LIBPTHREAD
pthread_mutex_unlock(&snd_config_update_mutex);
#endif
return err;
}
----------------------------------------------------------------
if (err < 0)
return err;
return snd_ctl_open_noupdate(ctlp, snd_config, name, mode);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int snd_ctl_open_noupdate(snd_ctl_t **ctlp, snd_config_t *root, const char *name, int mode)
{
int err;
snd_config_t *ctl_conf;
err = snd_config_search_definition(root, "ctl", name, &ctl_conf);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Searches for a definition in a configuration tree, using
* aliases and expanding hooks and arguments.
* \param[in] config Handle to the configuration (sub)tree to search.
* \param[in] base Implicit key base, or \c NULL for none.
* \param[in] name Key suffix, optionally with arguments.
* \param[out] result The function puts the handle to the expanded found
* node at the address specified by \a result.
* \return A non-negative value if successful, otherwise a negative error code.
*
* This functions searches for a child node of \a config, allowing
* aliases and expanding hooks, like #snd_config_search_alias_hooks.
*
* If \a name contains a colon (:), the rest of the string after the
* colon contains arguments that are expanded as with
* #snd_config_expand.
*
* In any case, \a result is a new node that must be freed by the
* caller.
*
* \par Errors:
* <dl>
* <dt>-ENOENT<dd>An id in \a key or an alias id does not exist.
* <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is
* not a compound node.
* </dl>
* Additionally, any errors encountered when parsing the hook
* definitions or arguments, or returned by (hook) functions.
*/
int snd_config_search_definition(snd_config_t *config,
const char *base, const char *name,
snd_config_t **result)
{
snd_config_t *conf;
char *key;
const char *args = strchr(name, ':');
int err;
if (args) {
args++;
key = alloca(args - name);
memcpy(key, name, args - name - 1);
key[args - name - 1] = '\0';
} else {
key = (char *) name;
}
/*
* if key contains dot (.), the implicit base is ignored
* and the key starts from root given by the 'config' parameter
*/
err = snd_config_search_alias_hooks(config, strchr(key, '.') ? NULL : base, key, &conf);
if (err < 0)
return err;
return snd_config_expand(conf, config, args, NULL, result);
}
----------------------------------------------------------------
if (err < 0) {
SNDERR("Invalid CTL %s", name);
return err;
}
err = snd_ctl_open_conf(ctlp, name, root, ctl_conf, mode);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static int snd_ctl_open_conf(snd_ctl_t **ctlp, const char *name,
snd_config_t *ctl_root, snd_config_t *ctl_conf, int mode)
{
const char *str;
char *buf = NULL, *buf1 = NULL;
int err;
snd_config_t *conf, *type_conf = NULL;
snd_config_iterator_t i, next;
const char *lib = NULL, *open_name = NULL;
const char *id;
int (*open_func)(snd_ctl_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
#ifndef PIC
extern void *snd_control_open_symbols(void);
#endif
void *h = NULL;
if (snd_config_get_type(ctl_conf) != SND_CONFIG_TYPE_COMPOUND) {
if (name)
SNDERR("Invalid type for CTL %s definition", name);
else
SNDERR("Invalid type for CTL definition");
return -EINVAL;
}
err = snd_config_search(ctl_conf, "type", &conf);
if (err < 0) {
SNDERR("type is not defined");
return err;
}
err = snd_config_get_id(conf, &id);
if (err < 0) {
SNDERR("unable to get id");
return err;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", id);
return err;
}
err = snd_config_search_definition(ctl_root, "ctl_type", str, &type_conf);
if (err >= 0) {
if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) {
SNDERR("Invalid type for CTL type %s definition", str);
goto _err;
}
snd_config_for_each(i, next, type_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {
err = snd_config_get_string(n, &lib);
if (err < 0) {
SNDERR("Invalid type for %s", id);
goto _err;
}
continue;
}
if (strcmp(id, "open") == 0) {
err = snd_config_get_string(n, &open_name);
if (err < 0) {
SNDERR("Invalid type for %s", id);
goto _err;
}
continue;
}
SNDERR("Unknown field %s", id);
err = -EINVAL;
goto _err;
}
}
if (!open_name) {
buf = malloc(strlen(str) + 32);
if (buf == NULL) {
err = -ENOMEM;
goto _err;
}
open_name = buf;
sprintf(buf, "_snd_ctl_%s_open", str);
}
if (!lib) {
const char *const *build_in = build_in_ctls;
while (*build_in) {
if (!strcmp(*build_in, str))
break;
build_in++;
}
if (*build_in == NULL) {
buf1 = malloc(strlen(str) + sizeof(ALSA_PLUGIN_DIR) + 32);
if (buf1 == NULL) {
err = -ENOMEM;
goto _err;
}
lib = buf1;
sprintf(buf1, "%s/libasound_module_ctl_%s.so", ALSA_PLUGIN_DIR, str);
}
}
#ifndef PIC
snd_control_open_symbols();
#endif
open_func = snd_dlobj_cache_lookup(open_name);
if (open_func) {
err = 0;
goto _err;
}
h = snd_dlopen(lib, RTLD_NOW);
if (h)
open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_CONTROL_DLSYM_VERSION));
err = 0;
if (!h) {
SNDERR("Cannot open shared library %s", lib);
err = -ENOENT;
} else if (!open_func) {
SNDERR("symbol %s is not defined inside %s", open_name, lib);
snd_dlclose(h);
err = -ENXIO;
}
_err:
if (type_conf)
snd_config_delete(type_conf);
if (err >= 0) {
err = open_func(ctlp, name, ctl_root, ctl_conf, mode);
if (err >= 0) {
if (h /*&& (mode & SND_CTL_KEEP_ALIVE)*/) {
snd_dlobj_cache_add(open_name, h, open_func);
h = NULL;
}
(*ctlp)->dl_handle = h;
err = 0;
} else {
if (h)
snd_dlclose(h);
}
}
free(buf);
free(buf1);
return err;
}
----------------------------------------------------------------
snd_config_delete(ctl_conf);
return err;
}
----------------------------------------------------------------
}
----------------------------------------------------------------
// 函数snd_ctl_card_info前面已经见过
if ((err = snd_ctl_card_info(handle, cardinfo)) < 0) {
LOGE("HW info error: %s\n", snd_strerror(err));
continue;
}
LOGD("Soundcard #%i:\n", idx + 1);
LOGD(" card - %i\n", snd_ctl_card_info_get_card(cardinfo));
LOGD(" id - '%s'\n", snd_ctl_card_info_get_id(cardinfo));
LOGD(" driver - '%s'\n", snd_ctl_card_info_get_driver(cardinfo));
LOGD(" name - '%s'\n", snd_ctl_card_info_get_name(cardinfo));
LOGD(" longname - '%s'\n", snd_ctl_card_info_get_longname(cardinfo));
LOGD(" mixername - '%s'\n", snd_ctl_card_info_get_mixername(cardinfo));
LOGD(" components - '%s'\n", snd_ctl_card_info_get_components(cardinfo));
strcpy(cardname[idx], snd_ctl_card_info_get_name(cardinfo));
LOGD("\n\n-----get cart name and id: %s : %d",cardname[idx],idx);
snd_ctl_close(handle);
if ((err = snd_hctl_open(&hctlhandle, str, 0)) < 0) {
LOGE("Control %s open error: %s", str, snd_strerror(err));
return err;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Opens an HCTL
* \param hctlp Returned HCTL handle
* \param name ASCII identifier of the underlying CTL handle
* \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC)
* \return 0 on success otherwise a negative error code
*/
int snd_hctl_open(snd_hctl_t **hctlp, const char *name, int mode)
{
snd_ctl_t *ctl;
int err;
if ((err = snd_ctl_open(&ctl, name, mode)) < 0)
return err;
err = snd_hctl_open_ctl(hctlp, ctl);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Opens an HCTL
* \param hctlp Returned HCTL handle
* \param ctl underlying CTL handle
* \return 0 on success otherwise a negative error code
*/
int snd_hctl_open_ctl(snd_hctl_t **hctlp, snd_ctl_t *ctl)
{
snd_hctl_t *hctl;
assert(hctlp);
*hctlp = NULL;
if ((hctl = (snd_hctl_t *)calloc(1, sizeof(snd_hctl_t))) == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&hctl->elems);
hctl->ctl = ctl;
*hctlp = hctl;
return 0;
}
----------------------------------------------------------------
if (err < 0)
snd_ctl_close(ctl);
return err;
}
----------------------------------------------------------------
if ((err = snd_hctl_load(hctlhandle)) < 0) {
LOGE("Control %s local error: %s\n", str, snd_strerror(err));
return err;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Load an HCTL with all elements and sort them
* \param hctl HCTL handle
* \return 0 on success otherwise a negative error code
*/
int snd_hctl_load(snd_hctl_t *hctl)
{
snd_ctl_elem_list_t list;
int err = 0;
unsigned int idx;
assert(hctl);
assert(hctl->ctl);
assert(hctl->count == 0);
assert(list_empty(&hctl->elems));
memset(&list, 0, sizeof(list));
if ((err = snd_ctl_elem_list(hctl->ctl, &list)) < 0)
goto _end;
while (list.count != list.used) {
err = snd_ctl_elem_list_alloc_space(&list, list.count);
if (err < 0)
goto _end;
if ((err = snd_ctl_elem_list(hctl->ctl, &list)) < 0)
goto _end;
}
if (hctl->alloc < list.count) {
hctl->alloc = list.count;
free(hctl->pelems);
hctl->pelems = malloc(hctl->alloc * sizeof(*hctl->pelems));
if (!hctl->pelems) {
err = -ENOMEM;
goto _end;
}
}
for (idx = 0; idx < list.count; idx++) {
snd_hctl_elem_t *elem;
elem = calloc(1, sizeof(snd_hctl_elem_t));
if (elem == NULL) {
snd_hctl_free(hctl);
err = -ENOMEM;
goto _end;
}
elem->id = list.pids[idx];
elem->hctl = hctl;
elem->compare_weight = get_compare_weight(&elem->id);
hctl->pelems[idx] = elem;
list_add_tail(&elem->list, &hctl->elems);
hctl->count++;
}
if (!hctl->compare)
hctl->compare = snd_hctl_compare_default;
snd_hctl_sort(hctl);
for (idx = 0; idx < hctl->count; idx++) {
int res = snd_hctl_throw_event(hctl, SNDRV_CTL_EVENT_MASK_ADD,
hctl->pelems[idx]);
if (res < 0)
return res;
}
err = snd_ctl_subscribe_events(hctl->ctl, 1);
_end:
free(list.pids);
return err;
}
----------------------------------------------------------------
for (elem = snd_hctl_first_elem(hctlhandle); elem; elem = snd_hctl_elem_next(elem)) {
if ((err = snd_hctl_elem_info(elem, info)) < 0) {
LOGE("Control %s snd_hctl_elem_info error: %s\n", str, snd_strerror(err));
return err;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Get information for an HCTL element
* \param elem HCTL element
* \param info HCTL element information
* \return 0 otherwise a negative error code on failure
*/
int snd_hctl_elem_info(snd_hctl_elem_t *elem, snd_ctl_elem_info_t *info)
{
assert(elem);
assert(elem->hctl);
assert(info);
info->id = elem->id;
return snd_ctl_elem_info(elem->hctl->ctl, info);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Get CTL element information
* \param ctl CTL handle
* \param info CTL element id/information pointer
* \return 0 on success otherwise a negative error code
*/
int snd_ctl_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info)
{
assert(ctl && info && (info->id.name[0] || info->id.numid));
return ctl->ops->element_info(ctl, info);
}
----------------------------------------------------------------
}
----------------------------------------------------------------
snd_hctl_elem_get_id(elem, id);
show_control_id(id);
}
snd_hctl_close(hctlhandle);
}
snd_config_update_free_global();
return 0;
}
----------------------------------------------------------------
// 根据cardname创建一个mixer
if (err == 0) {
for (id = 0; id < MAXCARDSNUM; id++) {
if(cardname[id] && strstr(cardname[id],SPDIF)){
LOGD(" CARD NAME: %s ID %d", cardname[id],id);
sprintf(snd_spdif,"hw:0%d",id);
sprintf(snd_spdif,"hw:CARD=%d",id);
mMixerSpdif = new ALSAMixer(snd_spdif);
}else if (cardname[id] && strstr(cardname[id],SGTL5000)){
LOGD(" CARD NAME: %s ID %d", cardname[id],id);
sprintf(snd_sgtl5000,"hw:0%d",id);
sprintf(snd_sgtl5000,"hw:CARD=%d",id);
mMixerSgtl5000 = new ALSAMixer(snd_sgtl5000);
}
}
} else {
LOGE("Don't find any Sound cards, use default");
mMixerSgtl5000 = new ALSAMixer("hw:00");
mMixerSpdif = new ALSAMixer("hw:00");
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ALSAMixer的构造函数:
ALSAMixer::ALSAMixer(const char *sndcard)
{
int err;
LOGI(" Init ALSAMIXER for SNDCARD : %s",sndcard);
mixerMasterProp =
ALSA_PROP(AudioSystem::DEVICE_OUT_ALL, "master", "PCM", "Capture");
mixerProp = {
ALSA_PROP(AudioSystem::DEVICE_OUT_EARPIECE, "earpiece", "Earpiece", "Capture"),
ALSA_PROP(AudioSystem::DEVICE_OUT_SPEAKER, "speaker", "Speaker", ""),
ALSA_PROP(AudioSystem::DEVICE_OUT_WIRED_HEADSET, "headset", "Headphone", "Capture"),
ALSA_PROP(AudioSystem::DEVICE_OUT_BLUETOOTH_SCO, "bluetooth.sco", "Bluetooth", "Bluetooth Capture"),
ALSA_PROP(AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP, "bluetooth.a2dp", "Bluetooth A2DP", "Bluetooth A2DP Capture"),
ALSA_PROP(static_cast<AudioSystem::audio_devices>(0), "", NULL, NULL)
};
initMixer (&mMixer[SND_PCM_STREAM_PLAYBACK], sndcard);
initMixer (&mMixer[SND_PCM_STREAM_CAPTURE], sndcard);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
函数initMixer的实现:
static int initMixer (snd_mixer_t **mixer, const char *name)
{
int err;
if ((err = snd_mixer_open(mixer, 0)) < 0) {
LOGE("Unable to open mixer: %s", snd_strerror(err));
return err;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Opens an empty mixer
* \param mixerp Returned mixer handle
* \param mode Open mode
* \return 0 on success otherwise a negative error code
*/
int snd_mixer_open(snd_mixer_t **mixerp, int mode ATTRIBUTE_UNUSED)
{
snd_mixer_t *mixer;
assert(mixerp);
mixer = calloc(1, sizeof(*mixer));
if (mixer == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&mixer->slaves);
INIT_LIST_HEAD(&mixer->classes);
INIT_LIST_HEAD(&mixer->elems);
mixer->compare = snd_mixer_compare_default;
*mixerp = mixer;
return 0;
}
----------------------------------------------------------------
if ((err = snd_mixer_attach(*mixer, name)) < 0) {
LOGW("Unable to attach mixer to device %s: %s",
name, snd_strerror(err));
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Attach an HCTL specified with the CTL device name to an opened mixer
* \param mixer Mixer handle
* \param name HCTL name (see #snd_hctl_open)
* \return 0 on success otherwise a negative error code
*/
int snd_mixer_attach(snd_mixer_t *mixer, const char *name)
{
snd_hctl_t *hctl;
int err;
err = snd_hctl_open(&hctl, name, 0);
if (err < 0)
return err;
err = snd_mixer_attach_hctl(mixer, hctl);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Attach an HCTL to an opened mixer
* \param mixer Mixer handle
* \param hctl the HCTL to be attached
* \return 0 on success otherwise a negative error code
*/
int snd_mixer_attach_hctl(snd_mixer_t *mixer, snd_hctl_t *hctl)
{
snd_mixer_slave_t *slave;
int err;
assert(hctl);
slave = calloc(1, sizeof(*slave));
if (slave == NULL)
return -ENOMEM;
err = snd_hctl_nonblock(hctl, 1);
if (err < 0) {
snd_hctl_close(hctl);
free(slave);
return err;
}
snd_hctl_set_callback(hctl, hctl_event_handler);
snd_hctl_set_callback_private(hctl, mixer);
slave->hctl = hctl;
list_add_tail(&slave->list, &mixer->slaves);
return 0;
}
----------------------------------------------------------------
if (err < 0) {
snd_hctl_close(hctl);
return err;
}
return 0;
}
----------------------------------------------------------------
if ((err = snd_mixer_attach(*mixer, "hw:00")) < 0) {
LOGE("Unable to attach mixer to device default: %s",
snd_strerror(err));
snd_mixer_close (*mixer);
*mixer = NULL;
return err;
}
}
if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0) {
LOGE("Unable to register mixer elements: %s", snd_strerror(err));
snd_mixer_close (*mixer);
*mixer = NULL;
return err;
}
// Get the mixer controls from the kernel
if ((err = snd_mixer_load(*mixer)) < 0) {
LOGE("Unable to load mixer elements: %s", snd_strerror(err));
snd_mixer_close (*mixer);
*mixer = NULL;
return err;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Load a mixer elements
* \param mixer Mixer handle
* \return 0 on success otherwise a negative error code
*/
int snd_mixer_load(snd_mixer_t *mixer)
{
struct list_head *pos;
list_for_each(pos, &mixer->slaves) {
int err;
snd_mixer_slave_t *s;
s = list_entry(pos, snd_mixer_slave_t, list);
err = snd_hctl_load(s->hctl);
if (err < 0)
return err;
}
return 0;
}
----------------------------------------------------------------
return 0;
}
----------------------------------------------------------------
snd_mixer_selem_id_t *sid;
snd_mixer_selem_id_alloca(&sid);
for (int i = 0; i <= SND_PCM_STREAM_LAST; i++) {
mixer_info_t *info = mixerMasterProp[i].mInfo = new mixer_info_t;
property_get (mixerMasterProp[i].propName,
info->name,
mixerMasterProp[i].propDefault);
for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
elem;
elem = snd_mixer_elem_next(elem)) {
if (!snd_mixer_selem_is_active(elem))
continue;
snd_mixer_selem_get_id(elem, sid);
// Find PCM playback volume control element.
const char *elementName = snd_mixer_selem_id_get_name(sid);
if (info->elem == NULL &&
strcmp(elementName, info->name) == 0 &&
hasVolume[i] (elem)) {
info->elem = elem;
getVolumeRange[i] (elem, &info->min, &info->max);
info->volume = info->max;
setVol[i] (elem, info->volume);
if (i == SND_PCM_STREAM_PLAYBACK &&
snd_mixer_selem_has_playback_switch (elem))
snd_mixer_selem_set_playback_switch_all (elem, 1);
break;
}
}
LOGV("Mixer: master '%s' %s.", info->name, info->elem ? "found" : "not found");
for (int j = 0; mixerProp[j][i].device; j++) {
mixer_info_t *info = mixerProp[j][i].mInfo = new mixer_info_t;
property_get (mixerProp[j][i].propName,
info->name,
mixerProp[j][i].propDefault);
for (snd_mixer_elem_t *elem = snd_mixer_first_elem(mMixer[i]);
elem;
elem = snd_mixer_elem_next(elem)) {
if (!snd_mixer_selem_is_active(elem))
continue;
snd_mixer_selem_get_id(elem, sid);
// Find PCM playback volume control element.
const char *elementName = snd_mixer_selem_id_get_name(sid);
if (info->elem == NULL &&
strcmp(elementName, info->name) == 0 &&
hasVolume[i] (elem)) {
info->elem = elem;
getVolumeRange[i] (elem, &info->min, &info->max);
info->volume = info->max;
setVol[i] (elem, info->volume);
if (i == SND_PCM_STREAM_PLAYBACK &&
snd_mixer_selem_has_playback_switch (elem))
snd_mixer_selem_set_playback_switch_all (elem, 1);
break;
}
}
LOGV("Mixer: route '%s' %s.", info->name, info->elem ? "found" : "not found");
}
}
LOGV("mixer initialized.");
}
----------------------------------------------------------------
for (int i = 0; i < MAXCARDSNUM; i++) {
delete []cardname[i];
}
delete []cardname;
mCurCard = new char[128];
if (!mCurCard)
LOGE("allocate memeory to store current sound card name fail");
memset(mCurCard,0,sizeof(mCurCard));
/* set current card as sgtl5000 default */
if(mMixerSgtl5000)
{
strcpy(mCurCard,SGTL5000);
mMixer = mMixerSgtl5000;
}else if(mMixerSpdif)
{
strcpy(mCurCard,SPDIF);
mMixer = mMixerSpdif;
}
err = hw_get_module(ACOUSTICS_HARDWARE_MODULE_ID,
(hw_module_t const**)&module);
if (err == 0) {
hw_device_t* device;
err = module->methods->open(module, ACOUSTICS_HARDWARE_NAME, &device);
if (err == 0)
mAcousticDevice = (acoustic_device_t *)device;
else
LOGE("Acoustics Module not found.");
}
}
----------------------------------------------------------------
it != mDeviceList.end(); ++it)
if (it->devices & devices) {
// 此处调用的其实是函数s_open
// devices就是我们刚开始看参数时看的那个东东
err = mALSADevice->open(&(*it), devices, mode());
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
static status_t s_open(alsa_handle_t *handle, uint32_t devices, int mode)
{
// Close off previously opened device.
// It would be nice to determine if the underlying device actually
// changes, but we might be recovering from an error or manipulating
// mixer settings (see asound.conf).
//
s_close(handle);
LOGD("open called for devices %08x in mode %d...", devices, mode);
const char *stream = streamName(handle);
// 在这儿使用到了devices
const char *devName = deviceName(handle, devices, mode, 1);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//card_device =0, return the card name, card_device=1, return the card device name
const char *deviceName(alsa_handle_t *alsa_handle, uint32_t device, int mode, int card_device)
{
snd_ctl_t *handle;
int card, err, dev, idx;
snd_ctl_card_info_t *info;
snd_pcm_info_t *pcminfo;
snd_ctl_card_info_alloca(&info);
snd_pcm_info_alloca(&pcminfo);
int cardnum = 0;
char value[PROPERTY_VALUE_MAX];
snd_pcm_stream_t stream = direction(alsa_handle);
bool havespdifdevice = false;
bool havesgtldevice = false;
card = -1;
if (snd_card_next(&card) < 0 || card < 0) {
LOGD("no soundcards found...");
return "default";
}
LOGD("**** List of %s Hardware Devices ****\n",
snd_pcm_stream_name(stream));
while (card >= 0) {
char name[32];
sprintf(name, "hw:%d", card);
if ((err = snd_ctl_open(&handle, name, 0)) < 0) {
LOGD("control open (%i): %s", card, snd_strerror(err));
goto next_card;
}
if ((err = snd_ctl_card_info(handle, info)) < 0) {
LOGD("control hardware info (%i): %s", card, snd_strerror(err));
snd_ctl_close(handle);
goto next_card;
}
dev = -1;
while (1) {
unsigned int count;
if (snd_ctl_pcm_next_device(handle, &dev)<0)
LOGD("snd_ctl_pcm_next_device");
if (dev < 0)
break;
snd_pcm_info_set_device(pcminfo, dev);
snd_pcm_info_set_subdevice(pcminfo, 0);
snd_pcm_info_set_stream(pcminfo, stream);
if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
if (err != -ENOENT)
LOGD("control digital audio info (%i): %s", card, snd_strerror(err));
continue;
}
LOGD("card %i: %s [%s], device %i: %s [%s]\n",
card, snd_ctl_card_info_get_id(info), snd_ctl_card_info_get_name(info),
dev,
snd_pcm_info_get_id(pcminfo),
snd_pcm_info_get_name(pcminfo));
if(strcmp(snd_pcm_info_get_id(pcminfo),"IMX SPDIF mxc spdif-0")==0) {
if(card_device==0) sprintf(spdifcardname, "hw:0%d", card);
else sprintf(spdifcardname, "hw:%d,%d", card, dev);
havespdifdevice = true;
}
if(strcmp(snd_pcm_info_get_id(pcminfo),"SGTL5000 SGTL5000-0")==0) {
if(card_device==0) sprintf(sgtlcardname, "hw:0%d", card);
else sprintf(sgtlcardname, "hw:%d,%d", card, dev);
havesgtldevice = true;
}
cardnum++;
}
snd_ctl_close(handle);
next_card:
if (snd_card_next(&card) < 0) {
LOGD("snd_card_next");
break;
}
}
property_get("ro.HDMI_AUDIO_OUTPUT", value, "");
// 只是在这儿用了一下
if((device & AudioSystem::DEVICE_OUT_WIRED_HDMI) && havespdifdevice && (strcmp(value, "1") == 0))
{
return spdifcardname;
}else if(havesgtldevice)
{
return sgtlcardname;
}
return "default";
}
----------------------------------------------------------------
// The PCM stream is opened in blocking mode, per ALSA defaults. The
// AudioFlinger seems to assume blocking mode too, so asynchronous mode
// should not be used.
int err = snd_pcm_open(&handle->handle, devName, direction(handle), 0);
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Opens a PCM
* \param pcmp Returned PCM handle
* \param name ASCII identifier of the PCM handle
* \param stream Wanted stream
* \param mode Open mode (see #SND_PCM_NONBLOCK, #SND_PCM_ASYNC)
* \return 0 on success otherwise a negative error code
*/
int snd_pcm_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_stream_t stream, int mode)
{
int err;
assert(pcmp && name);
// 函数snd_config_update前面已经见过
err = snd_config_update();
if (err < 0)
return err;
// 前面已经见过函数snd_ctl_open_noupdate
return snd_pcm_open_noupdate(pcmp, snd_config, name, stream, mode, 0);
}
----------------------------------------------------------------
if (err < 0) {
LOGE("Failed to Initialize any ALSA %s device: %s", stream, strerror(err));
return NO_INIT;
}
err = setHardwareParams(handle);
if (err == NO_ERROR) err = setSoftwareParams(handle);
LOGI("Initialized ALSA %s device %s", stream, devName);
handle->curDev = devices;
handle->curMode = mode;
return err;
}
----------------------------------------------------------------
if (err) break;
if (devices & AudioSystem::DEVICE_OUT_WIRED_HDMI){
strcpy(mCurCard ,SPDIF);
mMixer = mMixerSpdif;
} else {
strcpy(mCurCard,SGTL5000);
mMixer = mMixerSgtl5000;
}
out = new AudioStreamOutALSA(this, &(*it));
err = out->set(format, channels, sampleRate);
break;
}
if (status) *status = err;
return out;
}
###############################################################################################
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&总结&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
函数openOutputStream的功能:
1、从mDeviceList中寻找匹配的设备。
寻找的依据是传入的参数devices。
参数devices是首先根据stream type获得strategy,然后再根据strategy取得的。
mDeviceList的内容是从_defaults[]数组中copy过来的。
若要使自己的声卡工作,需要在_defaults[]中加入自己声卡的信息,或者修改函数s_init的实现方式。
2、调用函数s_open,得到一个alsa_handle_t指针。
3、根据得到的alsa_handle_t指针创建一个AudioStreamOutALSA对象。
4、调用AudioStreamOutALSA对象的set函数设置stream的格式,声道和采样率。
5、至此,就完成了output stream的创建。
6、调用AudioStreamOutALSA对象的write函数即可实现播放。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&