当前位置: 首页 > news >正文

Android14 系统左右声音通道设置代码

Android14 系统左右声音通道设置代码

文章目录

  • Android14 系统左右声音通道设置代码
    • 一、前言
    • 二、系统级设置左右声音通道分析
      • 1、各方案设置左右声音通道的主要代码
        • (1)3588 Android13 方案的实现
        • (2)9679 Android14 方案的实现
        • (3)311D2 Android13 方案的实现
      • 2、串口验证左右声道平衡设置
      • 3、AudioService.java 监听并设置左右声音通道平衡
      • 4、AudioSystem.java 上层设置左右声音通道平衡的主要代码
    • 三、其他
      • 1、Android设置左右声音通道代码小结
        • (1)串口设置左右平衡声音通道:
      • 2、为啥不用方案需要调用不同的声音通道接口实现?

一、前言

Android 系统左右声音通道设置在有些场景下会有用,信源或者多音箱的条件下可能需要设置,
还有些需求比如:用户偏好设置、将声音定位到特定方向,提升沉浸感。

最简单的理解:

比如耳机接入Android设备后,设置声音通道左平衡,只有左边的耳机出声;
设置声音通道右平衡,只有右边的耳机出声;

上面这样就可以简单验证系统左右声音通道是否有效。

网上基本找不到系统级的设置左右声音通道的代码,即使使用AI工具也搜不到。
网上设置左右声音通道都是针对某个播放媒体对象进行设置,并不是全局的。
网上设置左右声音通道关键部分代码如下:

    // 1、应用左右声道平衡,audioTrack 设置
    for (int i = 0; i < audioData.length; i += 2) {
        audioData[i] = (short) (audioData[i] * leftVolume); // 左声道
        audioData[i + 1] = (short) (audioData[i + 1] * rightVolume); // 右声道
    }

    audioTrack.write(audioData, 0, audioData.length);

    // 2、应用左右声道平衡,MediaPlayer 设置
    mediaPlayer = MediaPlayer.create(this, R.raw.your_audio_file);
        if (mediaPlayer != null) {
            // 设置左声道音量为 0.2,右声道音量为 0.8
            mediaPlayer.setVolume(0.2f, 0.8f);
            mediaPlayer.start();
        }

上面的两种方法都是针对某个音频流对象,并不针对系统的。

那么系统有接口方法吗?

其实是有设置左右声音通道接口的,本文记录总结一下。

二、系统级设置左右声音通道分析

目前接触的系统芯片方案主要有:RK、MTK、AML;他们的设置左右声音通道代码居然都不一样。

1、各方案设置左右声音通道的主要代码

(1)3588 Android13 方案的实现
    @Override
    public int getBalance() throws RemoteException {
                 float result = 0.0f;
                 int value_int = 0;
                 String value = "";
                 try {
            result = Settings.System.getFloatForUser(mContext.getContentResolver(),
                Settings.System.MASTER_BALANCE, UserHandle.USER_CURRENT);
                        value_int = (int) Math.round(result);
                        Log.d(TAG,"getBalance result == " + result+",value_int:"+value_int);

        } catch (Exception e) {
                e.printStackTrace();
        }
        return value_int;
    }

    @Override
    public void setBalance(int balance) throws RemoteException {
                synchronized (lock) {
                        Settings.System.putFloatForUser(mContext.getContentResolver(),
                Settings.System.MASTER_BALANCE, uiValue2parameterValue(balance), UserHandle.USER_CURRENT);
                }
    }

        //将UI 输入值 [-50, 50] 映射到 参数范围 [-1.0, 1.0]
        public  float uiValue2parameterValue(int value) {
                float y = ((float)(value + 50) / 50) - 1.0f;
        return y;
    }

上面看起来是比较原生的设置代码;
通过读取和设置 Settings.System.MASTER_BALANCE 的值设置左右声音平衡
系统中的 System.MASTER_BALANCE 的是值-1f ~ 1f,需要转换成-50 ~ 50
用户获取和设置的左右平衡值范围是-50 ~ 50。

(2)9679 Android14 方案的实现
import com.mediatek.tv.oneworld.tvapi.audio.TvAudioManager; //mtk api

    private TvAudioManager mTvAudioManager;
    mTvAudioManager = new TvAudioManager(mContext);

    @Override
    public int getBalance() throws RemoteException {
        int result = mTvAudioManager.get(mContext, TvAudioManager.CFG_AUD_AUD_BALANCE, 0);
        DebugLog.debug("getBalance result == " + result);
        return result;
    }

    @Override
    public void setBalance(int balance) throws RemoteException {
        // balance : (-50 ~ 50)
        mTvAudioManager.set(mContext, TvAudioManager.CFG_AUD_AUD_BALANCE, String.valueOf(balance));
        DebugLog.debug("setBalance: balance == " + balance);
    }


(3)311D2 Android13 方案的实现
        import com.droidlogic.app.AudioEffectManager; //AML方案
        private AudioEffectManager mAudioEffectManager;


       public SkgVoiceService(Context applicationContext) {
              mContext = applicationContext;
              mAudioEffectManager = AudioEffectManager.getInstance(applicationContext);
              DebugLog.debug("");
          }
      


          @Override
          public int getBalance() throws RemoteException {
              int result = mAudioEffectManager.getBalanceStatus();
              DebugLog.debug("getBalance result == " + result);
              return result;
          }
      
          @Override
          public void setBalance(int balance) throws RemoteException {
              mAudioEffectManager.setBalance(balance);
              DebugLog.debug("setBalance and no return balance == " + balance);
          }

后面的MTK 和AML 方案上,实现都不一样,都是供应商封装的接口;
通过Settings属性查看并没有修改上层的 System.MASTER_BALANCE 属性值;
可能是直接控制底层接口完成的左右声音通道平衡设置,这里不进行深入研究。

从上面的代码看,RK实现左右声音通道平衡设置的代码可以移植使用。

那么其他方案也可以用Settings属性值设置的方式吗?

2、串口验证左右声道平衡设置

属性:Settings.System.MASTER_BALANCE 的设置和读取:
settings 属性名称都是纯小写的。

settings get  system master_balance //获取当前左右声音通道平衡值,默认 null

settings put system master_balance -1 //设置左平衡,左边音箱出声
settings put system master_balance 1 //设置右平衡,右边音箱出声
settings put system master_balance 0 //设置左右平衡,左右音箱都出声,默认情况

通过串口命令验证了下确实可用。MTK方案上试了有用。

为啥会有用呢,应该是系统某个地方监听了settings属性变化,
最后调用某个接口,实现了调用逻辑。

查看了系统代码,确实有监听的地方,就在 AudioService.java

3、AudioService.java 监听并设置左右声音通道平衡

framework\base\services\core\java\com\android\server\audio\AudioService.java

下面是主要代码:


  private class SettingsObserver extends ContentObserver {

        SettingsObserver() {
            super(new Handler());
... //十几个Settings 属性状态的监听
            mContentResolver.registerContentObserver(Settings.Global.getUriFor(
                Settings.Global.DOCK_AUDIO_MEDIA_ENABLED), false, this);
            mContentResolver.registerContentObserver(Settings.System.getUriFor(
                    Settings.System.MASTER_MONO), false, this);
            
            //这个有注册监听 Settings.System.MASTER_BALANCE 的变化
            mContentResolver.registerContentObserver(Settings.System.getUriFor(
                    Settings.System.MASTER_BALANCE), false, this);

        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
  
            synchronized (mSettingsLock) {

                readDockAudioSettings(mContentResolver); //读取停靠音频设置
                updateMasterMono(mContentResolver); //更新主声道单声道
                updateMasterBalance(mContentResolver); //更新主声道平衡,左右声音通道平衡设置
                updateEncodedSurroundOutput(); //更新编码环绕声输出
                sendEnabledSurroundFormats(mContentResolver, mSurroundModeChanged); //发送启用的环绕声格式
                updateAssistantUIdLocked(/* forceUpdate= */ false); //并更新助手UID(不强制更新)
            }
        }
    ...
    }



    //继续追踪左右声道平衡的代码逻辑
    private void updateMasterBalance(ContentResolver cr) {
        //或者左右声音通道平衡的具体值
        final float masterBalance = System.getFloatForUser(
                cr, System.MASTER_BALANCE, 0.f /* default */, UserHandle.USER_CURRENT);
        if (DEBUG_VOL) { // DEBUG_VOL 默认true
            Log.d(TAG, String.format("Master balance %f", masterBalance));
        }
        //调用 AudioSystem.setMasterBalance 方法设置左右声音通道
        if (AudioSystem.setMasterBalance(masterBalance) != 0) {
            Log.e(TAG, String.format("setMasterBalance failed for %f", masterBalance));
        }
    }

上面可以看到左右声音通道平衡的设置最终是调用了 AudioSystem 的接口方法。

import android.media.AudioSystem;
AudioSystem.setMasterBalance(masterBalance); //设置值范围:-1f ~ 1f

AudioSystem 接口和方法默认是隐藏的,系统framework中可以调用到。
系统签名应用也是无法调用到,需要导入系统的framework jar包就可能调用到AudioSystem 的类。

4、AudioSystem.java 上层设置左右声音通道平衡的主要代码

framework\base\media\java\android\media\AudioSystem.java

/**
 * @hide
 */
@TestApi
public class AudioSystem
{
    private static final boolean DEBUG_VOLUME = false;

    private static final String TAG = "AudioSystem";

    // private constructor to prevent instantiating AudioSystem
    private AudioSystem() { //不能 new
        throw new UnsupportedOperationException("Trying to instantiate AudioSystem");
    }

//暴露的都是静态方法,所以不用new。

    /** @hide returns master balance value in range -1.f -> 1.f, where 0.f is dead center. */
    @TestApi
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS)
    public static native float getMasterBalance();
    
    /** @hide Changes the audio balance of the device. */
    @TestApi
    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS)
    public static native int setMasterBalance(float balance);

}

上面已有提示 左右声音通道的值范围是:-1.f -> 1.f

声音通道平衡的具体实现是nativie的调用,后续不做进一步分析。
有兴趣的自行查阅:framework\base\core\jni\android_media_AudioSystem.cpp

三、其他

1、Android设置左右声音通道代码小结

(1)串口设置左右平衡声音通道:
settings get  system master_balance //获取当前左右声音通道平衡值,默认 null

settings put system master_balance -1 //设置左平衡,左边音箱出声
settings put system master_balance 1 //设置右平衡,右边音箱出声
settings put system master_balance 0 //设置左右平衡,左右音箱都出声,默认情况

Android 原生设置左右声音通道代码:

//1、Settings属性设置,AudioService.java 有监听变化和调用接口
 Settings.System.putFloat(mContext.getContentResolver(),Settings.System.MASTER_BALANCE, balance);

//2、AudioSystem 的静态方法
AudioSystem.setMasterBalance(masterBalance)

如果是系统签名应用,但是又没有导入framework的jar包,
是无法识别AudioSystem的,并且System.MASTER_BALANCE 也是无法识别的;

可以使用代码直接设置具体属性就行:

 Settings.System.putFloat(mContext.getContentResolver(), "master_balance", balance);

2、为啥不用方案需要调用不同的声音通道接口实现?

这里不去看他们的具体实现,直接简单分析一下。

对比了不同的Android版本的系统代码:

Android 9或者更旧的代码 AudioSystem.java 里面是没有setMasterBalance方法的;
Android11 的系统代码中是有AudioSystem.setMasterBalance 方法的。

所以之前Android9之前的版本,不得不使用自定义的接口调用底层声音设置。
所以不同供应商提供的调用接口是有差异的。
这个只是我的一个猜测哈。

测试了一下MTK方案:

使用供应商提供的设置声音接口或者使用Settings属性都是可以设置左右声音通道平衡的;
但是有个小问题:Settings属性设置左声道后,再次调用供应商的接口设置左声道或者右声道,会导致设备没有声音;
重新用供应商的接口设置左右声音通道值为0 ,设备就有声音了。

估计是底层兼容有问题!同时设置可能标志位会错乱。
如果能只用Google接口实现是最好的。毕竟Google考虑的应该比较全一点。

最近也是发现一个供应商的蓝牙音箱左右声音通道问题:
连接耳机设置左右声音通道都是没有问题的。

AML 和MTK 的方案都是存在的:
连接多音箱的蓝牙设备,设置左右声音通道没有用!
RK 的方案是没有问题的,因为它是使用的Google原生接口设置的左右声音通道实现。

这种情况有可能就是 AML 和MTK 的方案 没有考虑蓝牙连接下的左右声音通道平衡导致 。

相关文章:

  • 今天你学C++了吗?——二叉搜索树
  • 深入理解Python闭包与递归:原理、应用与实践
  • java项目之基于ssm的少儿编程在线培训系统(源码+文档)
  • 《K230 从熟悉到...》屏幕手画板
  • AI 原生 IDE Trae 深度体验:SSHremote 功能如何重新定义远程开发与云原生部署
  • 项目-苍穹外卖(九) 店铺营业状态设置+HttpClient
  • Node.js模块:使用 Bull 打造高效的任务队列系统
  • SQL Server数据库简介及应用
  • 网络空间安全专业发展历程及开设院校
  • 【SpringBatch】03步骤对象 (Step)控制与执行流程设计
  • 学习记录 6 pointnet复现
  • 【深度学习量化交易18】盘前盘后回调机制设计与实现——基于miniQMT的量化交易回测系统开发实记
  • 美团Leaf分布式ID生成器使用教程:号段模式与Snowflake模式详解
  • 《UNIX网络编程卷1:套接字联网API》第2章 传输层:TCP、UDP和SCTP
  • [入门]NUC13配置Ubuntu20.04详细步骤
  • 三分钟掌握视频分辨率修改 | 在 Rust 中优雅地使用 FFmpeg
  • v-on=“$listeners“ 这个写法已经废弃了,如进行代替
  • 指令系统1(数据传输指令)
  • 电子工程师转战汽车OEM主机厂之路
  • (保姆级教程)CAN总线—如何使用CANoe(VN1640)的Scaner功能测量样件的波特率
  • 4月企业新发放贷款利率处于历史低位
  • 学习教育期间违规吃喝,李献林、叶金广等人被通报
  • 【社论】个人破产探索,要守住“诚实而不幸”的底线
  • 上海现有超12.3万名注册护士,本科及以上学历占一半
  • 人民日报整版聚焦:专家学者看2025中国经济增长点
  • 快评|印巴为何停火?已达成“一场胜利,各自表述”的效果