1 /*
2  * Copyright 2020 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 #include <android/log.h>
17 
18 #include "OboeRecorder.h"
19 
20 #include "AudioSink.h"
21 
22 static const char * const TAG = "OboeRecorder(native)";
23 
24 using namespace oboe;
25 
26 constexpr int32_t kBufferSizeInBursts = 2; // Use 2 bursts as the buffer size (double buffer)
27 
OboeRecorder(AudioSink * sink,int32_t subtype)28 OboeRecorder::OboeRecorder(AudioSink* sink, int32_t subtype)
29         : Recorder(sink, subtype),
30           mInputPreset(-1)
31 {}
32 
33 //
34 // State
35 //
setupStream(int32_t channelCount,int32_t sampleRate,int32_t routeDeviceId)36 StreamBase::Result OboeRecorder::setupStream(
37     int32_t channelCount, int32_t sampleRate, int32_t routeDeviceId)
38 {
39     //TODO much of this could be pulled up into OboeStream.
40 
41     std::lock_guard<std::mutex> lock(mStreamLock);
42 
43     oboe::Result result = oboe::Result::ErrorInternal;
44     if (mAudioStream != nullptr) {
45         return ERROR_INVALID_STATE;
46     } else {
47         mChannelCount = channelCount;
48         mSampleRate = sampleRate;
49         mRouteDeviceId = routeDeviceId;
50 
51         // Create an audio stream
52         AudioStreamBuilder builder;
53         builder.setChannelCount(mChannelCount);
54         builder.setSampleRate(mSampleRate);
55         builder.setCallback(this);
56         if (mInputPreset != DEFAULT_INPUT_NONE) {
57             builder.setInputPreset((enum InputPreset)mInputPreset);
58         }
59         builder.setPerformanceMode(PerformanceMode::LowLatency);
60         // builder.setPerformanceMode(PerformanceMode::None);
61         builder.setSharingMode(SharingMode::Exclusive);
62         builder.setSampleRateConversionQuality(SampleRateConversionQuality::None);
63         builder.setDirection(Direction::Input);
64 
65         if (mRouteDeviceId != -1) {
66             builder.setDeviceId(mRouteDeviceId);
67         }
68 
69         if (mSubtype == SUB_TYPE_OBOE_AAUDIO) {
70             builder.setAudioApi(AudioApi::AAudio);
71         } else if (mSubtype == SUB_TYPE_OBOE_OPENSL_ES) {
72             builder.setAudioApi(AudioApi::OpenSLES);
73         }
74 
75         result = builder.openStream(mAudioStream);
76         if (result != oboe::Result::OK){
77             __android_log_print(
78                     ANDROID_LOG_ERROR,
79                     TAG,
80                     "openStream failed. Error: %s", convertToText(result));
81         } else {
82             mBufferSizeInFrames = mAudioStream->getFramesPerBurst();
83             mAudioSink->init(mBufferSizeInFrames, mChannelCount);
84         }
85     }
86 
87     return OboeErrorToMegaAudioError(result);
88 }
89 
startStream()90 StreamBase::Result OboeRecorder::startStream() {
91     StreamBase::Result result = Recorder::startStream();
92     if (result == OK) {
93         mAudioSink->start();
94     }
95     return result;
96 }
97 
onAudioReady(oboe::AudioStream * audioStream,void * audioData,int numFrames)98 oboe::DataCallbackResult OboeRecorder::onAudioReady(
99         oboe::AudioStream *audioStream, void *audioData, int numFrames) {
100     mAudioSink->push((float*)audioData, numFrames, mChannelCount);
101     return oboe::DataCallbackResult::Continue;
102 }
103 
104 #include <jni.h>
105 
106 extern "C" {
107 JNIEXPORT jlong JNICALL
Java_org_hyphonate_megaaudio_recorder_OboeRecorder_allocNativeRecorder(JNIEnv * env,jobject thiz,jlong native_audio_sink,jint recorderSubtype)108 Java_org_hyphonate_megaaudio_recorder_OboeRecorder_allocNativeRecorder(JNIEnv *env, jobject thiz, jlong native_audio_sink, jint recorderSubtype) {
109     OboeRecorder* recorder = new OboeRecorder((AudioSink*)native_audio_sink, recorderSubtype);
110     return (jlong)recorder;
111 }
112 
113 JNIEXPORT jint JNICALL
Java_org_hyphonate_megaaudio_recorder_OboeRecorder_getBufferFrameCountN(JNIEnv * env,jobject thiz,jlong native_recorder)114 Java_org_hyphonate_megaaudio_recorder_OboeRecorder_getBufferFrameCountN(
115         JNIEnv *env, jobject thiz, jlong native_recorder) {
116     return ((OboeRecorder*)native_recorder)->getNumBufferFrames();
117 }
118 
119 JNIEXPORT void JNICALL
Java_org_hyphonate_megaaudio_recorder_OboeRecorder_setInputPresetN(JNIEnv * env,jobject thiz,jlong native_recorder,jint input_preset)120 Java_org_hyphonate_megaaudio_recorder_OboeRecorder_setInputPresetN(JNIEnv *env, jobject thiz,
121                                                                    jlong native_recorder,
122                                                                    jint input_preset) {
123     ((OboeRecorder*)native_recorder)->setInputPreset(input_preset);
124 }
125 
126 JNIEXPORT jint JNICALL
Java_org_hyphonate_megaaudio_recorder_OboeRecorder_setupStreamN(JNIEnv * env,jobject thiz,jlong native_recorder,jint channel_count,jint sample_rate,jint route_device_id)127 Java_org_hyphonate_megaaudio_recorder_OboeRecorder_setupStreamN(JNIEnv *env, jobject thiz,
128                                                                    jlong native_recorder,
129                                                                    jint channel_count,
130                                                                    jint sample_rate,
131                                                                    jint route_device_id) {
132     return ((OboeRecorder*)native_recorder)->setupStream(channel_count, sample_rate, route_device_id);
133 }
134 
135 JNIEXPORT jint JNICALL
Java_org_hyphonate_megaaudio_recorder_OboeRecorder_teardownStreamN(JNIEnv * env,jobject thiz,jlong native_recorder)136 Java_org_hyphonate_megaaudio_recorder_OboeRecorder_teardownStreamN(
137     JNIEnv *env, jobject thiz, jlong native_recorder) {
138     return ((OboeRecorder*)native_recorder)->teardownStream();
139 }
140 
141 JNIEXPORT jint JNICALL
Java_org_hyphonate_megaaudio_recorder_OboeRecorder_startStreamN(JNIEnv * env,jobject thiz,jlong native_recorder,jint recorder_subtype)142 Java_org_hyphonate_megaaudio_recorder_OboeRecorder_startStreamN(JNIEnv *env, jobject thiz,
143                                                               jlong native_recorder,
144                                                               jint recorder_subtype) {
145     return ((OboeRecorder*)native_recorder)->startStream();
146 }
147 
148 JNIEXPORT jint JNICALL
Java_org_hyphonate_megaaudio_recorder_OboeRecorder_stopN(JNIEnv * env,jobject thiz,jlong native_recorder)149 Java_org_hyphonate_megaaudio_recorder_OboeRecorder_stopN(JNIEnv *env, jobject thiz,
150                                                        jlong native_recorder) {
151     return ((OboeRecorder*)native_recorder)->stopStream();
152 }
153 
154 JNIEXPORT jboolean JNICALL
Java_org_hyphonate_megaaudio_recorder_OboeRecorder_isRecordingN(JNIEnv * env,jobject thiz,jlong native_recorder)155         Java_org_hyphonate_megaaudio_recorder_OboeRecorder_isRecordingN(
156                 JNIEnv *env, jobject thiz, jlong native_recorder) {
157     OboeRecorder* nativeRecorder = ((OboeRecorder*)native_recorder);
158     return nativeRecorder->isRecording();
159 }
160 
161 JNIEXPORT jint JNICALL
Java_org_hyphonate_megaaudio_recorder_OboeRecorder_getNumBufferFramesN(JNIEnv * env,jobject thiz,jlong native_recorder)162 Java_org_hyphonate_megaaudio_recorder_OboeRecorder_getNumBufferFramesN(
163         JNIEnv *env, jobject thiz, jlong native_recorder) {
164     OboeRecorder* nativeRecorder = ((OboeRecorder*)native_recorder);
165     return nativeRecorder->getNumBufferFrames();
166 }
167 
168 extern "C"
169 JNIEXPORT jint JNICALL
Java_org_hyphonate_megaaudio_recorder_OboeRecorder_getRoutedDeviceIdN(JNIEnv * env,jobject thiz,jlong native_recorder)170 Java_org_hyphonate_megaaudio_recorder_OboeRecorder_getRoutedDeviceIdN(JNIEnv *env, jobject thiz, jlong native_recorder) {
171     return ((OboeRecorder*)native_recorder)->getRoutedDeviceId();
172 }
173 
174 }   // extern "C"
175