Обработка на входа на микрофона

Опитвам се да получа аудио данни от микрофона. Постигнах това, като използвах класа AudioRecord, който запълва буфер с тип shorts.

В крайна сметка бих искал да направя графика на този буфер, така че да получа дисплей като осцилоскоп (информация в реално време). Проблемът е, че ако искам да покажа стойност (да речем в текст), тогава имам нужда от различна нишка, за да актуализирам потребителския интерфейс. В момента правя това, като използвам AsyncTask и актуализирам потребителския интерфейс с AsyncTasks.publishProgress(). Досега не съм имал голям успех и бих искал да знам дали съм на прав път? Дръжките по-добър начин ли са? Има ли някой там, който е правил нещо подобно преди, и ако да, какъв метод е работил за вас? Също така, възможно ли е изобщо просто да се допитва микрофона?


Ето моят код. Той е предназначен да изведе всяка прочетена проба от MIC. Изглежда, че прави това с приемлива скорост, но понякога показва нула. Защо?

package com.ss.audioacquireapp3;

import android.app.Activity;
import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;


public class AudioAcquireApp3Activity extends Activity 
{
        //Properties (AsyncTask)
        protected TextView _percentField;
        protected InitTask _initTask;

        //Properties (MIC)
        public AudioRecord audioRecord; 
        public int mSamplesRead; //how many samples read 
        public int recordingState;
        public int buffersizebytes; 
        public int channelConfiguration = AudioFormat.CHANNEL_IN_MONO; 
        public int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; 
        public static short[] buffer; //+-32767 
        public static final int SAMPPERSEC = 44100; //samp per sec 8000, 11025, 22050 44100 or 48000

    @Override
    public void onCreate( Bundle savedInstanceState ) 
    {
        super.onCreate(savedInstanceState);
        setContentView( R.layout.main );

        _percentField = ( TextView ) findViewById( R.id.percent_field );

        buffersizebytes = AudioRecord.getMinBufferSize(SAMPPERSEC,channelConfiguration,audioEncoding); //4096 on ion 
        buffer = new short[buffersizebytes]; 
        audioRecord = new AudioRecord(android.media.MediaRecorder.AudioSource.MIC,SAMPPERSEC,channelConfiguration,audioEncoding,buffersizebytes); //constructor 

        _initTask = new InitTask();
        _initTask.execute( this );
    }

    /**
     * sub-class of AsyncTask
     */
    protected class InitTask extends AsyncTask<Context, Integer, String>
    {
        // -- run intensive processes here
        // -- notice that the datatype of the first param in the class definition matches the param passed to this method 
        // -- and that the datatype of the last param in the class definition matches the return type of this method
                @Override
                protected String doInBackground( Context... params ) 
                {
                        //-- on every iteration
                        //-- runs a while loop that causes the thread to sleep for 50 milliseconds 
                        //-- publishes the progress - calls the onProgressUpdate handler defined below
                        //-- and increments the counter variable i by one
                        //int i = 0;

                    audioRecord.startRecording();

                        while( true ) 
                        {
                                try{
                                        mSamplesRead = audioRecord.read(buffer, 0, buffersizebytes);

                                        int amp;

                                        for(int i = 0; i < buffersizebytes - 1; i++){
                                            amp = (int)buffer[i];
                                            publishProgress( amp );
                                        }

                                } catch( Exception e ){                        
                                }
                        }
                }

                // -- gets called just before thread begins
                @Override
                protected void onPreExecute() 
                {
                        //Log.i( "makemachine", "onPreExecute()" );
                        super.onPreExecute();

                }

                // -- called from the publish progress 
                // -- notice that the datatype of the second param gets passed to this method
                @Override
                protected void onProgressUpdate(Integer... values) 
                {
                        super.onProgressUpdate(values);
                        //Log.i( "makemachine", "onProgressUpdate(): " +  String.valueOf( values[0] ) );
                        _percentField.setText( String.valueOf(values[0]) );
                }

                // -- called as soon as doInBackground method completes
                // -- notice that the third param gets passed to this method
                @Override
                protected void onPostExecute( String result ) 
                {
                        super.onPostExecute(result);
                        //Log.i( "makemachine", "onPostExecute(): " + result );
                }   


     } 
}

И тук е main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center_vertical|center_horizontal"
    >

<TextView android:id="@+id/percent_field"
          android:layout_width="fill_parent" 
          android:layout_height="wrap_content"
          android:gravity="center_horizontal"/>

</LinearLayout>

Имайте предвид, че трябва да добавите това към AndroidManifest.xml

<uses-permission android:name="android.permission.RECORD_AUDIO" />

Използвам това на LG Optimus Black. Моля, помогнете ми да направя този код възможно най-ефективен.


person user893825    schedule 14.08.2011    source източник


Отговори (2)


  1. Ако трябва да актуализирате нишката на потребителския интерфейс от която и да е нишка, винаги можете да използвате Activity.runOnUiThread(Runnable action). Вижте javadoc на Activity клас за подробности.
  2. Не съм сигурен какво точно имате предвид под „просто проучване на микрофона“, но според мен AudioRecord е добър начин, когато записвате с микрофон. Ето връзка към примерна реализация, която чете честотата на записано аудио на живо: http://www.anddev.org/novice-tutorials-f8/get-frequency-data-from-microphone-in-real-time-t16774.html

Надявам се това да помогне.

person pkk    schedule 14.08.2011
comment
Благодаря, това много помага. Използвах връзката с кода и имам нещо, което сега работи. За съжаление работи много бавно. Може ли да помогнете с кода? - person user893825; 15.08.2011
comment
Е, трудно е да ви помогна, без всъщност да видя кода. Намерете частта от кода, който се изпълнява най-често (т.е. в AsyncTask.run()) и го оптимизирайте, например: премахнете ненужните регистрационни файлове за отстраняване на грешки, продължителни операции и т.н. - person pkk; 15.08.2011
comment
Публикувах кода си. Трябваше ли да го напиша тук, а не като отговор? Благодаря :) - person user893825; 24.08.2011

Това е късен отговор, но може би някой има нужда от отговор на същия въпрос. Ето връзка към Android Oscilloscope с отворен код (OsciPrime) (http://android.serverbox.ch/?p=268). Изходният код използва Thread вместо AsyncTask. Ако видите изходния код, можете да разберете как Thread обработва AudioRecord с Looper и Handler. Надявам се да е полезно и на други :)

person user689072    schedule 16.08.2013