1. 程式人生 > >簡介錄音和播放音訊實現

簡介錄音和播放音訊實現

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;