Android 中的实时音频处理
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2257075/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Real-time audio processing in Android
提问by Jeremy Logan
I'm trying to figure out how to write an app that can decode audio morse code on the fly. I found this documentwhich explains how to record audio from the microphone in Android. What I'd like to know is whether it's possible to access the raw input from the microphone or whether it has to be written/read to a file.
我试图弄清楚如何编写一个可以即时解码音频莫尔斯电码的应用程序。我找到了这篇文档,它解释了如何在 Android 中从麦克风录制音频。我想知道的是是否可以从麦克风访问原始输入,或者是否必须将其写入/读取到文件中。
Thanks.
谢谢。
采纳答案by dmazzoni
If you use MediaRecorder(the example, above) it will save compressed audio to a file.
如果您使用MediaRecorder(上面的示例),它会将压缩的音频保存到文件中。
If you use AudioRecord, you can get audio samples directly.
如果使用AudioRecord,则可以直接获取音频样本。
Yes, what you want to do should be possible.
是的,你想做的应该是可能的。
回答by gregm
Using AudioRecord is overkill. Just check MediaRecorder.getMaxAmplitude() every 1000 milliseconds for loud noises versus silence.
使用 AudioRecord 是矫枉过正。只需每 1000 毫秒检查一次 MediaRecorder.getMaxAmplitude() 以了解噪音与静音。
If you really need to analyze the waveform, then yes you need AudioRecord. Get the raw data and calculate something like the root mean squared of the part of the raw bytes you are concerned with to get a sense of the volume.
如果您确实需要分析波形,那么是的,您需要 AudioRecord。获取原始数据并计算您所关心的原始字节部分的均方根之类的东西,以了解数量。
But, why do all that when MediaRecorder.getMaxAmplitude() is so much easier to use.
但是,当 MediaRecorder.getMaxAmplitude() 更容易使用时,为什么还要这样做呢?
see my code from this answer: this question
从这个答案看我的代码:这个问题
回答by Andrei
there is a sensing framework from MIT media labs called funf: http://code.google.com/p/funf-open-sensing-framework/
They already created classes for audio input and some analysis (FFT and the like), also saving to files or uploading is implemented as far as I've seen, and they handle most of the sensors available on the phone.
You can also get inspired from the code they wrote, which I think is pretty good.
麻省理工学院媒体实验室有一个名为 funf 的传感框架:http: //code.google.com/p/funf-open-sensing-framework/
他们已经为音频输入和一些分析(FFT 等)创建了类,也据我所知,保存到文件或上传是实现的,它们处理手机上可用的大多数传感器。您还可以从他们编写的代码中获得灵感,我认为这非常好。
回答by Amuoeba
I have found a way how to do it. Basically you need to run a new thread within which you continuously call myAndroidRecord.read()
. After this call loop over all the entries in the buffer, and you can see raw values in real time one by one. Below is the code sample of the Main activity
我找到了一种方法来做到这一点。基本上,您需要运行一个新线程,在其中不断调用myAndroidRecord.read()
. 在此调用循环缓冲区中的所有条目之后,您可以实时地一一查看原始值。下面是主要活动的代码示例
package com.example.mainproject;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.core.app.ActivityCompat;
import android.content.pm.PackageManager;
import android.Manifest;
import android.content.Context;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.widget.TextView;
import android.media.AudioManager;
import android.media.AudioFormat;
import android.os.Bundle;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
private AudioManager myAudioManager;
private static final int REQUEST_RECORD_AUDIO_PERMISSION = 200;
// Requesting permission to RECORD_AUDIO
private boolean permissionToRecordAccepted = false;
private String [] permissions = {Manifest.permission.RECORD_AUDIO};
private static final int PERMISSION_RECORD_AUDIO = 0;
Thread mThread;
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case REQUEST_RECORD_AUDIO_PERMISSION:
permissionToRecordAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
break;
}
if (!permissionToRecordAccepted ) finish();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(ContextCompat.checkSelfPermission(this,Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED){
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.RECORD_AUDIO)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
ActivityCompat.requestPermissions(this,
new String[] { Manifest.permission.RECORD_AUDIO },
PERMISSION_RECORD_AUDIO);
return;
} else {
// No explanation needed; request the permission
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.RECORD_AUDIO},
1);
ActivityCompat.requestPermissions(this,
new String[] { Manifest.permission.RECORD_AUDIO },
PERMISSION_RECORD_AUDIO);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}else{
myAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
String x = myAudioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED);
runOnUiThread(()->{
TextView tvAccXValue = findViewById(R.id.raw_available);
tvAccXValue.setText(x);
});
mThread = new Thread(new Runnable() {
@Override
public void run() {
record();
}
});
mThread.start();
}
}
private void record(){
int audioSource = MediaRecorder.AudioSource.MIC;
int samplingRate = 11025;
int channelConfig = AudioFormat.CHANNEL_IN_DEFAULT;
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
int bufferSize = AudioRecord.getMinBufferSize(samplingRate,channelConfig,audioFormat);
short[] buffer = new short[bufferSize/4];
AudioRecord myRecord = new AudioRecord(audioSource,samplingRate,channelConfig,audioFormat,bufferSize);
myRecord.startRecording();
int noAllRead = 0;
while(true){
int bufferResults = myRecord.read(buffer,0,bufferSize/4);
noAllRead += bufferResults;
int ii = noAllRead;
for (int i = 0;i<bufferResults;i++){
int val = buffer[i];
runOnUiThread(()->{
TextView raw_value = findViewById(R.id.sensor_value);
raw_value.setText(String.valueOf(val));
TextView no_read = findViewById(R.id.no_read_val);
no_read.setText(String.valueOf(ii));
});
}
}
}
}
This is just a demonstration and in reall app you will need to think a bit more about how and when to stop the running thread. This example just runs indefinitely untill you exit the app.
这只是一个演示,在真正的应用程序中,您需要更多地考虑如何以及何时停止正在运行的线程。这个例子会无限期地运行,直到你退出应用程序。
Code concerning the UI updates such as TextView raw_value = findViewById(R.id.sensor_value);
is specific to this example and you should define your own.
与 UI 更新相关的代码(例如TextView raw_value = findViewById(R.id.sensor_value);
特定于本示例),您应该定义自己的代码。
Lines int ii = noAllRead;
and int val = buffer[i];
are necesary because Java doesent let you put non effectively final variables in lambda methods.
行int ii = noAllRead;
和int val = buffer[i];
是必要的,因为 Java 不允许您在 lambda 方法中放置无效的最终变量。
回答by Dagwoody Djiboutay
It looks like it has to be dumped first to a file.
看起来它必须首先转储到文件中。
If you peek at the android.media.AudioRecord source, the native audio data byte buffers are not exposed to the public API.
如果您查看android.media.AudioRecord 源,则本机音频数据字节缓冲区不会暴露给公共 API。
In my experience, having built an audio synthesizer for Android, it's hard to achieve real-time performance and maintain audio fidelity. A Morse Code 'translator' is certainly doable though, and sounds like a fun little project. Good Luck!
根据我的经验,为 Android 构建了音频合成器后,很难实现实时性能并保持音频保真度。不过,摩尔斯电码“翻译器”当然是可行的,而且听起来像是一个有趣的小项目。祝你好运!