android语音录制与播放

android语音录制可以通过MediaRecorder和AudioRecorder。
MediaRecorder本来是多媒体录制控件,可以同时录制视频和语音,当不指定视频源时就只录制语音;AudioRecorder只能录制语音。
二者录制的区别在于,MediaRecorder固定了语音的编码格式,而且使用时指定输出文件,在录制的同时系统将语音数据写入文件。AudioRecorder输出的是pcm,即原始音频数据,使用者需要自己读取这些数据,这样的好处是可以根据需要边录制边对音频数据处理,读取的同时也可以保存到文件进行存储。


语音的播放可以使用MediaPlayer和AudioTracker,与上面的对应,MediaPlayer可以播放各种多媒体文件,而AudioTracker只能播放pcm数据,使用者手动将数据连续写入进行播放。

MediaRecorder的使用

  1. private void startRecording() {  
  2.         mRecorder = new MediaRecorder();  
  3.         mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);  
  4.         mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);  
  5.         mRecorder.setOutputFile(mFileName);  
  6.         mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);  
  7.   
  8.   
  9.         try {  
  10.             mRecorder.prepare();  
  11.         } catch (IOException e) {  
  12.             Log.e(LOG_TAG, "prepare() failed");  
  13.         }  
  14.   
  15.   
  16.         mRecorder.start();  
  17. }  
private void startRecording() {
        mRecorder = new MediaRecorder();
        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
        mRecorder.setOutputFile(mFileName);
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);


        try {
            mRecorder.prepare();
        } catch (IOException e) {
            Log.e(LOG_TAG, "prepare() failed");
        }


        mRecorder.start();
}


AudioRecorder录制语音


  1. int suggestBufferSize = AudioRecord.getMinBufferSize(mSampleRate,  
  2.                 mChannelConfig, mAudioFormat);  
  3. mAudioRecord = new AudioRecord(AudioSource.MIC, mSampleRate,  
  4.                 mChannelConfig, mAudioFormat, suggestBufferSize);  
  5. mAudioRecorder.startRecording();  
  6. byte[] inByteBuf = new byte[BUF_SIZE]  
  7. while (runFlag) {  
  8.     int readSize = mAudioRecord.read(inByteBuf, 0, inByteBuf.length);  
  9. }  
  10. mAudioRecorder.stop();  
  11. mAudioRecord.release();  
int suggestBufferSize = AudioRecord.getMinBufferSize(mSampleRate,
				mChannelConfig, mAudioFormat);
mAudioRecord = new AudioRecord(AudioSource.MIC, mSampleRate,
				mChannelConfig, mAudioFormat, suggestBufferSize);
mAudioRecorder.startRecording();
byte[] inByteBuf = new byte[BUF_SIZE]
while (runFlag) {
	int readSize = mAudioRecord.read(inByteBuf, 0, inByteBuf.length);
}
mAudioRecorder.stop();
mAudioRecord.release();


上面是AudioRecorder的完整使用过程,AudioRecorder实例化的时候需要指定录音源、采样率等音频参数,最后一个是音频数据缓冲区大小,需要通过AudioRecord.getMinBufferSize()来得到缓冲区的最小值,如果实例化时参数小于这个最小值,那么AudioRecoder将创建失败。当然大于这个值肯定可以。之后通过read将缓冲区的数据读出来。
之前一直以为读取时使用的byte数组大小必须和缓冲区的大小一致,但实际并不是这样,看下AudioRecorder构造函数中对bufferSizeInBytes的解释:

bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written to during the recording. New audio data can be read from this buffer in smaller chunks than this size.
也就是说缓冲区只是系统用来临时存放音频数据的,读取时可以每次读取较小的块,然后多次读取。

AudioRecorder还有一个方法setPositionNotificationPeriod (int periodInFrames)。这个方法可以在读取指定数据后发出一个回调,需要配合setRecordPositionUpdateListener (AudioRecord.OnRecordPositionUpdateListener listener),当读取的总数据是指定的periodInFrames的整数倍时就会调用listner的方法onPeriodicNotification.
  1. new OnRecordPositionUpdateListener() {  
  2.               
  3.             @Override  
  4.             public void onPeriodicNotification(AudioRecord recorder) {  
  5.                 // TODO Auto-generated method stub  
  6.                   
  7.             }  
  8.               
  9.             @Override  
  10.             public void onMarkerReached(AudioRecord recorder) {  
  11.                 // TODO Auto-generated method stub  
  12.                   
  13.             }  
  14. };  
new OnRecordPositionUpdateListener() {
			
			@Override
			public void onPeriodicNotification(AudioRecord recorder) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void onMarkerReached(AudioRecord recorder) {
				// TODO Auto-generated method stub
				
			}
};


这个特性使用时有个注意点,就是回调只会发生在实际数据读取之后,也就是使用者通过read方法读取出periodInFrames这么多数据时才会触发这个回调,否则什么也不会发生。


MeidaPlayer播放音频文件


  1. MediaPlayer mediaPlayer = new MediaPlayer();  
  2. mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);  
  3. mediaPlayer.setDataSource(getApplicationContext(), myUri);  
  4. mediaPlayer.prepare();  
  5. mediaPlayer.start();  
  6. mediaPlayer.stop();  
  7. mediaPlayer.release();  
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();
mediaPlayer.stop();
mediaPlayer.release();


其中setDataSource()有多个覆写,如下
void setDataSource(String path)
Sets the data source (file-path or http/rtsp URL) to use.
void setDataSource(Context context, Uri uri, Map<String, String> headers)
Sets the data source as a content Uri.
void setDataSource(Context context, Uri uri)
Sets the data source as a content Uri.
void setDataSource(FileDescriptor fd, long offset, long length)
Sets the data source (FileDescriptor) to use.
void setDataSource(FileDescriptor fd)
Sets the data source (FileDescriptor) to use.
可以看到不同的输入参数指定了数据的来源。其中setDataSource(FileDescriptor fd, long offset, long length)可以指定开始读取的偏移量和长度。
之所以会注意到这个参数是因为在实际使用时有一个需求,即可以播放WAV文件,又可以播放MP3文件,而且能够限定播放开始和结束的位置。
对于MP3文件使用setDataSource(FileDescriptor fd, long offset, long length)是完全可行的,像这样

  1. mPlayer.reset();  
  2. mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);  
  3. mPlayer.setDataSource(  
  4.         new FileInputStream(mSoundFile.getFilePath()).getFD(),  
  5.                     startByte, endByte - startByte);  
mPlayer.reset();
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setDataSource(
		new FileInputStream(mSoundFile.getFilePath()).getFD(),
					startByte, endByte - startByte);
  1. mPlayer.prepare();  
mPlayer.prepare();


但WAV文件却完全没有效果,是什么原因呢,看下官方对setDataSource(FileDescriptor fd, long offset, long length)的解释
Sets the data source (FileDescriptor) to use. The FileDescriptor must be seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility to close the file descriptor. It is safe to do so as soon as this call returns.

注意到该方法指定的文件类型必须是seekable的,mp3文件属于这种类型(tips:不知道还有没有其他类型),因为mp3内部是按帧存储的,可以指定到具体的帧位置,而WAV文件音频数据是pcm,也就是一大片完整的数据,要对wav文件指定开始播放位置,需要使用另一个方法seekTo (int msec)。这里指定的参数是毫秒。


开始时间可以指定了,那结束播放的时间如何指定?

MediaPlayer有另外两个方法:getDuration ()和getCurrentPosition (),这两个返回的都是时间信息,前者返回总的播放时间,后者返回当前播放位置的时间。


那返回的结果对于mp3和wav是否会不同呢?会!
对WAV类型,getDuration返回的是音频文件的总时间,getCurrentPosition返回的是从文件起始到现在的播放时间;而对mp3类型,getDuration返回的是startByte和endByte之间播放的时间间隔,getCurrentPosition返回从startByte到现在的播放时间。想一下原因也可以明白,因为MP3是可以精确指定起始位置的,所以所有计算都可以从指定的位置开始,而wav文件一切只能从最开始的位置计算。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值