web-dev-qa-db-fra.com

Traitement audio en temps réel dans Android

J'essaie de comprendre comment écrire une application qui peut décoder du code audio morse à la volée. J'ai trouvé ce document qui explique comment enregistrer de l'audio à partir du microphone dans Android. Ce que j'aimerais savoir, c'est s'il est possible d'accéder à l'entrée brute du microphone ou si elle doit être écrite/lue dans un fichier.

Merci.

34
Jeremy Logan

Si vous utilisez MediaRecorder (l'exemple ci-dessus), il enregistrera l'audio compressé dans un fichier.

Si vous utilisez AudioRecord , vous pouvez obtenir des échantillons audio directement.

Oui, ce que vous voulez faire devrait être possible.

24
dmazzoni

il existe un cadre de détection de MIT laboratoires de médias appelés funf: http://code.google.com/p/funf-open-sensing-framework/
Ils ont déjà créé des classes pour l'entrée audio et certaines analyses (FFT et similaires), également l'enregistrement dans des fichiers ou le téléchargement est mis en œuvre pour autant que je l'ai vu, et ils gèrent la plupart des capteurs disponibles sur le téléphone. Vous pouvez également vous inspirer du code qu'ils ont écrit, ce qui, je pense, est plutôt bon.

6
Andrei

L'utilisation d'AudioRecord est exagérée. Vérifiez simplement MediaRecorder.getMaxAmplitude () toutes les 1000 millisecondes pour les bruits forts par rapport au silence.

Si vous avez vraiment besoin d'analyser la forme d'onde, alors oui, vous avez besoin d'AudioRecord. Obtenez les données brutes et calculez quelque chose comme la moyenne racine au carré de la partie des octets bruts qui vous intéresse pour avoir une idée du volume.

Mais pourquoi faire tout cela lorsque MediaRecorder.getMaxAmplitude () est tellement plus facile à utiliser.

voir mon code de cette réponse: cette question

6
gregm

J'ai trouvé un moyen de le faire. Fondamentalement, vous devez exécuter un nouveau thread dans lequel vous appelez en permanence myAndroidRecord.read(). Après cette boucle d'appel sur toutes les entrées du tampon, et vous pouvez voir les valeurs brutes en temps réel une par une. Voici l'exemple de code de l'activité principale

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));
                });
            }

        }
    }
}

Ceci est juste une démonstration et dans l'application reall, vous devrez réfléchir un peu plus à la manière et au moment d'arrêter le thread en cours d'exécution. Cet exemple s'exécute indéfiniment jusqu'à ce que vous quittiez l'application.

Le code concernant les mises à jour de l'interface utilisateur tel que TextView raw_value = findViewById(R.id.sensor_value); est spécifique à cet exemple et vous devez définir le vôtre.

Les lignes int ii = noAllRead; Et int val = buffer[i]; Sont nécessaires car Java ne permet pas de mettre des variables finales non efficaces dans les méthodes lambda.

0
Amuoeba