1.MediaRecorder及MediaPlayer:
MediaRecorder类父类是object,位于media包下,是用来录制音频和视频。
下面是录音模型:
看起来很是复杂的样子,当然了解下流程还是有必要的。至少你知道如果想重新初始化的话,可以直接使用 reset()方法,不管在哪个阶段都可以重置,有图有真相是吧!
其他的话,也没啥说的,流程都是规范化了的,改动的空间不大,看api好了。
MediaPlayer也位于media包下,直接继承与object类,可以用来放音频/视频的文件和流。
下面是工作流程:
看起来比录音的流程更复杂了,更深的东西我们暂时就不深究了。相对于录音来说,主要多了暂停,回放的状态,方法中更是添加了循环,进度等东西,更多的就不说了,有兴趣的童鞋可以慢慢研究,可以对下面的实例进行扩展。
首先,我对功能进行了封装。
/** * 录音的类,封装了录音/播放的开始停止功能 */ public class MyRecord { /** 录音 */ private MediaRecorder mRecorder; /** 播放 */ private MediaPlayer mPlayer; private String path = ""; public static MyRecord myRecord = null; // 单例 private MyRecord() { mkMyDir(); }; public static synchronized MyRecord getInstance() { if (myRecord == null) { myRecord = new MyRecord(); } return myRecord; } // 在sdcard上创建文件夹 public void mkMyDir() { File dir = new File(Environment.getExternalStorageDirectory() .getAbsolutePath() + "/TestRecord"); path = dir.getAbsolutePath(); dir.mkdir(); } // 获取文件夹路径 public String getPath() { return path; } // 开始录音 public void startRecord(String filePath) { mRecorder = new MediaRecorder(); mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mRecorder.setOutputFile(filePath); mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 设定录音文件大小 ,大概是1s 就是1k的大小 // mRecorder.setMaxFileSize(10 * 1024); // 设定录音的最大时长,貌似这个跟上面的数据差距有点大 ,最大得到的数据是17k多的数据 // mRecorder.setMaxDuration(10 * 1000); try { mRecorder.prepare(); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } mRecorder.start(); } // 停止录音 public void stopRecord() { mRecorder.stop(); mRecorder.release(); mRecorder = null; } // 播放录音 public void startPlay(String fileName) { mPlayer = new MediaPlayer(); try { mPlayer.setDataSource(fileName); mPlayer.prepare(); mPlayer.start(); } catch (IOException e) { e.printStackTrace(); } } // 停止播放 public void stopPlay() { mPlayer.stop(); mPlayer.release(); mPlayer = null; } }
然后是使用了:
public class TestVoice extends Activity implements OnClickListener { private Button startR; private Button stopR; private Button startP; private Button stopP; private MyRecord myRecord; private String path = "/test.3gp"; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.acy_testvoice); initView(); } public void initView() { myRecord = MyRecord.getInstance(); path = myRecord.getPath() + path; startR = (Button) findViewById(R.id.startRecord); stopR = (Button) findViewById(R.id.stopRecord); startP = (Button) findViewById(R.id.startPlay); stopP = (Button) findViewById(R.id.stopPlay); startR.setOnClickListener(this); stopR.setOnClickListener(this); startP.setOnClickListener(this); stopP.setOnClickListener(this); } @Override public void onClick(View v) { // TODO Auto-generated method stub if (v == startR) { myRecord.startRecord(path); } else if (v == stopR) { myRecord.stopRecord(); } else if (v == startP) { myRecord.startPlay(path); } else if (v == stopP) { myRecord.stopPlay(); } } }
2.AudioTrack及AudioRecord:
这是通过流读取操作来录制/播放音频文件的,相对于上面的方法来对解码器更加依赖来讲,这是直接读取的是pcm文件。不过好像录音的效果不咋好,通过对流的处理是可以实现降噪的,在网上没找到比较好的办法,在这就不写了,因为我也不懂那个。
public class AltAudioRecorder extends Activity implements OnClickListener { private RecordAudio recordTask; private PlayAudio playTask; private Button startRecordingButton, stopRecordingButton, startPlaybackButton, stopPlaybackButton; private TextView statusText; File recordingFile; boolean isRecording = false; boolean isPlaying = false; // 要确定在人耳的接收频率以内, int frequency = 11025; int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_STEREO; int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.acy_record); statusText = (TextView) this.findViewById(R.id.StatusTextView); startRecordingButton = (Button) this .findViewById(R.id.StartRecordingButton); stopRecordingButton = (Button) this .findViewById(R.id.StopRecordingButton); startPlaybackButton = (Button) this .findViewById(R.id.StartPlaybackButton); stopPlaybackButton = (Button) this .findViewById(R.id.StopPlaybackButton); startRecordingButton.setOnClickListener(this); stopRecordingButton.setOnClickListener(this); startPlaybackButton.setOnClickListener(this); stopPlaybackButton.setOnClickListener(this); File path = new File(Environment.getExternalStorageDirectory() .getAbsolutePath() + "/Android/data/AudioRecorder/files/"); path.mkdirs(); try { recordingFile = File.createTempFile("recording", ".pcm", path); } catch (Exception e) { throw new RuntimeException("Couldn't create file on SD card", e); } } public void onClick(View v) { if (v == startRecordingButton) { record(); } else if (v == stopRecordingButton) { stopRecording(); } else if (v == startPlaybackButton) { play(); } else if (v == stopPlaybackButton) { stopPlaying(); } } //开始录音 public void play() { playTask = new PlayAudio(); playTask.execute(); } //停止录音 public void stopPlaying() { isPlaying = false; } //开始播放 public void record() { recordTask = new RecordAudio(); recordTask.execute(); } //停止播放 public void stopRecording() { isRecording = false; } private class PlayAudio extends AsyncTask<Void, Integer, Void> { @Override protected Void doInBackground(Void... params) { isPlaying = true; // 返回实例创建后的最好缓冲区 int bufferSize = AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding); short[] audiodata = new short[bufferSize / 4]; try { DataInputStream dis = new DataInputStream( new BufferedInputStream(new FileInputStream( recordingFile))); AudioTrack audioTrack = new AudioTrack( // 流类型 AudioManager.STREAM_MUSIC, // 音频数据的采样率 frequency, // 声道 为双声道立体声 channelConfiguration, // 设置音频数据块为16位 audioEncoding, // 在录制过程中,音频数据写入缓冲区的总数(字节) bufferSize, // 设置模式类型为流 AudioTrack.MODE_STREAM); audioTrack.play(); while (isPlaying && dis.available() > 0) { int i = 0; while (dis.available() > 0 && i < audiodata.length) { audiodata[i] = dis.readShort(); i++; } audioTrack.write(audiodata, 0, audiodata.length); } dis.close(); } catch (Throwable t) { Log.e("AudioTrack", "Playback Failed"); } return null; } } private class RecordAudio extends AsyncTask<Void, Integer, Void> { @Override protected Void doInBackground(Void... params) { isRecording = true; try { DataOutputStream dos = new DataOutputStream( new BufferedOutputStream(new FileOutputStream( recordingFile))); int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding); AudioRecord audioRecord = new AudioRecord( MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, audioEncoding, bufferSize); short[] buffer = new short[bufferSize]; audioRecord.startRecording(); int r = 0; while (isRecording) { int bufferReadResult = audioRecord.read(buffer, 0, bufferSize); for (int i = 0; i < bufferReadResult; i++) { dos.writeShort(buffer[i]); } publishProgress(new Integer(r)); r++; } audioRecord.stop(); dos.close(); } catch (Throwable t) { Log.e("AudioRecord", "Recording Failed"); } return null; } protected void onProgressUpdate(Integer... progress) { statusText.setText(progress[0].toString()); } protected void onPostExecute(Void result) { } } }
1.android.media.MediaPlayer.finalize() timed out after 10 seconds
在onPause()中调用MediaPlayer.release()
,在onStop()中释放对象:
mPlayer.stop(); mPlayer.release(); mPlayer = null;