1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.mobileer.oboetester; 18 19 20 import android.media.AudioDeviceInfo; 21 import android.media.AudioFormat; 22 import android.media.AudioRecord; 23 import android.media.MediaRecorder; 24 25 /** 26 * Abstract class for recording. 27 * Call processBuffer(buffer) when data is read. 28 */ 29 class AudioRecordThread implements Runnable { 30 private static final String TAG = "AudioRecordThread"; 31 32 private final int mSampleRate; 33 private final int mChannelCount; 34 private Thread mThread; 35 protected boolean mGo; 36 private AudioRecord mRecorder; 37 private CircularCaptureBuffer mCaptureBuffer; 38 protected float[] mBuffer = new float[256]; 39 private static int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_FLOAT; 40 private Runnable mTask; 41 private int mTaskCountdown; 42 private boolean mCaptureEnabled = true; 43 44 private AudioDeviceInfo mDeviceInfo; 45 AudioRecordThread(int frameRate, int channelCount, int maxFrames)46 public AudioRecordThread(int frameRate, int channelCount, int maxFrames) { 47 mSampleRate = frameRate; 48 mChannelCount = channelCount; 49 mCaptureBuffer = new CircularCaptureBuffer(maxFrames); 50 } 51 createRecorder()52 private void createRecorder() { 53 int channelConfig = (mChannelCount == 1) 54 ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO; 55 int audioFormat = AudioFormat.ENCODING_PCM_FLOAT; 56 int minRecordBuffSizeInBytes = AudioRecord.getMinBufferSize(mSampleRate, 57 channelConfig, 58 audioFormat); 59 mRecorder = new AudioRecord( 60 MediaRecorder.AudioSource.VOICE_RECOGNITION, 61 mSampleRate, 62 channelConfig, 63 audioFormat, 64 2 * minRecordBuffSizeInBytes); 65 mRecorder.setPreferredDevice(mDeviceInfo); 66 if (mRecorder.getState() == AudioRecord.STATE_UNINITIALIZED) { 67 throw new RuntimeException("Could not make the AudioRecord - UNINITIALIZED"); 68 } 69 } 70 71 @Override run()72 public void run() { 73 startAudioRecording(); 74 75 while (mGo) { 76 int result = handleAudioPeriod(); 77 if (result < 0) { 78 mGo = false; 79 } 80 } 81 82 stopAudioRecording(); 83 } 84 startAudio()85 public void startAudio() { 86 if (mThread == null) { 87 mGo = true; 88 mThread = new Thread(this); 89 mThread.start(); 90 } 91 } 92 stopAudio()93 public void stopAudio() { 94 mGo = false; 95 if (mThread != null) { 96 try { 97 mThread.join(1000); 98 } catch (InterruptedException e) { 99 e.printStackTrace(); 100 } 101 mThread = null; 102 } 103 } 104 getSampleRate()105 public int getSampleRate() { 106 return mSampleRate; 107 } 108 109 /** 110 * @return number of samples read or negative error 111 */ handleAudioPeriod()112 private int handleAudioPeriod() { 113 int numSamplesRead = mRecorder.read(mBuffer, 0, mBuffer.length, 114 AudioRecord.READ_BLOCKING); 115 if (numSamplesRead <= 0) { 116 return numSamplesRead; 117 } else { 118 if (mTaskCountdown > 0) { 119 mTaskCountdown -= numSamplesRead; 120 if (mTaskCountdown <= 0) { 121 mTaskCountdown = 0; 122 new Thread(mTask).start(); // run asynchronously with audio thread 123 } 124 } 125 if (mCaptureEnabled) { 126 return mCaptureBuffer.write(mBuffer, 0, numSamplesRead); 127 } else { 128 return numSamplesRead; 129 } 130 } 131 } 132 startAudioRecording()133 private void startAudioRecording() { 134 stopAudioRecording(); 135 createRecorder(); 136 mRecorder.startRecording(); 137 } 138 stopAudioRecording()139 private void stopAudioRecording() { 140 if (mRecorder != null) { 141 mRecorder.stop(); 142 mRecorder.release(); 143 mRecorder = null; 144 } 145 } 146 147 /** 148 * Schedule task to be run on its own thread when numSamples more samples have been recorded. 149 * 150 * @param numSamples 151 * @param task 152 */ scheduleTask(int numSamples, Runnable task)153 public void scheduleTask(int numSamples, Runnable task) { 154 mTask = task; 155 mTaskCountdown = numSamples; 156 } 157 setCaptureEnabled(boolean captureEnabled)158 public void setCaptureEnabled(boolean captureEnabled) { 159 mCaptureEnabled = captureEnabled; 160 } 161 readMostRecent(float[] buffer)162 public int readMostRecent(float[] buffer) { 163 return mCaptureBuffer.readMostRecent(buffer); 164 } 165 setInputDevice(AudioDeviceInfo deviceInfo)166 public void setInputDevice(AudioDeviceInfo deviceInfo) { 167 mDeviceInfo = deviceInfo; 168 } 169 } 170