【问题标题】:What does Android's getMaxAmplitude() function for the MediaRecorder actually give me?Android 的 MediaRecorder 的 getMaxAmplitude() 函数实际上给了我什么?
【发布时间】:2012-05-18 15:42:15
【问题描述】:

Android MediaRecorder 有一个功能

.getMaxAmplitude();
正如API 告诉我的那样,“返回自上次调用此方法以来采样的最大绝对幅度。” 但我可以'找不到这是什么幅度?是帕斯卡还是瓦特?

我在网络上的几个页面上发现,您可以使用(如建议的here)计算与分贝密切相关的值。

double db = (20 * Math.log10(amplitude / REFERENCE)); 

这会让我假设返回值是某种线性比例(可能类似于 milipascal...)

REFERENCE=0.1(我知道这应该类似于 2*10^(-5) Pascal ((20 uPascal)),但它会返回奇怪的值...... 0.1 奇怪地工作得更好。)

现在我使用

测量 MaxAmplitude()
getMaxAmplitude()
并将其放入变量amplitude

这是方法:

public double getNoiseLevel() 
{
    //Log.d("SPLService", "getNoiseLevel() ");
    int x = mRecorder.getMaxAmplitude();
    double x2 = x;
    Log.d("SPLService", "x="+x);
    double db = (20 * Math.log10(x2 / REFERENCE));
    //Log.d("SPLService", "db="+db);
    if(db>0)
    {
        return db;
    }
    else
    {
        return 0;
    }
}

这在半秒内完成了 5 次,这是平均水平

for(int i=0; i<5; i++)
{
    try 
    {
            Thread.sleep(100);
    } 
    catch (InterruptedException e) 
    {
            e.printStackTrace();
            return 0;
    }
    level = level+getNoiseLevel();
    if(level>0)
    {
        counter++;
    }
}
level=level/counter;
Log.d(LOG_TAG, "level="+level);

我得到的东西有点像分贝,但我不确定它实际上是分贝......

那么,有人可以帮我解决这个问题吗? API 根本没有指定返回的内容,这似乎很奇怪......

【问题讨论】:

  • 这行得通,感谢您解释 getMaxAmplitude 的作用。但我不确定这些是否是准确的 dB 值。当我在某些情况下进行测试并尝试将值与此进行比较时:newton.dep.anl.gov/askasci/phy99/phy99405.htm,缺少 30dB。你知道为什么吗?
  • 可能有以下几个原因: 1. 该算法使用getMaxAmplitude作为计算基础,即忽略该周期内所有较小的事件。这导致结果可能高于实际 dB 水平(通常在自然环境中它们是) 2. 电话的麦克风是不同的。有些人比其他人更敏感。该算法没有考虑到这一点,在任何手机上进行相同的计算。由于这些差异,一些手机可能会给出更高或更低的值。
  • 另外,由于限制因素(该值仅达到32767),无法准确检测到非常响亮的噪音。通常截止频率约为 100 dB。
  • 好的,那有没有更好的解决方案呢?要从麦克风中获得一些准确的 dB 值?市场上有一些应用程序(即:声级计)可以完美地做到这一点。
  • 显然有。如果您存储来自录音机的原始数据,您可以对其进行复杂的分析,包括使用过滤/频率分析来弥补麦克风的缺点。例如,您可以使用斯坦福的 FFT-Packed,并可能从样本中计算出更好的值,而不是谨慎的 maxAmplitude 值。然而,所有这些都超出了原始问题的范围,可能已经在其他地方讨论过。

标签: android mediarecorder decibel


【解决方案1】:

我可以找到这个问题的答案,我会在这里分享给关心的人:MediaRecorder.getMaxAmplitude() 函数返回无符号的 16 位整数值 (0-32767)。这可能只是范围从 -32768 到 32767 的 CD 质量样本值的 abs()。这意味着它们可能代表麦克风构建的 0-100% 最大电压范围的电输出的 16 位数字化进那部手机。由于即使在一个品牌的手机中,这些麦克风的精确范围有时也会有所不同,即使对于类似的手机而言,在与相同声源的距离相同的情况下也不一定会返回相同的值。

然而,这个值与 Pascal 中的声压相关,因为它也是声压的线性量化,在可以用给定麦克风测量声音的区域(由于手机的限制,它不会覆盖整个频谱)。

【讨论】:

  • 无符号 16 位整数实际上是 0-65536,而不是 0-32767,所以我猜这个值被移动了,所以它是正数
  • @mcont 答案说“可能”取的是从 -32768 到 32768 的 16 位值的绝对值。因此,输出将介于 0 和 32768
【解决方案2】:

在这方面做了更多工作。使用经过校准的 SPL 计和具有不同纯频率、白噪声和粉红噪声的智能手机进行的一些测试,我现在知道手机麦克风不适用于任何应在 90 到 100 dB(SPL)以上的地方注册的任何东西,具体取决于手机.

假设 90 dB(SPL) 是最大值,可以计算出这对应于麦克风上 0.6325 Pa 的压力。现在假设 p0=0.0002 Pa 是参考最小值,并假设这将在 getMaxAmplitude() 中注册为 0(实际上永远不会发生),我们可以将 getMaxAmplitude() 函数的值与麦克风的最大压力相关联。这意味着来自 getMaxAmplitude() 的 16375 的结果将对应于 0.3165 Pa 的最大压力。这当然不是很科学,因为最大值和最小值纯属猜想,但它为我们提供了一个起点。我们现在可以用

计算 p

p=getMaxAmplitude()/51805.5336

知道麦克风的压力,我们可以使用众所周知的公式计算 dB(SPL) 值

X = 20 log_10 (p/p0)

这仍然会给出一个很高的值,因为在计算中只使用了最大幅度。要解决这个问题,一定不要使用 getMaxAmplitude() ,虽然这稍微超出了这个问题的重点,但无论如何我都会把代码放进去,希望它会有所帮助

public class NoiseRecorder 
{

private final String TAG = SoundOfTheCityConstants.TAG;
public static double REFERENCE = 0.00002;

public double getNoiseLevel() throws NoValidNoiseLevelException
{
    Logging.e(TAG, "start new recording process");
    int bufferSize = AudioRecord.getMinBufferSize(44100,AudioFormat.CHANNEL_IN_DEFAULT,AudioFormat.ENCODING_PCM_16BIT);
    //making the buffer bigger....
    bufferSize=bufferSize*4;
    AudioRecord recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
            44100, AudioFormat.CHANNEL_IN_DEFAULT, AudioFormat.ENCODING_PCM_16BIT, bufferSize);

    short data [] = new short[bufferSize];
    double average = 0.0;
    recorder.startRecording();
    //recording data;
    recorder.read(data, 0, bufferSize);

    recorder.stop();
    Logging.e(TAG, "stop");
    for (short s : data)
    {
        if(s>0)
        {
            average += Math.abs(s);
        }
        else
        {
            bufferSize--;
        }
    }
    //x=max;
    double x = average/bufferSize;
    Logging.e(TAG, ""+x);
    recorder.release();
    Logging.d(TAG, "getNoiseLevel() ");
    double db=0;
    if (x==0){
        NoValidNoiseLevelException e = new NoValidNoiseLevelException(x);
        throw e;
    }
    // calculating the pascal pressure based on the idea that the max amplitude (between 0 and 32767) is 
    // relative to the pressure
    double pressure = x/51805.5336; //the value 51805.5336 can be derived from asuming that x=32767=0.6325 Pa and x=1 = 0.00002 Pa (the reference value)
    Logging.d(TAG, "x="+pressure +" Pa");
    db = (20 * Math.log10(pressure/REFERENCE));
    Logging.d(TAG, "db="+db);
    if(db>0)
    {
        return db;
    }
    NoValidNoiseLevelException e = new NoValidNoiseLevelException(x);
    throw e;
}
}

这些值现在来自 4 秒样本中所有幅度的平均值,因此更准确。之后完成上述计算。这将给出更现实的分贝值。请注意,手机麦克风仍然很糟糕,而且该算法不会产生实际的 dB(SPL),而只会产生比之前更好的近似值。

要获得某些应用程序的性能,还需要做更多工作。大多数这些应用程序都使用滑动窗口,这意味着持续录制并滑动 x 秒的窗口以持续评估声音级别。另外我会评估一下什么 db Value 最适合用作最大值,现在它是 90 dB(SPL)/0.6325 Pa,这只是一个合理的猜测,它可能会略高于这个值。

我会尽快更新信息。

【讨论】:

  • 非常感谢 - 这很有帮助
  • 您知道在调用recorder.read(data, 0, bufferSize); 方法时,记录器是否正在获取幅度并将它们存储在短数据数组中?我不完全确定我是否理解该方法的作用。
  • 这些是存储的麦克风的采样值。这些将(就像麦克风上的压力一样)在正值和负值之间振荡。因此,要获得幅度,您必须使用某种中值或加权计算..
  • 那么这些“采样值”是什么?它们的测量单位是什么? (即存储在数据数组中的内容)-再次感谢,我真的很感激
  • 当来自声波的气压撞击麦克风时,压力会转化为电流。正常气压等于值 0。由于声音是一种波,因此电流会低于和高于零,具体取决于振荡。该电流以每秒 44.100 次采样。这些值被翻译成短值并存储在这个数组中(这是量化的结果)
猜你喜欢
  • 2011-09-29
  • 2019-10-25
  • 2011-12-20
  • 1970-01-01
  • 1970-01-01
  • 2015-07-03
  • 2011-12-12
  • 2013-06-02
  • 2020-11-21
相关资源
最近更新 更多