user13267
user13267

Reputation: 7193

How exactly does the AudioRecord class work?

Please see my other questions as well because I think they are related:
Question 1
Question 2
Question 3
This is the code I am using which performs a pass through of the audio signals obtained at the mic to the speaker, when I press a buton:

public class MainActivity extends Activity {
    AudioManager am = null;
    AudioRecord record =null;
    AudioTrack track =null;
    final int SAMPLE_FREQUENCY = 44100;
    final int SIZE_OF_RECORD_ARRAY = 1024;  // 1024 ORIGINAL
    final int WAV_SAMPLE_MULTIPLICATION_FACTOR = 1;
    int i= 0;
    boolean isPlaying = false;
    private volatile boolean keepThreadRunning;
    private RandomAccessFile stateFile, stateFileTemp;
    private File delFile, renFile;
    String stateFileLoc = Environment.getExternalStorageDirectory().getPath(); 
    class MyThread extends Thread{
        private volatile boolean needsToPassThrough;
        // /*
        MyThread(){
            super();
        }

        MyThread(boolean newPTV){
            this.needsToPassThrough = newPTV;
        }
        // */

        // /*
        @Override
        public void run(){
            // short[] lin = new short[SIZE_OF_RECORD_ARRAY];
            byte[] lin = new byte[SIZE_OF_RECORD_ARRAY];
            int num = 0;
            // /*
            if(needsToPassThrough){
                record.startRecording();
                track.play();
            }
            // */
            while (keepThreadRunning) {
            // while (!isInterrupted()) {
                num = record.read(lin, 0, SIZE_OF_RECORD_ARRAY);
                for(i=0;i<lin.length;i++)
                    lin[i] *= WAV_SAMPLE_MULTIPLICATION_FACTOR; 
                track.write(lin, 0, num);
            }
            // /*
            record.stop();
            track.stop();
            record.release();
            track.release();
            // */
        }
        // */

        // /*
        public void stopThread(){
            keepThreadRunning = false;
        }
        // */
    }

    MyThread newThread;

    private void init() {
        int min = AudioRecord.getMinBufferSize(SAMPLE_FREQUENCY, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
        record = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, SAMPLE_FREQUENCY, AudioFormat.CHANNEL_IN_MONO,
                                 AudioFormat.ENCODING_PCM_16BIT, min);
        int maxJitter = AudioTrack.getMinBufferSize(SAMPLE_FREQUENCY, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
        track = new AudioTrack(AudioManager.MODE_IN_COMMUNICATION, SAMPLE_FREQUENCY, AudioFormat.CHANNEL_OUT_MONO,
                               AudioFormat.ENCODING_PCM_16BIT, maxJitter, AudioTrack.MODE_STREAM);
        am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        am.setMode(AudioManager.MODE_IN_COMMUNICATION);

        try {
            stateFile = new RandomAccessFile(stateFileLoc+"/appState.txt", "rwd");
            stateFileTemp = new RandomAccessFile(stateFileLoc+"/appStateTemp.txt", "rwd");
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        delFile = new File(stateFileLoc+"/appState.txt");
        renFile = new File(stateFileLoc+"/appStateTemp.txt");

    }

    @Override
    protected void onResume(){
        super.onResume();
        // newThread.stopThread();
        Log.d("MYLOG", "onResume() called");
        init();
        keepThreadRunning = true;
        try {
            if(stateFile.readInt() == 1){
                isPlaying = true;
                Log.d("MYLOG", "readInt == 1");
            }
            else{
                isPlaying = false;
                Log.d("MYLOG", "readInt <> 1");
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // */


        // newThread = new MyThread(true);
        newThread = new MyThread(isPlaying);
        newThread.start();

    }

    @Override
    protected void onPause(){
        super.onPause();
        Log.d("MYLOG", "onPause() called");
        newThread.stopThread();
        // android.os.Process.killProcess(android.os.Process.myPid());
        try {
            if(isPlaying)
                stateFileTemp.writeInt(1);
            else
                stateFileTemp.writeInt(0);

            delFile.delete();

            renFile.renameTo(delFile);

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setVolumeControlStream(AudioManager.MODE_IN_COMMUNICATION);
        Log.d("MYLOG","onCreate() called");
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        newThread.stopThread();
        // android.os.Process.killProcess(android.os.Process.myPid());
        // killProcess(android.os.Process.myPid());
        // newThread.interrupt();
        delFile.delete();
         Log.d("MYLOG", "onDestroy() called");
    }

    public void passStop(View view){
        Button playBtn = (Button) findViewById(R.id.button1);  
        // /*
        if(!isPlaying){
            record.startRecording();
            track.play();
            isPlaying = true;
            playBtn.setText("Pause");
        }
        else{
           record.stop();
           track.pause();
           isPlaying=false;
           playBtn.setText("Pass through");
        }
        // */
    }  

the files appState.txt and appStateTemp.txt were added to save whether pass through was being performed when the app last lost focus, but that is probably not very significant here. What I want to know is:

  1. What happens when record.read() is called without calling record.startrecording() ?

  2. What is the significance of SIZE_OF_RECORD_ARRAY? I thought it should be at least the value returned by AudioRecord.getMinBufferSize() but in this program it doesn't affect the output at all even if I set it to 1.

  3. If I use 16 bit PCM encoding I need at least a short variable to store the digital equivalent of the audio samples. However in this code even if I change the lin variable from short array to byte array, there is no apparent change in the output. So how does the read function store the digital samples in the array? Does it automatically allocate 2 byte elements for each sample? If that is the case, does it do it as little endian or big endian?

Upvotes: 0

Views: 1552

Answers (2)

Michael
Michael

Reputation: 58427

Question 1 and 3 should be easy for you to check with your app, but here goes:

1: What happens when record.read() is called without calling record.startrecording() ?

I would expect there to be no flow of data from the underlying audio input stream, and that read() therefore returns 0 or possibly an error code, indicating that no data has been read.


2: What is the significance of SIZE_OF_RECORD_ARRAY? I thought it should be at least the value returned by AudioRecord.getMinBufferSize() but in this program it doesn't affect the output at all even if I set it to 1.

The value of getMinBufferSize is important when you specify the buffer size in the call to the AudioRecord constructor. What you're changing with SIZE_OF_RECORD_ARRAY is just the amount of data you're reading with each call to read() - and while it isn't a particularly good idea to call read() once per byte (because of the overhead of all those function calls), I can imagine that it still will work.


3: If I use 16 bit PCM encoding I need at least a short variable to store the digital equivalent of the audio samples. However in this code even if I change the lin variable from short array to byte array, there is no apparent change in the output. So how does the read function store the digital samples in the array? Does it automatically allocate 2 byte elements for each sample? If that is the case, does it do it as little endian or big endian?

The underlying native code always uses the byte version. The short version is simply a wrapper around the byte version. So yes, a pair of bytes will be used for each sample in this case.
As for the endianness; it would be little-endian on the vast majority of Android devices out there.

Upvotes: 2

Biraj Zalavadia
Biraj Zalavadia

Reputation: 28484

Try this I hope will work 100%

  MediaRecorder mRecorder = null;
    String mFileName;
private void startRecording() {

        try {
            mRecorder = new MediaRecorder();
            mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
            mFileName = getRecordDefaultFileName();
            mRecorder.setOutputFile(mFileName);
            mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

            try {
                mRecorder.prepare();
            } catch (IOException e) {
                System.out.println("prepare() failed");
            }

            mRecorder.start();
        } catch (Exception e) {

            return;
        }

    }

private void stopRecording() {
        try {
            if (mRecorder != null) {
                mRecorder.stop();
                mRecorder.release();
                mRecorder = null;
            }
        } catch (Exception e) {
        }
    }

private String getRecordDefaultFileName() {

        File wallpaperDirectory = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + "recordingFolder" + "/");
        if (!wallpaperDirectory.exists()) {
            wallpaperDirectory.mkdirs();
        }

        return wallpaperDirectory.getAbsolutePath() + File.separator + "iarecord" + ".3gp";
    }

Upvotes: 0

Related Questions