You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android设备麦克风输入ADC的RMS值无文件测量方案咨询

方案推荐:使用Android原生AudioRecord类

你的需求完全可以通过Android原生的AudioRecord类实现,无需依赖Oboe库——Oboe更适合对延迟要求极致(如亚10ms)的场景,而100-200ms的测量周期用原生API足够简单高效。

核心步骤说明

1. 申请麦克风权限

麦克风属于危险权限,需在AndroidManifest.xml中声明静态权限,同时在代码中动态申请(Android 6.0+):

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-feature android:name="android.hardware.microphone" android:required="true" />

2. 配置AudioRecord参数

选择适合的音频参数,以16位单声道PCM为例(计算RMS最方便):

  • 采样率:44100Hz(通用兼容值)
  • 声道:CHANNEL_IN_MONO(单声道减少计算量)
  • 格式:ENCODING_PCM_16BIT(16位线性PCM,直接映射ADC采样值)

通过AudioRecord.getMinBufferSize()获取系统要求的最小缓冲区大小,避免初始化失败。

3. 实现RMS测量逻辑

启动后台线程循环读取PCM数据,按100-200ms的周期计算RMS:

  • 将读取到的字节数组转换为短数组(16位PCM每个采样占2字节)
  • 计算周期内所有采样值的平方和,再求平均值的平方根(即RMS)

Kotlin代码示例

import android.Manifest
import android.content.pm.PackageManager
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlin.math.sqrt

class MainActivity : AppCompatActivity() {
    private val RECORD_AUDIO_PERMISSION_REQUEST = 1001
    private lateinit var audioRecord: AudioRecord
    private var isRecording = false

    // 配置参数
    private val sampleRate = 44100
    private val channelConfig = AudioFormat.CHANNEL_IN_MONO
    private val audioFormat = AudioFormat.ENCODING_PCM_16BIT
    // 测量周期:150ms(可在100-200ms之间调整)
    private val measurementPeriodMs = 150
    // 每个周期的采样数 = 采样率 * 周期(秒)
    private val samplesPerPeriod = (sampleRate * measurementPeriodMs / 1000).toInt()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO), RECORD_AUDIO_PERMISSION_REQUEST)
            return
        }
        initAudioRecord()
        startRecording()
    }

    private fun initAudioRecord() {
        val minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)
        // 确保缓冲区大小至少能容纳一个周期的采样数据
        val bufferSize = maxOf(minBufferSize, samplesPerPeriod * 2) // 16位PCM每个采样2字节

        audioRecord = AudioRecord(
            MediaRecorder.AudioSource.MIC,
            sampleRate,
            channelConfig,
            audioFormat,
            bufferSize
        )
    }

    private fun startRecording() {
        isRecording = true
        audioRecord.startRecording()

        // 用协程在后台线程处理音频数据
        CoroutineScope(Dispatchers.IO).launch {
            val buffer = ShortArray(samplesPerPeriod) // 存储一个周期的采样数据

            while (isRecording) {
                val readCount = audioRecord.read(buffer, 0, samplesPerPeriod)
                if (readCount > 0) {
                    val rms = calculateRms(buffer, readCount)
                    // 在这里处理RMS值,比如更新UI(需切换到主线程)
                    runOnUiThread {
                        // 示例:打印RMS值
                        println("当前RMS值:$rms")
                    }
                }
            }
            audioRecord.stop()
            audioRecord.release()
        }
    }

    // 计算RMS值:sqrt(Σ(x²)/n)
    private fun calculateRms(samples: ShortArray, count: Int): Double {
        var sum = 0.0
        for (i in 0 until count) {
            val sample = samples[i].toDouble()
            sum += sample * sample
        }
        val mean = sum / count
        return sqrt(mean)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == RECORD_AUDIO_PERMISSION_REQUEST) {
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                initAudioRecord()
                startRecording()
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        isRecording = false
    }
}

关键注意事项

  • 权限处理:必须确保用户授予录音权限,否则AudioRecord无法初始化
  • 线程安全:音频数据读取必须在后台线程进行,避免阻塞UI线程
  • 资源释放:在Activity销毁时停止录音并释放AudioRecord资源,防止内存泄漏
  • RMS计算:直接使用ADC采样的原始PCM值计算,不会引入噪声过滤逻辑,完全符合你“获取ADC的RMS值而非噪声水平”的需求

内容的提问来源于stack exchange,提问作者Ivan Vashchenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 04:10:29