AudioRecord类究竟是如何工作的?

时间:2013-08-23 02:57:30

标签: android audio wav

请参阅我的其他问题因为我认为它们是相关的:
Question 1
Question 2
Question 3
这是我正在使用的代码,当我按下一个按钮时,它会将麦克风上获得的音频信号传递给扬声器:

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");
        }
        // */
    }  

添加了文件appState.txtappStateTemp.txt以保存在应用上次失去焦点时是否正在执行传递,但这可能不是很重要。我想知道的是:

  1. 在未调用record.read()的情况下调用record.startrecording()时会发生什么?

  2. SIZE_OF_RECORD_ARRAY有什么意义?我认为它至少应该是AudioRecord.getMinBufferSize()返回的值,但是在这个程序中,即使我将它设置为1,它也不会影响输出。

  3. 如果我使用16位PCM编码,我至少需要一个短变量来存储音频样本的数字等效值。但是在这段代码中,即使我将lin变量从短数组更改为字节数组,输出也没有明显的变化。那么read函数如何将数字样本存储在数组中呢?它是否为每个样本自动分配2个字节的元素?如果是这种情况,它是以小端还是大端进行的?

2 个答案:

答案 0 :(得分:2)

问题1和3应该很容易让您查看您的应用程序,但这里是:

  

1:调用record.read()而不调用record.startrecording()会发生什么?

我希望底层音频输入流没有数据流,因此read()返回0或可能是错误代码,表示没有数据被读取。


  

2:SIZE_OF_RECORD_ARRAY有什么意义?我认为它至少应该是AudioRecord.getMinBufferSize()返回的值,但是在这个程序中,即使我将它设置为1,它也不会影响输出。

getMinBufferSize构造函数的调用中指定缓冲区大小时,AudioRecord的值很重要。您使用SIZE_OF_RECORD_ARRAY更改的内容只是每次调用read()时您正在阅读的数据量 - 而且每次调用read()不是特别好的主意字节(由于所有这些函数调用的开销),我可以想象它仍然可以工作。


  

3:如果我使用16位PCM编码,我至少需要一个短变量来存储音频样本的数字等效值。但是在这段代码中,即使我将lin变量从短数组更改为字节数组,输出也没有明显的变化。那么read函数如何将数字样本存储在数组中呢?它是否为每个样本自动分配2个字节的元素?如果是这种情况,它是以小端还是大端进行的?

底层本机代码始终使用byte版本。 short版本只是byte版本的包装器。所以是的,在这种情况下,每个样本都会使用一对字节 至于字节序;对于绝大多数Android设备来说,它将是小端的。

答案 1 :(得分:0)

试试这个我希望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";
    }