如何在Android上对麦克风进行采样而不需要录制以获得实时的振幅/电平?

24
我尝试获取Android上麦克风的振幅级别,像这样:
MediaRecorder recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);

Timer timer = new Timer();
timer.scheduleAtFixedRate(new RecorderTask(recorder), 0, 1000);

private class RecorderTask extends TimerTask {
    private MediaRecorder recorder;

    public RecorderTask(MediaRecorder recorder) {
        this.recorder = recorder;
    }

    public void run() {
        Log.v("MicInfoService", "amplitude: " + recorder.getMaxAmplitude());
    }
}

不幸的是,这总是返回 0。

看起来我必须实际开始录制才能使其工作。是这样吗?

如果是这样,我需要录制 500 毫秒,获取振幅,停止录制并重复吗?

最后,我必须记录到文件吗?我不需要保存此音频文件,难道我不能仅从当前实时麦克风输入的最高振幅或最近一次调用的当前振幅中获取吗?

感谢任何帮助。

5个回答

24
Toumal提供的解决方案可行,但刷新率不够高,所以我最终使用了Toumal链接的SoundMeter.java类,但修改了它以使用此answer中的代码。
这是我使用的代码,提供了更好的刷新率:
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;

public class SoundMeter {

    private AudioRecord ar = null;
    private int minSize;

    public void start() {
        minSize= AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
        ar = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000,AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,minSize);
        ar.startRecording();
    }

    public void stop() {
        if (ar != null) {
            ar.stop();
        }
    }

    public double getAmplitude() {
        short[] buffer = new short[minSize];
        ar.read(buffer, 0, minSize);
        int max = 0;
        for (short s : buffer)
        {
            if (Math.abs(s) > max)
            {
                max = Math.abs(s);
            }
        }
        return max;
    }

}

1
我看到这两个代码之间唯一的区别是AudioFormat.CHANNEL_IN_MONO而不是AudioFormat.CHANNEL_CONFIGURATION_MONO。这是什么让刷新率更好的原因吗? - YoussefDir
@YoussefDir 我不知道,自从7年前回答这个问题以来我就没有再看过它。 - Benjamin Kaiser

8

2
显然可以使用AudioRecord,无需记录文件。 - Tom
2
你可以将音频/媒体输出重定向到/dev/null,以此防止任何实际录制发生。同时确保使用 recorder.prepare(),否则它将无法正常工作。 - kpax

2
您可以使用MediaRecorder类,要在UI上显示实时数据,需要使用Handler:
public class SoundMeter {
private MediaRecorder mediaRecorder;
public  void start(){
    if(started){
        return;
    }
    if (mediaRecorder == null){
        mediaRecorder = new MediaRecorder();

        mediaRecorder.setAudioSource(
                MediaRecorder.AudioSource.MIC);
        mediaRecorder.setOutputFormat(
                MediaRecorder.OutputFormat.THREE_GPP);
        mediaRecorder.setAudioEncoder(
                MediaRecorder.AudioEncoder.AMR_NB);
        mediaRecorder.setOutputFile("/dev/null");

        try{
            mediaRecorder.prepare();
        }catch (IllegalStateException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
        mediaRecorder.start();
        started = true;
    }
}

}
public double getAmplitude(){
    return  mediaRecorder.getMaxAmplitude();
}
}

这部分在UI上显示数据:

    private Runnable pollTask = new Runnable() {
    @Override
    public void run() {
        double amplitude = soundMeter.getAmplitude();
        amplitudeTextView.setText("Amplitude: " + amplitude);

        handler.postDelayed(pollTask, 500);
    }
};

不要忘记在onCreate方法中调用处理程序:
handler.postDelayed(pollTask, 500);

500是以毫秒为单位的延迟,UI将在此期间更新

正如您在这里看到的,如果您将输出目标设置为以下内容,则无需将输出保存到文件中,它不会保存在任何地方:

mediaRecorder.setOutputFile("/dev/null");

2

1

基于 Benjamin 的答案,但更新为 Kotlin:

class MicChecker @Inject constructor(@ApplicationContext val context: Context) {
    private var audioRecord: AudioRecord? = null
    private var minSize = 0
    val level: Int
        get() {
            val buffer = ShortArray(minSize)
            audioRecord?.read(buffer, 0, minSize)
            return buffer.max().toInt()
        }

    fun startCheck() {
        minSize = AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT)
        if (ActivityCompat.checkSelfPermission(context, RECORD_AUDIO) != PERMISSION_GRANTED) {
            Timber.d("==> record audio permission not granted")
            return
        }
        audioRecord = AudioRecord(
            MediaRecorder.AudioSource.MIC,
            8000,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT,
            minSize
        )
        audioRecord?.startRecording()
    }

    fun stopCheck() {
        audioRecord?.stop()
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接