简体   繁体   English

Android中的实时音频处理

[英]Real-time audio processing in Android

I'm trying to figure out how to write an app that can decode audio morse code on the fly. 我试图弄清楚如何编写一个可以即时解码音频摩尔斯电码的应用程序。 I found this document which explains how to record audio from the microphone in Android. 我找到了这份文档该文档解释了如何在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. 我想知道的是,是否可以从麦克风访问原始输入,或者是否必须将其写入/读取到文件中。

Thanks. 谢谢。

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. 是的,您想做的应该是可能的。

there is a sensing framework from MIT media labs called funf: http://code.google.com/p/funf-open-sensing-framework/ 麻省理工学院媒体实验室有一个名为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. 他们已经创建了用于音频输入和一些分析(FFT等)的类,据我所知还实现了保存到文件或上传,并且它们处理了电话上可用的大多数传感器。 You can also get inspired from the code they wrote, which I think is pretty good. 您也可以从他们编写的代码中获得启发,我认为这很好。

Using AudioRecord is overkill. 使用AudioRecord太过分了。 Just check MediaRecorder.getMaxAmplitude() every 1000 milliseconds for loud noises versus silence. 只需每1000毫秒检查一次MediaRecorder.getMaxAmplitude()即可获得较大的噪音与静音。

If you really need to analyze the waveform, then yes you need AudioRecord. 如果您确实需要分析波形,那么是的,您需要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. 获取原始数据,并计算一些与您关心的原始字节部分的均方根类似的东西,以了解音量。

But, why do all that when MediaRecorder.getMaxAmplitude() is so much easier to use. 但是,为什么在MediaRecorder.getMaxAmplitude()更易于使用时做所有这些事情。

see my code from this answer: this question 从这个答案中看到我的代码: 这个问题

I have found a way how to do it. 我找到了一种方法。 Basically you need to run a new thread within which you continuously call myAndroidRecord.read() . 基本上,您需要运行一个新线程,在其中继续调用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 下面是Main活动的代码示例

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); 有关UI更新的代码,例如TextView raw_value = findViewById(R.id.sensor_value); is specific to this example and you should define your own. 特定于此示例,您应该定义自己的示例。

Lines int ii = noAllRead; int ii = noAllRead;int ii = noAllRead; and int val = buffer[i]; int val = buffer[i]; are necesary because Java doesent let you put non effectively final variables in lambda methods. 是必需的,因为Java确实允许您将非最终变量有效地放入lambda方法中。

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. 以我的经验,已经为Android构建了音频合成器,很难实现实时性能并保持音频保真度。 A Morse Code 'translator' is certainly doable though, and sounds like a fun little project. 摩尔斯电码的“翻译器”当然是可行的,听起来像是一个有趣的小项目。 Good Luck! 祝好运!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM