Android MediaRecorder有一个函数
.getMaxAmplitude();,正如API告诉我的那样,“返回自上次调用此方法以来采样的最大绝对幅度。 “但我找不到这是什么幅度的?它是帕斯卡还是瓦特?
我在网上的几个页面上发现,您可以使用(如建议的here)计算与分贝密切相关的值。
double db = (20 * Math.log10(amplitude / REFERENCE));
这会让我假设返回的值是某种线性范围(可能类似于milipascal ......)
REFERENCE = 0.1(我知道这应该是2 * 10 ^( - 5)Pascal((20 uPascal)),但这会返回奇怪的值... 0.1奇怪地效果更好。)
现在我使用
getMaxAmplitude()测量MaxAmplitude()并将其放入变量幅度。
这是方法:
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根本不会反映出什么...
答案 0 :(得分:19)
我可以找到这个问题的答案,我将在这里分享给任何关心的人:MediaRecorder.getMaxAmplitude()函数返回无符号的16位整数值(0-32767)。这可能只是CD质量样本值的abs(),范围从-32768到32767.这意味着它们可能表示从麦克风构建的0-100%最大电压范围的电输出的16位数字化进入那部手机。因为即使在一个品牌的移动设备中,这些麦克风有时会在其精确范围内变化,甚至类似的电话也不一定会返回与同一声源相同距离的相同值。
然而,这个值与Pascal中的声压相关,因为它也是一个线性量化的溶液压力,在可以用给定麦克风测量声音的区域(由于手机的限制,它不会覆盖整个扫描范围) )。
答案 1 :(得分:16)
对此进行了更多工作。使用经过校准的SPL-Meters和具有不同纯频率,白噪声和粉红噪声的智能手机进行的一些测试我现在知道手机麦克风不能用于任何应该在90到100 dB(SPL)以上注册的东西,具体取决于手机
假设90 dB(SPL)是最大的,可以计算出这对应于麦克风上的0.6325 Pa的压力。现在假设p0 = 0.0002 Pa是参考最小值,并且假设这将从getMaxAmplitude()注册为0(这实际上永远不会发生),我们可以使用麦克风处的最大压力来核心来自getMaxAmplitude()函数的值。这意味着来自getMaxAmplitude()的16375的结果将对应于0.3165 Pa的最大压力。这当然不是很科学,因为最大值和最小值是纯粹的对比,但它给了我们一个起点。我们现在可以用
计算pP = 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值最适合用作最大值,现在它是90 dB(SPL)/0.6325 Pa这只是一个合理的猜测,它可能会略高于此值。
只要我有更多信息,我就会更新信息。