我正在尝试编写一个应用程序,可以实时解码音频莫尔斯电码。我找到了这份文档,其中介绍了如何在Android上从麦克风录制音频。我想知道的是是否可以访问麦克风的原始输入数据,还是必须将其写入/读取到文件中。
谢谢。
有一个来自MIT媒体实验室的感知框架叫做Funf:http://code.google.com/p/funf-open-sensing-framework/
他们已经创建了用于音频输入和一些分析(如FFT等)的类,文件保存或上传也已实现,而且他们处理了手机上可用的大多数传感器。
你还可以从他们编写的代码中获得灵感,我认为那非常好。
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));
});
}
}
}
}
这只是一个演示,在实际应用中,您需要更加深入地思考何时以及如何停止运行的线程。此示例会无限期地运行,直到您退出应用程序。
与UI更新相关的代码,例如TextView raw_value = findViewById(R.id.sensor_value);
,是特定于此示例的,您应该定义自己的代码。
行int ii = noAllRead;
和int val = buffer[i];
是必要的,因为Java不允许您在lambda方法中放置非有效最终变量。
看起来必须先将其转储到文件中。
如果你查看android.media.AudioRecord source, 原生音频数据字节缓冲区不会暴露给公共API。
根据我的经验,在为Android构建音频合成器时,很难实现实时性能并保持音频保真度。然而,摩尔斯电码的“翻译器”肯定是可行的,并且听起来是一个有趣的小项目。祝好运!