xref: /aosp_15_r20/external/oboe/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/AudioRecordThread.java (revision 05767d913155b055644481607e6fa1e35e2fe72c)
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