Android和线程中的现场音频录制和播放回调处理

时间:2012-02-23 13:27:26

标签: android audio-streaming audio-recording audiorecord

我想录制现场音频并播放它。就UI而言,应用程序只有三个按钮:一个用于开始录制和流式传输,一个用于播放预录制文件,最后一个用于停止当前录制文件任务(录音/播放)。为此,我分别使用 AudioRecord AudioTrack 类进行录制和播放。我的程序看起来像......

/ **  * @author amit  *  * /

public class AudioRecorder extends Activity {
    private String LOG_TAG = null;

    /* variables which are required to generate and manage the UI of the App */
    // private RecordButton mRecordButton = null;
    private Button recordBtn, stopBtn, playBtn;

    /*
     * variables which are required for the actual functioning of the recording
     * and playing
     */
    private AudioRecord recorder = null;
    private AudioTrack player = null;
    private AudioManager audioManager = null;
    private int recorderBufSize, recordingSampleRate;
    private int trackBufSize;
    private short[] audioData;
    private boolean isRecording = false, isPlaying = false;
    private Thread startRecThread;

    private AudioRecord.OnRecordPositionUpdateListener posUpdateListener;

    /**
     * constructor method for initializing the variables
     */
    public AudioRecorder() {
        super();
        LOG_TAG = "Constructor";
        recorderBufSize = recordingSampleRate = trackBufSize = 0;

        // init function will initialize all the necessary variables ...
        init();

        if (recorder != null && player != null) {
            Log.e(LOG_TAG, "recorder and player initialized");
            audioData = new short[recorderBufSize / 2]; // since we r reading shorts

        } else {
            Log.e(LOG_TAG, "Problem inside init function ");
        }
        posUpdateListener = new AudioRecord.OnRecordPositionUpdateListener() {
            int numShortsRead = 0;

            @Override
            public void onPeriodicNotification(AudioRecord rec) {
                // TODO Auto-generated method stub
//              String LOG_TAG = Thread.currentThread().getName();
//               Log.e(LOG_TAG, "inside position listener");
                audioData = new short[recorderBufSize / 2]; // divide by 2 since now we are reading shorts 
                numShortsRead = rec.read(audioData, 0, audioData.length);
                player.write(audioData, 0, numShortsRead);

            }

            @Override
            public void onMarkerReached(AudioRecord recorder) {
                // TODO Auto-generated method stub
                Log.e(LOG_TAG, "Marker Reached");
            }
        };
        // listener will be called every time 160 frames are reached
        recorder.setPositionNotificationPeriod(160);
        recorder.setRecordPositionUpdateListener(posUpdateListener);

        Log.e(LOG_TAG, "inside constructor");
    }

    private void init() {
        LOG_TAG = "initFunc";
        // int[] mSampleRates = new int[] { 8000, 11025, 22050, 44100 };
        short audioFormat = AudioFormat.ENCODING_PCM_16BIT;
        // for (int rate : mSampleRates) {
        this.recordingSampleRate = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);
        try {
            // Log.d(LOG_TAG, "Attempting rate " + rate + "Hz, bits: " +
            // audioFormat);
            int bufrSize = AudioRecord.getMinBufferSize(this.recordingSampleRate,
                    AudioFormat.CHANNEL_IN_MONO, audioFormat);

                // lets find out the minimum required size for AudioTrack
            int audioTrackBufSize = AudioTrack.getMinBufferSize(this.recordingSampleRate,
                    AudioFormat.CHANNEL_OUT_MONO, audioFormat);

            if (bufrSize != AudioRecord.ERROR_BAD_VALUE
                    && bufrSize != AudioRecord.ERROR) {
                // check if we can instantiate and have a success
                if(audioTrackBufSize >= bufrSize){
                    this.recorderBufSize = audioTrackBufSize;
                }else{
                    this.recorderBufSize = bufrSize;
                }

                AudioRecord rec = new AudioRecord(
                        MediaRecorder.AudioSource.DEFAULT, this.recordingSampleRate,
                        AudioFormat.CHANNEL_IN_MONO, audioFormat, this.recorderBufSize);

                if (rec != null
                        && rec.getState() == AudioRecord.STATE_INITIALIZED) {

                    // storing variables for future use . . .
//                  this.recordingSampleRate = rate;
//                  this.recorderBufSize = bufrSize;

                    Log.e(LOG_TAG,
                            "Returning..(rate:channelConfig:audioFormat:recorderBufSize)"
                                    + this.recordingSampleRate + ":" + AudioFormat.CHANNEL_IN_MONO
                                    + ":" + audioFormat + ":" + this.recorderBufSize);

                    // Now create an instance of the AudioTrack
//                  int audioTrackBufSize = AudioTrack.getMinBufferSize(rate,
//                          AudioFormat.CHANNEL_OUT_MONO, audioFormat);

                    Log.e(LOG_TAG, "Audio Record / Track / Final buf size :" + bufrSize + "/ " +audioTrackBufSize + "/ "+this.recorderBufSize);


                    this.player = new AudioTrack(AudioManager.STREAM_MUSIC,
                            this.recordingSampleRate, AudioFormat.CHANNEL_OUT_MONO, audioFormat,
                            this.recorderBufSize, AudioTrack.MODE_STREAM);

                    this.recorder = rec;
                    this.player.stop();
                    this.player.flush();
                    this.player.setPlaybackRate(this.recordingSampleRate);
                    return;
                }
            }
        } catch (IllegalArgumentException e) {
            Log.d(LOG_TAG, this.recordingSampleRate + "Exception, keep trying.", e);
        } catch (Exception e) {
            Log.e(LOG_TAG, this.recordingSampleRate + "Some Exception!!", e);
        }
        // for loop for channel config ended here. . . .
        // for loop for audioFormat ended here. . .
        // }// for loop for sampleRate
        return;
    }

    private void startPlaying() {
        LOG_TAG = "startPlaying";

        Log.e(LOG_TAG, "start Playing");
    }

    private void stopPlaying() {
        LOG_TAG = "stopPlaying";

        Log.e(LOG_TAG, "stop Playing");
    }

    private void startRecording() {
        LOG_TAG = "startRecording"; 

        /* start a separate recording thread from here . . . */
        startRecThread = new Thread() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                android.os.Process
                        .setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
//              String LOG_TAG = Thread.currentThread().getName();
                if(recorder.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING){
                    recorder.startRecording();
                }
//              Log.e(LOG_TAG, "running" +recorder.getRecordingState());
                while (recorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
                    recorder.read(audioData, 0, audioData.length);
                    try {

                         Thread.sleep(1000); // sleep for 2s
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        Log.e("run Method", "recorder thread is interrupted");
                        e.printStackTrace();
                    }
                }
            }
        };
        setVolumeControlStream(AudioManager.STREAM_MUSIC);
        audioManager.setSpeakerphoneOn(false);
        player.flush();     
        player.play();
        startRecThread.start();

        Log.e(LOG_TAG, "start Recording");
    }

    private void stopRecording() {
        LOG_TAG = "stopRecording";
        recorder.stop();

        if (startRecThread != null && startRecThread.isAlive()) {           
            startRecThread.destroy();
            startRecThread = null;
        }

        player.stop();
        player.flush();
        Log.e(LOG_TAG, "stop Recording");
    }

    private void stop() {
        if (isRecording) {
            isRecording = false;
            stopRecording();
        }
        if (isPlaying) {
            isPlaying = false;
            stopPlaying();
        }
        recordBtn.setEnabled(true);
        playBtn.setEnabled(true);
    }

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        LOG_TAG = "onCreate";
//      Log.e(LOG_TAG, "Create Called");
        // getting the audio service
        audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
        LinearLayout ll = new LinearLayout(this);

        // creating Buttons one by one . . . .
        // button to start the recording process
        recordBtn = new Button(this);
        recordBtn.setText("Record");
        recordBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                recordBtn.setEnabled(false);
                playBtn.setEnabled(false);
                isRecording = true;
                startRecording();
            }
        });
        // single button to stop recording and playing as applicable
        stopBtn = new Button(this);
        stopBtn.setText("Stop");
        stopBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                stop();
            }
        });
        // button to play the recorded sound
        playBtn = new Button(this);
        playBtn.setText("Play");
        playBtn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                // reverse the isPlaying
                isPlaying = true;
                recordBtn.setEnabled(false);
                playBtn.setEnabled(false);
                startPlaying();
            }
        });

        ll.addView(recordBtn, new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT, 1));

        ll.addView(playBtn, new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT, 1));

        ll.addView(stopBtn, new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT, 1));

        setContentView(ll);
    }

    @Override
    protected void onDestroy() {
        // Clean up code . ..
        super.onDestroy();
        if (recorder != null)
            recorder.release();
        if (startRecThread!=null && startRecThread.isAlive())
            startRecThread.destroy();
        if (recorder != null)
            recorder.release();
        if (player != null)
            player.release();
        startRecThread = null;
        recorder = null;
        player = null;
        recordBtn = null;
        stopBtn = null;
        playBtn = null;
        audioData = null;
        System.gc();
    }

}

正如您可能会看到startPlaying()stopPlaying()函数尚未实现,所以不要谈论它们。目前我只是想录制和播放。当我运行程序它播放录制的音频但音频出现在远处。另一个问题是应用程序的 UI线程挂起虽然我有一个单独的线程来读取音频。请帮帮....

2 个答案:

答案 0 :(得分:21)

如果您的要求是在录制时它应该播放(意味着循环播放音频),在您的while循环线程中,您正在存储录制的数据(audioData bufer),在那里您可以将其复制到播放器对象中while循环(player.write(audioData,0,numShortsRead);)。你说你的UI线程卡住了,可能是因为你给予音频记录线程更多的优先权。

检查以下用于上述环回要求的代码

boolean m_isRun=true;
public void loopback() {
        // Prepare the AudioRecord & AudioTrack
        try {
            buffersize = AudioRecord.getMinBufferSize(SAMPLE_RATE,
                    AudioFormat.CHANNEL_CONFIGURATION_MONO,
                    AudioFormat.ENCODING_PCM_16BIT);

            if (buffersize <= BUF_SIZE) {
                buffersize = BUF_SIZE;
            }
            Log.i(LOG_TAG,"Initializing Audio Record and Audio Playing objects");

            m_record = new AudioRecord(MediaRecorder.AudioSource.MIC,
                    SAMPLE_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                    AudioFormat.ENCODING_PCM_16BIT, buffersize * 1);

            m_track = new AudioTrack(AudioManager.STREAM_ALARM,
                    SAMPLE_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                    AudioFormat.ENCODING_PCM_16BIT, buffersize * 1,
                    AudioTrack.MODE_STREAM);

            m_track.setPlaybackRate(SAMPLE_RATE);
        } catch (Throwable t) {
            Log.e("Error", "Initializing Audio Record and Play objects Failed "+t.getLocalizedMessage());
        }

        m_record.startRecording();
        Log.i(LOG_TAG,"Audio Recording started");
        m_track.play();
        Log.i(LOG_TAG,"Audio Playing started");

        while (m_isRun) {
            m_record.read(buffer, 0, BUF_SIZE);
            m_track.write(buffer, 0, buffer.length);
        }

        Log.i(LOG_TAG, "loopback exit");
    }

    private void do_loopback() {
        m_thread = new Thread(new Runnable() {
            public void run() {
                loopback();
            }
        });

还有一件事,如果你的要求是记录几秒然后播放,当它正在播放你的记录时应该重新开始,你可以用一个带有超时的延迟处理程序线程做到这一点,在那个线程你可以停止录制复制缓冲区,然后开始录制。

答案 1 :(得分:3)

我的建议是使用其他称为无痛线程的异步任务。这是利用Android中线程的最佳方式。您可以通过划分现有程序来使用后台处理,预执行和后执行方法。

http://android-developers.blogspot.ca/2009/05/painless-threading.html