Code example for AudioRecord

Methods: getRecordingStategetStatereleasestop

0
		mOneSec = RESOLUTION_IN_BYTES * CHANNELS * mSampleRate;
		// TODO: replace 35 with the max length of the recording (as specified in the settings) 
		mRecording = new byte[mOneSec * 35];
		try { 
			setBufferSizeAndFramePeriod(); 
			mRecorder = new AudioRecord(audioSource, mSampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO, RESOLUTION, mBufferSize);
			if (mRecorder.getState() != AudioRecord.STATE_INITIALIZED) {
				throw new Exception("AudioRecord initialization failed");
			} 
			mBuffer = new byte[mFramePeriod * RESOLUTION_IN_BYTES * CHANNELS];
			setState(State.READY);
		} catch (Exception e) {
			release(); 
			setState(State.ERROR);
			if (e.getMessage() == null) {
				Log.e(LOG_TAG, "Unknown error occured while initializing recording");
			} else { 
				Log.e(LOG_TAG, e.getMessage());
			} 
		} 
	} 
 
 
	public RawAudioRecorder(int sampleRate) {
		this(DEFAULT_AUDIO_SOURCE, sampleRate);
	} 
 
 
	public RawAudioRecorder() { 
		this(DEFAULT_AUDIO_SOURCE, DEFAULT_SAMPLE_RATE);
	} 
 
 
	private int read(AudioRecord recorder) {
		// public int read (byte[] audioData, int offsetInBytes, int sizeInBytes) 
		int numberOfBytes = recorder.read(mBuffer, 0, mBuffer.length); // Fill buffer
 
		// Some error checking 
		if (numberOfBytes == AudioRecord.ERROR_INVALID_OPERATION) {
			Log.e(LOG_TAG, "The AudioRecord object was not properly initialized");
			return -1; 
		} else if (numberOfBytes == AudioRecord.ERROR_BAD_VALUE) {
			Log.e(LOG_TAG, "The parameters do not resolve to valid data and indexes.");
			return -2; 
		} else if (numberOfBytes > mBuffer.length) {
			Log.e(LOG_TAG, "Read more bytes than is buffer length:" + numberOfBytes + ": " + mBuffer.length);
			return -3; 
		} else if (numberOfBytes == 0) {
			Log.e(LOG_TAG, "Read zero bytes");
			return -4; 
		} 
		// Everything seems to be OK, adding the buffer to the recording. 
		add(mBuffer);
		return 0; 
	} 
 
 
	// old version 
	private void setBufferSizeAndFramePeriod_812() { 
		// The interval in which the recorded samples are output to the file 
		// TODO: explain why 120 
		final int TIMER_INTERVAL = 120;
		mFramePeriod = mSampleRate * TIMER_INTERVAL / 1000;
		mBufferSize = mFramePeriod * 2 * RESOLUTION_IN_BYTES * CHANNELS;
 
		// Check to make sure buffer size is not smaller than the smallest allowed one 
		if (mBufferSize < AudioRecord.getMinBufferSize(mSampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO, RESOLUTION)) {
			mBufferSize = AudioRecord.getMinBufferSize(mSampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO, RESOLUTION);
			// Set frame period and timer interval accordingly 
			mFramePeriod = mBufferSize / ( 2 * RESOLUTION_IN_BYTES * CHANNELS );
			Log.i(LOG_TAG, "AudioRecord buffer size (MIN): " + mBufferSize);
		} 
	} 
 
 
	private void setBufferSizeAndFramePeriod() { 
		int minBufferSizeInBytes = AudioRecord.getMinBufferSize(mSampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO, RESOLUTION);
		if (minBufferSizeInBytes == AudioRecord.ERROR_BAD_VALUE) {
			throw new IllegalArgumentException("AudioRecord.getMinBufferSize: parameters not supported by hardware");
		} else if (minBufferSizeInBytes == AudioRecord.ERROR) {
			Log.e(LOG_TAG, "AudioRecord.getMinBufferSize: unable to query hardware for output properties");
			minBufferSizeInBytes = mSampleRate * (120 / 1000) * RESOLUTION_IN_BYTES * CHANNELS;
		} 
		mBufferSize = 2 * minBufferSizeInBytes;
		mFramePeriod = mBufferSize / ( 2 * RESOLUTION_IN_BYTES * CHANNELS );
		Log.i(LOG_TAG, "AudioRecord buffer size: " + mBufferSize + ", min size = " + minBufferSizeInBytes);
	} 
 
 
	/** 
	 * @return recorder state 
	 */ 
	public State getState() { 
		return mState;
	} 
 
	private void setState(State state) {
		mState = state;
	} 
 
 
	/** 
	 * @return bytes that have been recorded since the beginning 
	 */ 
	public byte[] getCompleteRecording() { 
		return getCurrentRecording(0); 
	} 
 
 
	/** 
	 * @return bytes that have been recorded since this method was last called 
	 */ 
	public synchronized byte[] consumeRecording() { 
		byte[] bytes = getCurrentRecording(mConsumedLength);
		Log.i(LOG_TAG, "Copied from: " + mConsumedLength + ": " + bytes.length + " bytes");
		mConsumedLength = mRecordedLength;
		return bytes;
	} 
 
 
	private byte[] getCurrentRecording(int startPos) {
		int len = getLength() - startPos;
		byte[] bytes = new byte[len];
		System.arraycopy(mRecording, startPos, bytes, 0, len);
		return bytes;
	} 
 
 
	public int getLength() { 
		return mRecordedLength;
	} 
 
 
	/** 
	 * @return <code>true</code> iff a speech-ending pause has occurred at the end of the recorded data 
	 */ 
	public boolean isPausing() { 
		double pauseScore = getPauseScore();
		Log.i(LOG_TAG, "Pause score: " + pauseScore);
		return pauseScore > 7;
	} 
 
 
	/** 
	 * @return volume indicator that shows the average volume of the last read buffer 
	 */ 
	public float getRmsdb() { 
		long sumOfSquares = getRms(mRecordedLength, mBuffer.length);
		double rootMeanSquare = Math.sqrt(sumOfSquares / (mBuffer.length / 2));
		if (rootMeanSquare > 1) {
			Log.i(LOG_TAG, "getRmsdb(): " + rootMeanSquare);
			// TODO: why 10? 
			return (float) (10 * Math.log10(rootMeanSquare));
		} 
		return 0; 
	} 
 
 
	/** 
	 * <p>In order to calculate if the user has stopped speaking we take the 
	 * data from the last second of the recording, map it to a number 
	 * and compare this number to the numbers obtained previously. We 
	 * return a confidence score (0-INF) of a longer pause having occurred in the 
	 * speech input.</p> 
	 *  
	 * <p>TODO: base the implementation on some well-known technique.</p> 
	 *  
	 * @return positive value which the caller can use to determine if there is a pause 
	 */ 
	private double getPauseScore() { 
		long t2 = getRms(mRecordedLength, mOneSec);
		if (t2 == 0) {
			return 0; 
		} 
		double t = mAvgEnergy / t2;
		mAvgEnergy = (2 * mAvgEnergy + t2) / 3;
		return t;
	} 
 
 
	/** 
	 * <p>Stops the recording (if needed) and releases the resources. 
	 * The object can no longer be used and the reference should be 
	 * set to null after a call to release().</p> 
	 */ 
	public synchronized void release() { 
		if (mRecorder != null) {
			if (mRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
				stop(); 
			} 
			mRecorder.release();
			mRecorder = null;
		} 
	} 
 
 
	/** 
	 * <p>Starts the recording, and sets the state to RECORDING.</p> 
	 */ 
	public void start() { 
		if (mRecorder.getState() == AudioRecord.STATE_INITIALIZED) {
			mRecorder.startRecording();
			if (mRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
				setState(State.RECORDING);
				new Thread() {
					public void run() { 
						while (mRecorder != null && mRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
							int status = read(mRecorder);
							if (status < 0) {
								break; 
							} 
						} 
					} 
				}.start();
			} else { 
				Log.e(LOG_TAG, "startRecording() failed");
				setState(State.ERROR);
			} 
		} else { 
			Log.e(LOG_TAG, "start() called on illegal state");
			setState(State.ERROR);
		} 
	} 
 
 
	/** 
	 * <p>Stops the recording, and sets the state to STOPPED. 
	 * If stopping fails then sets the state to ERROR.</p> 
	 */ 
	public void stop() { 
		// We check the underlying AudioRecord state trying to avoid IllegalStateException. 
		// If it still occurs then we catch it. 
		if (mRecorder.getState() == AudioRecord.STATE_INITIALIZED &&
				mRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
			try { 
				mRecorder.stop();
				setState(State.STOPPED);
			} catch (IllegalStateException e) {
				Log.e(LOG_TAG, "native stop() called in illegal state: " + e.getMessage());
				setState(State.ERROR);
			}