Android Audio System线性音量和对数音量的转换

2024-05-27 12:08

本文主要是介绍Android Audio System线性音量和对数音量的转换,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android的音频系统的代码中,应用程序对每个音频流的音量做出调整后,最终会转换为一个系数K,所有的音频数据在输出到硬件之前,都要乘以系数K,只要应用程序发出调整音量的调用,中间层的Audio System就会重新计算系数K的值。对应用程序来说,音量控制通常都是按照线性进行调整的,比如对于具有15级音量的音频流来说,我们预期每级的音量变化都是相当的,也就是说:从第5级调到第6级,和从第7级调到第8级,我们期望人耳可以感觉到同样大小的音量变化。但是,在Android的代码中,我们看到了计算系数K的公式,它相当奇怪,代码位于frameworks/base/media/libmedia/audiosystem.cpp中:

/*****************************************************************************************************/
声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢!
/*****************************************************************************************************/

[cpp] view plain copy
  1. // convert volume steps to natural log scale  
  2.   
  3. // change this value to change volume scaling  
  4. static const float dBPerStep = 0.50f;  
  5. // shouldn't need to touch these  
  6. static const float dBConvert = -dBPerStep * 2.302585093f / 20.0f;  
  7. static const float dBConvertInverse = 1.0f / dBConvert;  
  8.   
  9. float AudioSystem::linearToLog(int volume)  
  10. {  
  11.     // float v = volume ? exp(float(100 - volume) * dBConvert) : 0;  
  12.     // LOGD("linearToLog(%d)=%f", volume, v);  
  13.     // return v;  
  14.     return volume ? exp(float(100 - volume) * dBConvert) : 0;  
  15. }  
  16.   
  17. int AudioSystem::logToLinear(float volume)  
  18. {  
  19.     // int v = volume ? 100 - int(dBConvertInverse * log(volume) + 0.5) : 0;  
  20.     // LOGD("logTolinear(%d)=%f", v, volume);  
  21.     // return v;  
  22.     return volume ? 100 - int(dBConvertInverse * log(volume) + 0.5) : 0;  
  23. }  

要理解上面代码中的公式,我们先要了解人耳的声心理学模型。根据人耳的声心理学的研究,人耳对声音大小的感知程度并不是线性的,而是呈对数关系。对数形式的单位是dB,在音频领域,通常我们会定义一个标准电平V0,那么电平X的转换公式是:

dB=20log(X/V0);

例如:我们给喇叭输出满负荷最大音量时的电平是1V,如果有15级音量,如果按线性进行调整,1/15 = 66.6mV,我们就得到每级音量的调整量是:

66.6mV,133.2mV,200mV,......,866.8mV,933.4mV,1000mV;

如果按照这个步长进行调整,人耳感觉到的音量变化就不是连续的。

另一种方式是按对数进行调整,在数字音频领域,通常0dB代表最大音量,0dB意味着不对数据进行任何的变换处理,输出等于输入,所以20log(V0/V0)=20log(1)=0dB。这意味着最大音量以下的dB值为一个负数,现在我们把1V认为是0dB,最低音量是-28dB,那么对应15级音量的dB值就是:

-28dB,-26dB,-24dB,......,-4dB,-2dB,0dB;

对应的电平值是( 使用公式Vx=10^(dB/20)*V0 ):

39mV,50mV,63mV,......,630mV,794mV,1000mV;


                                      线性音量和对数音量的调整曲线

回到Android的代码中,它也使用了对数的调节方式,它先是定义了每次调节音量的步长值为0.5dB:

static const float dBPerStep = 0.50f;

然后他定义了一个计算用的中间常数:

static const float dBConvert = -dBPerStep * 2.302585093f / 20.0f;

这个一开始有点难于理解,尤其是奇怪的系数:2.302585093。所有这些定义都是为了得到用于与音频数据相乘的系数K,Android中有多种音频流,每种音频流的默认音量调节步数都不一样,有的是7步,有的是5步,有的是15步,为了便于计算的统一,计算前都会先把相应的步数映射为0-100步之间,因为步长已经定义为0.5dB,所以各级音量对应的dB数如下:

音量级别0123......979899100
dB数mute-49.5dB-49dB-48.5dB......-1.5dB1.0dB0.5dB0dB

很显然,知道了音量为哪个步数级别后,相应的dB值也会知道,那么我们要做的就是把dB值转换为系数K值,K值实际上就是公式dB=20log(X/V0)中的比值:X/V0,根据此公式反推,音量级别为volume对应的K值:

(1)          dB = -dBPerStep * ( 100 - volume );

又因为:

(2)          dB/20 = log(Vx/V0) = log(K);

把(1)式代入(2)式:

(3)          -dBPerStep * ( 100 - volume ) / 20 = log(K);

为了得到K,两边取以10为底的指数:

(4)           10 ^ ( -dBPerStep * ( 100 - volume ) / 20 ) = 10 ^ ( log(K) );

(5)            K = 10 ^ ( log(K) ) = 10 ^ ( -dBPerStep * ( 100 - volume ) / 20 ) ;

(6)             K = 10 ^ ( dBConvert * ( 100 - volume ) ) ;      // 令:dBConvert = -dBPerStep  / 20;

使用(6)式即可得到系数K,需要计算以10为底的幂,可是这与Android使用的计算公式有些差异,Andrioid使用的公式是:

(7)            exp(float(100 - volume) * dBConvert);

这是因为它没有使用以10为底的幂运算,而是使用以自然常数e为底的幂运算,因为:

(8)           ln( 10)  = 2.302585093;

我们把dBConvert 重新定义为-dBPerStep * 2.302585093/ 20后,式子(6)和式子(7)实际上是完全等价的。也就是说:

(9)           e^2.302585093 = e^ln(10) = 10;

这下终于知道2.302585093这个奇怪数字的来历啦!!从代码的注释中,我们可以知道,只要改变dBPerStep的大小,就可以决定系统的最小音量了:

最小音量 = -99 * dBPerStep;默认情况下是-49.5dB,K值为:0.00334965439;

至于最大软件数字音量,就是0dB,不能改变,要改就修改底层的音频驱动的硬件音量吧!!


这篇关于Android Audio System线性音量和对数音量的转换的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1007430

相关文章

java Long 与long之间的转换流程

《javaLong与long之间的转换流程》Long类提供了一些方法,用于在long和其他数据类型(如String)之间进行转换,本文将详细介绍如何在Java中实现Long和long之间的转换,感... 目录概述流程步骤1:将long转换为Long对象步骤2:将Longhttp://www.cppcns.c

在Java中将XLS转换为XLSX的实现方案

《在Java中将XLS转换为XLSX的实现方案》在本文中,我们将探讨传统ExcelXLS格式与现代XLSX格式的结构差异,并为Java开发者提供转换方案,通过了解底层原理、性能优势及实用工具,您将掌握... 目录为什么升级XLS到XLSX值得投入?实际转换过程解析推荐技术方案对比Apache POI实现编程

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

Python使用FFmpeg实现高效音频格式转换工具

《Python使用FFmpeg实现高效音频格式转换工具》在数字音频处理领域,音频格式转换是一项基础但至关重要的功能,本文主要为大家介绍了Python如何使用FFmpeg实现强大功能的图形化音频转换工具... 目录概述功能详解软件效果展示主界面布局转换过程截图完成提示开发步骤详解1. 环境准备2. 项目功能结

使用Python实现网页表格转换为markdown

《使用Python实现网页表格转换为markdown》在日常工作中,我们经常需要从网页上复制表格数据,并将其转换成Markdown格式,本文将使用Python编写一个网页表格转Markdown工具,需... 在日常工作中,我们经常需要从网页上复制表格数据,并将其转换成Markdown格式,以便在文档、邮件或

Python将字符串转换为小写字母的几种常用方法

《Python将字符串转换为小写字母的几种常用方法》:本文主要介绍Python中将字符串大写字母转小写的四种方法:lower()方法简洁高效,手动ASCII转换灵活可控,str.translate... 目录一、使用内置方法 lower()(最简单)二、手动遍历 + ASCII 码转换三、使用 str.tr

Java如何将文件内容转换为MD5哈希值

《Java如何将文件内容转换为MD5哈希值》:本文主要介绍Java如何将文件内容转换为MD5哈希值的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java文件内容转换为MD5哈希值一个完整的Java示例代码代码解释注意事项总结Java文件内容转换为MD5

使用Java将实体类转换为JSON并输出到控制台的完整过程

《使用Java将实体类转换为JSON并输出到控制台的完整过程》在软件开发的过程中,Java是一种广泛使用的编程语言,而在众多应用中,数据的传输和存储经常需要使用JSON格式,用Java将实体类转换为J... 在软件开发的过程中,Java是一种广泛使用的编程语言,而在众多应用中,数据的传输和存储经常需要使用j

Java实现视频格式转换的完整指南

《Java实现视频格式转换的完整指南》在Java中实现视频格式的转换,通常需要借助第三方工具或库,因为视频的编解码操作复杂且性能需求较高,以下是实现视频格式转换的常用方法和步骤,需要的朋友可以参考下... 目录核心思路方法一:通过调用 FFmpeg 命令步骤示例代码说明优点方法二:使用 Jaffree(FF

C语言中的常见进制转换详解(从二进制到十六进制)

《C语言中的常见进制转换详解(从二进制到十六进制)》进制转换是计算机编程中的一个常见任务,特别是在处理低级别的数据操作时,C语言作为一门底层编程语言,在进制转换方面提供了灵活的操作方式,今天,我们将深... 目录1、进制基础2、C语言中的进制转换2.1 从十进制转换为其他进制十进制转二进制十进制转八进制十进