Android7.1 音频声音控制策略

本文深入探讨了在特定条件下,为何音乐流会在Speaker和耳机同时输出时被静音的问题,并提出了有效的解决方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们知道在AUDIO_STREAM_MUSIC的stream类型下,声音是从Speaker或则耳机输出的,为了加深对AudioPolicy的认识,希望通过修改代码实现在AUDIO_STREAM_MUSIC的stream类型下,声音可以从Speaker和耳机同时输出。其实办法有2个,一个是改Framwork层,一个方法是改Hal层,原理其实是一致的。

Track::start
	AudioPolicyManager::startOutput
		startSource
			setOutputDevice
				createAudioPatch
HAL:				out_set_parameters       (高通不支持AudioPatch,转换为设置parameters)
						修改stream输出设备的指向:out->devices

如上流程就是修改音频输出设备的地方,因此如果修改Hal的方式,那么只需要如下改动即可:

hardware\qcom\audio\hal\Audio_hw.c

int start_output_stream(struct stream_out *out)
{
	...
	// Jon Add Begin
	out->devices = SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES
	// End
	...
	uc_info->devices = out->devices;
	...
	//完成声卡和路由的切换和打开
	select_devices(adev, out->usecase);
	...
}

关于第二种方式有点曲折,我是这么修改的:

hardware\qcom\audio\policy_hal\AudioPolicyManager.cpp

status_t AudioPolicyManagerCustom::startSource(sp<AudioOutputDescriptor> outputDesc,
                                             audio_stream_type_t stream,
                                             audio_devices_t device,
                                             const char *address,
                                             uint32_t *delayMs)
{
	...
	//Begin Jon Add
	if(stream == AUDIO_STREAM_MUSIC)
		device = AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADSET;
	//End
	
	muteWaitMs = setOutputDevice(outputDesc, device, force, 0, NULL, address);
	...
}

这种方式修改后发现无论是Speaker还是耳机都没有声音,同时adb去查看Speaker和耳机的控件,发现device和dai都是正确打开了的,百思不得其解,最后再一思索,音频路径虽然都打通了,如果音量被设置为0了,不也没有声音吗。果然:

adb shell dumpsys media.audio_policy > D:\policy.txt

- Output 29 dump:
 profile name: deep_buffer
 Latency: 80
 Flags 00000008
 ID: 6
 Sampling rate: 48000
 Format: 00000001
 Channels: 00000003
 Devices 00000006
 Stream volume refCount muteCount
 00     0.000     00       01
 01     -764.000     00       01
 02     -14.700     00       00
 03     -758.00     03       01                   //对应这条stream
 04     -14.450     00       00
 05     -764.000     00       01
 06     -1.000     00       01
 07     -764.000     00       01
 08     -758.000     00       01
 09     0.000     00       01
 10     -758.000     00       01
 11     0.000     00       01
 12     -1.000     00       00

我们的stream是music类型,因此序号为3,我们发现volume为 -758,果然是被静音了。

我们借此为契机,分析下为什么这种情况下music的stream会被静音

hardware\qcom\audio\policy_hal\AudioPolicyManager.cpp

uint32_t AudioPolicyManager::setOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
                                             audio_devices_t device,
                                             bool force,
                                             int delayMs,
                                             audio_patch_handle_t *patchHandle,
                                             const char* address)
{
	...
	//特别留意这条,会将音频路径的默认设备输出改变
	if (device != AUDIO_DEVICE_NONE) {
        outputDesc->mDevice = device;
    }
    
    muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevice, delayMs);
    ...
}

静音路径如下:

checkDeviceMuteStrategies
	setStrategyMute
		setStreamMute
			checkAndSetVolume
				float volumeDb = computeVolume(stream, index, device);
				outputDesc->setVolume(volumeDb, stream, device, delayMs, force);

我们重点分析下checkDeviceMuteStrategies这个函数就好:

uint32_t AudioPolicyManager::checkDeviceMuteStrategies(sp<AudioOutputDescriptor> outputDesc,
                                                       audio_devices_t prevDevice,
                                                       uint32_t delayMs)
{

    uint32_t muteWaitMs = 0;
    //获取当前音频路径的默认输出设备,此刻已经被改变为Speaker+耳机
    audio_devices_t device = outputDesc->device();
    //是否静音的判断原则,当前音频路径处于活跃状态,而且新设置的输出设备终端多余2个,
    //很不幸,我们当前全都满足,shouldMute为 ture
    bool shouldMute = outputDesc->isActive() && (popcount(device) >= 2);

    for (size_t i = 0; i < NUM_STRATEGIES; i++) {
    	//对于STRATEGY_MEDIA策略,返回curDevice 为0x04(耳机),针对这个函数后面有分析
        audio_devices_t curDevice = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/);
        //主输出的路径上肯定是有耳机的,因此curDevice还是为0x04
        curDevice = curDevice & outputDesc->supportedDevices();
        //curDevice为0x04,但是device是0x06,所有mute为true
        bool mute = shouldMute && (curDevice & device) && (curDevice != device);
        bool doMute = false;
		//由于设备描述符在构造的时候,对于所有的策略mStrategyMutedByDevice全部赋值为false,因此doMute被赋值为true了
        if (mute && !outputDesc->mStrategyMutedByDevice[i]) {
            doMute = true;
            outputDesc->mStrategyMutedByDevice[i] = true;
        } else if (!mute && outputDesc->mStrategyMutedByDevice[i]){
            doMute = true;
            outputDesc->mStrategyMutedByDevice[i] = false;
        }
        if (doMute) {
        	//我机子总共4条音频路径,取出所有音频路径逐个和当前调用的音频路径的比较是否支持的设备集合有交集,但凡有交集,那么对应的音频路径在这个策略上都会被mute
            for (size_t j = 0; j < mOutputs.size(); j++) {
                sp<AudioOutputDescriptor> desc = mOutputs.valueAt(j);
                // skip output if it does not share any device with current output
                if ((desc->supportedDevices() & outputDesc->supportedDevices())
                        == AUDIO_DEVICE_NONE) {
                    continue;
                }
                
				
                setStrategyMute((routing_strategy)i, mute, desc, mute ? 0 : delayMs);
                ...
           }
	}
	...
    return 0;
}

我们先分析getDeviceForStrategy,根据策略获取当前的输出设备

audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
                                                         bool fromCache)
{
    // Routing
    // see if we have an explicit route
    // scan the whole RouteMap, for each entry, convert the stream type to a strategy
    // (getStrategy(stream)).
    // if the strategy from the stream type in the RouteMap is the same as the argument above,
    // and activity count is non-zero
    // the device = the device from the descriptor in the RouteMap, and exit.
	/*
		下面的逻辑是从当前所有的音频路由中选择一条策略相同的,返回其路由指向的输出设备即可。原则有2条
		1. 音频路由中策略选择,是依据路由中的stream类型来判断
		2. 选中的音频路由,必须是active的(设备描述符不为空,与此同时这条音频路由发生了改变或者引用计数大于0)
		遗憾的是由于我当前的更改并不是track->setOutputDevice的方式,因此路由不会发生改变,当然会话的引用计数也为0,从而导致条件不满足,调用后面的mEngine->getDeviceForStrategy
	*/
    for (size_t routeIndex = 0; routeIndex < mOutputRoutes.size(); routeIndex++) {
        sp<SessionRoute> route = mOutputRoutes.valueAt(routeIndex);
        routing_strategy routeStrategy = getStrategy(route->mStreamType);
        if ((routeStrategy == strategy) && route->isActive()) {
            return route->mDeviceDescriptor->type();
        }
    }
	//这里fromCache为false
    if (fromCache) {
        ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x",
              strategy, mDeviceForStrategy[strategy]);
        return mDeviceForStrategy[strategy];
    }
	
	//直接根据音频策略获取输出设备,当前我插着耳机,因此必然返回0x04(耳机)
    return mEngine->getDeviceForStrategy(strategy);
}

checkDeviceMuteStrategies最后会调用到setStrategyMute,如下

void AudioPolicyManager::setStrategyMute(routing_strategy strategy,
                                             bool on,
                                             const sp<AudioOutputDescriptor>& outputDesc,
                                             int delayMs,
                                             audio_devices_t device)
{
    ALOGVV("setStrategyMute() strategy %d, mute %d, output ID %d",
           strategy, on, outputDesc->getId());
    for (int stream = 0; stream < AUDIO_STREAM_FOR_POLICY_CNT; stream++) {
        if (getStrategy((audio_stream_type_t)stream) == strategy) {
        	//将system和music(都是STRATEGY_MEDIA策略)的stream在当前音频路径上静音
            setStreamMute((audio_stream_type_t)stream, on, outputDesc, delayMs, device);
        }
    }
}

而我的测试代码正好是music的stream,因此被静音了。原因就在于这么暴力的修改并没有触发音频路由的改变,导致stream被静音,然后track->setOutputDevice虽然能够触发音频路由的改变,但是它并不能设置多个snd_device的输出,原因在前文分析过,它是比较的id(会自然增长)而不是匹配的device type。

有的小伙伴在这儿可能会有疑问,那为啥铃声类型的stream却可以从Speaker和耳机同时输出呢。
原因在于我们new track的时候会Call到getOutputForAttr

frameworks\av\services\audiopolicy\managerdefault\audioPolicyManager.cpp

status_t AudioPolicyManager::getOutputForAttr(...)
{
	audio_attributes_t attributes;
	if (attr != NULL) {
		attributes = *attr;
	} else {
		stream_type_to_audio_attributes(*stream, &attributes);
	}
	...
	//根据当前音频的属性,获取当前track的音频策略,这里的音频策略返回STRATEGY_PHONE
    routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);
    //根据当前track的音频策略,获取当前音频的输出终端,这里会根据STRATEGY_PHONE的策略返回输出设备为0x06(Speaker和耳机)
    audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/);
    //根据音频设备,音频输出标识,format等选择输出路径,这个函数的详细分析请查看本文最后
    *output = getOutputForDevice(device, session, *stream,
                                 samplingRate, format, channelMask,
                                 flags, offloadInfo);
	...
}

如上

因此回到最初的话题,如果我非得在Framework中改下,使得music可以在耳机和Speaker同时输出,很显然我们只需要将getDeviceForStrategy返回耳机+speaker即可
如下:

frameworks\av\services\audiopolicy\managerdefault\AudioPolicyManager.cpp

audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy,
                                                         bool fromCache)
{
	...
	
	/*Begin Add Jon*/
	if(strategy == STRATEGY_MEDIA)
		return AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADSET;
	/*End*/
	
    return mEngine->getDeviceForStrategy(strategy);
}
<think>好的,我现在需要帮助用户解决如何在Android 7.1的RK3288平台上修改源码,以设置喇叭的最大功率为4欧3瓦的问题。首先,我需要明确用户的需求。用户提到的是RK3288芯片,这是一个Rockchip的处理器,常用于平板或嵌入式设备。喇叭规格是4欧姆3瓦,这说明用户可能在进行音频硬件适配,或者遇到了音量过大导致喇叭过载的问题。 接下来,我需要考虑Android音频系统中与功率相关的部分。通常,喇叭的最大功率设置可能涉及到音量控制、增益限制或硬件驱动参数。在Android系统中,音频策略和硬件抽象层(HAL)是调整这些参数的关键部分。 首先,应该想到的是音频增益控制。Android音频策略文件(audio_policy.conf或audio_policy_configuration.xml)可能包含不同设备的输出配置,比如扬声器的增益值。用户可能需要调整这些增益值来限制最大输出功率,防止超过3瓦。 然后,硬件驱动部分。RK3288可能使用特定的音频编解码器,比如RK616或RK1000,或者通用的ALSA驱动。在驱动代码中,可能会有与输出功率相关的寄存器配置,比如设置放大器的增益级别或输出限制。需要检查驱动源码中的相关部分,比如在sound/soc/codecs目录下的代码,寻找与扬声器功率相关的参数。 另外,可能需要修改设备树(DTS)文件,配置音频接口的参数,比如设置正确的阻抗和功率限制。设备树中的音频节点可能包含与硬件相关的配置,如rockchip,codec属性或放大器配置。 还要考虑Android音频混音器设置。使用amixer工具或查看mixer_paths.xml文件,调整输出通道的增益和音量级别。例如,降低Speaker Volume的dB值,限制最大音量输出。 需要注意的是,直接修改源码可能需要对硬件有深入了解,否则错误的设置可能导致硬件损坏或音频失真。因此,建议用户先通过软件方式调整音量上限,比如在frameworks/base中修改音量控制的最大值,观察效果,再考虑硬件参数的调整。 此外,用户可能需要参考RK3288的硬件手册,了解音频输出功率的计算方式,确保软件设置的增益和硬件参数匹配。例如,计算特定负载(4欧姆)下的电压和电流,确保输出功率不超过3瓦,这可能需要调整D类放大器的配置或输出级的限流设置。 最后,测试和验证是关键。修改后需要编译并烧系统,通过实际测量输出功率来确认设置是否有效,避免因软件设置不当导致的硬件问题。</think>在Android 7.1 RK3288平台上设置喇叭最大功率为4Ω3W的源码修改建议(需硬件设计支持): $$ 关键修改路径 = \begin{cases} 1. \text{驱动层配置} \\ \quad \triangleright \texttt{kernel/sound/soc/codecs/rk...\_codec.c} \\ \quad \triangleright \texttt{kernel/arch/arm/boot/dts/rk3288-...-audio.dtsi} \\ 2. \text{Android HAL层} \\ \quad \triangleright \texttt{hardware/rockchip/audio/tinyalsa\_hal/...} \\ 3. \text{音量策略控制} \\ \quad \triangleright \texttt{frameworks/base/services/core/java/com/android/server/audio/...} \end{cases} $$ 具体实施步骤: 1. **硬件驱动层限制**(以RK616/RK1000 codec为例): ```c // 在codec驱动中设置输出功率限制 static const struct snd_kcontrol_new rk616_speaker_controls[] = { SOC_SINGLE_TLV("Speaker Playback Volume", RK616_SPK_CTL, RK616_VOL_SFT, 0x3f, 0, rk616_vol_tlv), // 修改tlv参数限制增益范围 SOC_SINGLE("Speaker Power Limit", RK616_PWR_CTL, RK616_PWR_SFT, 3, 0) // 新增功率限制寄存器配置 }; ``` 2. **设备树配置**(DTS文件): ```dts &i2s0 { rockchip,codec = <&codec>; rockchip,audio-power = <3>; // 单位:瓦特 rockchip,audio-impedance = <4>; // 单位:欧姆 }; &codec { spk-gain-db = <12>; // 降低默认增益值 spk-max-power-mw = <3000>; // 3W限制 }; ``` 3. **HAL层音量映射**: ```xml <!-- mixer_paths.xml --> <path name="speaker"> <ctl name="Speaker Playback Volume" value="52" /> <!-- 原值63 --> <ctl name="SPK GAIN" value="12dB" /> <!-- 降低增益 --> </path> ``` 4. **Android音量策略**: ```java // AudioService.java private static final int MAX_STREAM_VOLUME = new int[] { 15, // STREAM_VOICE_CALL 15, // STREAM_SYSTEM 10, // STREAM_RING -> 降低音乐类音量最大值 15, // STREAM_MUSIC ... }; ``` 功率计算验证公式: $$ P = \frac{V_{rms}^2}{R} \leq 3W $$ 其中: - $V_{rms}$ 为输出电压有效值 - $R = 4Ω$ 喇叭阻抗 注意事项: 1. 需要配合硬件电路设计(如功放芯片选型) 2. 修改前测量实际输出电压: $$ adb shell tinymix 'SPK GAIN' $$ 3.后使用示波器测量最大输出电压,确保: $$ V_{max} \leq \sqrt{P \times R} = \sqrt{3 \times 4} \approx 3.464V $$ 4. 建议在设置中增加过载保护逻辑: ```c // 音频驱动中增加动态检测 if (current_power > 3000) { // 3W阈值 reduce_gain(); log_warn("Speaker power exceeded limit!"); } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值