【问题标题】:Real-time audio processing in AndroidAndroid中的实时音频处理
【发布时间】:2011-01-16 10:58:10
【问题描述】:

我正试图弄清楚如何编写一个可以即时解码音频莫尔斯电码的应用程序。我找到了this document,它解释了如何在 Android 中从麦克风录制音频。我想知道的是是否可以从麦克风访问原始输入,或者是否必须将其写入/读取到文件中。

谢谢。

【问题讨论】:

  • 你做过保罗这个项目吗?我正在研究类似于“本垒打”项目的东西,并且对即时处理传入的音频也很感兴趣......也许需要使用本机库来获得足够的性能?如果你想在 mackenzie-serres.net 上的 andrew 给我发电子邮件。谢谢!

标签: android audio real-time


【解决方案1】:

如果您使用MediaRecorder(上面的示例),它会将压缩音频保存到文件中。

如果使用AudioRecord,可以直接获取音频样本。

是的,你想做的应该是可能的。

【讨论】:

【解决方案2】:

使用 AudioRecord 太过分了。只需每 1000 毫秒检查一次 MediaRecorder.getMaxAmplitude() 以了解响亮的噪音还是静音。

如果您真的需要分析波形,那么是的,您需要 AudioRecord。获取原始数据并计算诸如您关心的原始字节部分的均方根之类的东西,以了解音量。

但是,当 MediaRecorder.getMaxAmplitude() 更容易使用时,为什么还要这样做。

从这个答案中查看我的代码:this question

【讨论】:

  • 1000 ms = 1 s,这似乎不足以解析摩尔斯电码。
  • 但是你不能使用 getMaxAmplitude() (你可以,但你总是会得到 0),除非你真的开始录制。因此,您仍然需要记录一个文件,该文件可能会变得无限大。绝对不是解决方案。
  • 你能帮我解决stackoverflow.com/questions/61184352/…的问题
【解决方案3】:

麻省理工学院媒体实验室有一个名为 funf 的传感框架:http://code.google.com/p/funf-open-sensing-framework/
据我所知,他们已经为音频输入和一些分析(FFT 等)创建了类,还实现了保存到文件或上传,并且他们处理了手机上可用的大多数传感器。 你也可以从他们写的代码中得到启发,我觉得挺好的。

【讨论】:

    【解决方案4】:

    我已经找到了一种方法。基本上,您需要运行一个新线程,在其中不断调用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 方法中放置非有效的最终变量。

    【讨论】:

      【解决方案5】:

      看起来它必须先转储到文件中。

      如果你偷看android.media.AudioRecord source, 本机音频数据字节缓冲区不向公共 API 公开。

      根据我的经验,为 Android 构建了音频合成器,很难实现实时性能并保持音频保真度。莫尔斯电码“翻译器”当然是可行的,而且听起来像是一个有趣的小项目。祝你好运!

      【讨论】:

      • 为什么您认为音频缓冲区没有传递给 Java?那么 read() 方法呢?
      • @Error454 您能否举一个不转储文件直接读取值的示例?我目前正在努力解决同样的问题。谢谢!
      • @Amuoeba 您将使用 AudioRecord 并定期调用 read(...) 来获取原始音频数据。
      • @Error454 是的,我在阅读您的回复之前已经弄清楚了。可惜我以前没看过,会节省我不少时间。也为其他人发布了带有示例代码的答案。谢谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-17
      • 2015-12-09
      • 2015-07-18
      • 2017-08-02
      相关资源
      最近更新 更多