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 "OboePlayer.h"
19
20 #include "WaveTableSource.h"
21
22 #include "AudioSource.h"
23
24 static const char * const TAG = "OboePlayer(native)";
25 static const bool LOG = true;
26
27 using namespace oboe;
28
29 constexpr int32_t kBufferSizeInBursts = 2; // Use 2 bursts as the buffer size (double buffer)
30
javaChannelMaskToOboeChannelMask(int32_t javaMask)31 ChannelMask OboePlayer::javaChannelMaskToOboeChannelMask(int32_t javaMask) {
32 return (ChannelMask) (javaMask >> 2);
33 }
34
javaChannelMaskToChannelCount(int32_t javaMask)35 int32_t OboePlayer::javaChannelMaskToChannelCount(int32_t javaMask) {
36 // return the count of 1 bits
37 return __builtin_popcount(static_cast<uint32_t>(javaMask));
38 }
39
OboePlayer(JNIEnv * env,AudioSource * source,int subtype)40 OboePlayer::OboePlayer(JNIEnv *env, AudioSource* source, int subtype)
41 : Player(source, subtype)
42 {
43 env->GetJavaVM(&mJvm);
44
45 jclass clsAudioTimestamp = env->FindClass("android/media/AudioTimestamp");
46
47 mFidFramePosition = env->GetFieldID(clsAudioTimestamp, "framePosition", "J");
48 mFidNanoTime = env->GetFieldID(clsAudioTimestamp, "nanoTime", "J");
49 }
50
onAudioReady(AudioStream * oboeStream,void * audioData,int32_t numFrames)51 DataCallbackResult OboePlayer::onAudioReady(AudioStream *oboeStream, void *audioData,
52 int32_t numFrames) {
53 StreamState streamState = oboeStream->getState();
54 if (streamState != StreamState::Open && streamState != StreamState::Started) {
55 __android_log_print(ANDROID_LOG_ERROR, TAG, " streamState:%d",
56 static_cast<int>(streamState));
57 }
58 if (streamState == StreamState::Disconnected) {
59 __android_log_print(ANDROID_LOG_ERROR, TAG, " streamState::Disconnected");
60 }
61
62 // Pull the data here!
63 int numFramesRead = mAudioSource->pull((float*)audioData, numFrames, mNumExchangeChannels);
64 // may need to handle 0-filling if numFramesRead < numFrames
65
66 return numFramesRead != 0 ? DataCallbackResult::Continue : DataCallbackResult::Stop;
67 }
68
onErrorAfterClose(AudioStream * oboeStream,oboe::Result error)69 void OboePlayer::onErrorAfterClose(AudioStream *oboeStream, oboe::Result error) {
70 }
71
onErrorBeforeClose(AudioStream *,oboe::Result error)72 void OboePlayer::onErrorBeforeClose(AudioStream *, oboe::Result error) {
73 }
74
buildStream(int32_t channelCount,int32_t channelMask,int32_t sampleRate,int32_t performanceMode,int32_t sharingMode,int32_t routeDeviceId)75 StreamBase::Result OboePlayer::buildStream(int32_t channelCount, int32_t channelMask,
76 int32_t sampleRate, int32_t performanceMode, int32_t sharingMode,
77 int32_t routeDeviceId) {
78
79 if (LOG) {
80 __android_log_print(ANDROID_LOG_DEBUG, TAG, "%s()", __FUNCTION__);
81 }
82
83 oboe::Result result = oboe::Result::ErrorInternal;
84 if (mAudioStream != nullptr) {
85 __android_log_print(ANDROID_LOG_ERROR, TAG,
86 "ERROR_INVALID_STATE - Stream Already Open.");
87 return ERROR_INVALID_STATE;
88 } else {
89 std::lock_guard<std::mutex> lock(mStreamLock);
90
91 mChannelCount = channelCount;
92 mChannelMask = channelMask;
93
94 mSampleRate = sampleRate;
95 mRouteDeviceId = routeDeviceId;
96
97 // Create an audio stream
98 if (mChannelCount != 0) {
99 mBuilder.setChannelCount(mChannelCount);
100 mNumExchangeChannels = mChannelCount;
101 } else {
102 mBuilder.setChannelMask(javaChannelMaskToOboeChannelMask(mChannelMask));
103 mNumExchangeChannels = javaChannelMaskToChannelCount(mChannelMask);
104 }
105 mBuilder.setSampleRate(mSampleRate);
106 mBuilder.setCallback(this);
107
108 mBuilder.setSampleRateConversionQuality(SampleRateConversionQuality::None);
109 mBuilder.setDirection(Direction::Output);
110 switch (mSubtype) {
111 case SUB_TYPE_OBOE_AAUDIO:
112 mBuilder.setAudioApi(AudioApi::AAudio);
113 break;
114
115 case SUB_TYPE_OBOE_OPENSL_ES:
116 mBuilder.setAudioApi(AudioApi::OpenSLES);
117 break;
118 }
119
120 mBuilder.setPerformanceMode((PerformanceMode) performanceMode);
121 mBuilder.setSharingMode((SharingMode) sharingMode);
122
123 if (mRouteDeviceId != ROUTING_DEVICE_NONE) {
124 mBuilder.setDeviceId(mRouteDeviceId);
125 }
126
127 result = oboe::Result::OK;
128 }
129
130 if (LOG) {
131 __android_log_print(ANDROID_LOG_DEBUG, TAG, "%s() return:%d",
132 __FUNCTION__ , OboeErrorToMegaAudioError(result));
133 }
134
135 return OboeErrorToMegaAudioError(result);
136 }
137
openStream()138 StreamBase::Result OboePlayer::openStream() {
139 if (LOG) {
140 __android_log_print(ANDROID_LOG_DEBUG, TAG, "%s()", __FUNCTION__);
141 }
142 oboe::Result result = mBuilder.openStream(mAudioStream);
143 if (result != oboe::Result::OK) {
144 __android_log_print(
145 ANDROID_LOG_ERROR,
146 TAG,
147 "openStream() failed. Error: %s",convertToText(result));
148 } else {
149 // Reduce stream latency by setting the buffer size to a multiple of the burst size
150 // Note: this will fail with ErrorUnimplemented if we are using a callback with
151 // OpenSL ES. See oboe::AudioStreamBuffered::setBufferSizeInFrames
152 // This doesn't affect the success of opening the stream.
153 int32_t desiredSize = mAudioStream->getFramesPerBurst() * kBufferSizeInBursts;
154 mAudioStream->setBufferSizeInFrames(desiredSize);
155
156 mAudioSource->init(desiredSize, mNumExchangeChannels);
157 }
158
159 if (LOG) {
160 __android_log_print(ANDROID_LOG_DEBUG, TAG, "%s() return:%d",
161 __FUNCTION__, OboeErrorToMegaAudioError(result));
162 }
163 return OboeErrorToMegaAudioError(result);
164 }
165
startStream()166 StreamBase::Result OboePlayer::startStream() {
167 if (LOG) {
168 __android_log_print(ANDROID_LOG_DEBUG, TAG, "%s()", __FUNCTION__);
169 }
170 StreamBase::Result result = Player::startStream();
171 if (LOG) {
172 __android_log_print(ANDROID_LOG_DEBUG, TAG, "%s() return:%d",
173 __FUNCTION__, result);
174 }
175 return result;
176 }
177
getJavaTimestamp(jobject timestampObj)178 bool OboePlayer::getJavaTimestamp(jobject timestampObj) {
179 oboe::FrameTimestamp nativeStamp;
180 StreamBase::Result result = Player::getTimeStamp(&nativeStamp);
181 if (result == OK) {
182 JNIEnv* env;
183 mJvm->AttachCurrentThread(&env, NULL);
184
185 env->SetLongField(timestampObj, mFidFramePosition, nativeStamp.position);
186 env->SetLongField(timestampObj, mFidNanoTime, nativeStamp.timestamp);
187 }
188
189 return result == OK;
190 }
191
getLastErrorCallbackResult()192 int OboePlayer::getLastErrorCallbackResult() {
193 return (int)(mAudioStream->getLastErrorCallbackResult());
194 }
195
196 //
197 // JNI functions
198 //
199 #include <jni.h>
200
201 extern "C" {
202 JNIEXPORT JNICALL jlong
Java_org_hyphonate_megaaudio_player_OboePlayer_allocNativePlayer(JNIEnv * env,jobject thiz,jlong native_audio_source,jint playerSubtype)203 Java_org_hyphonate_megaaudio_player_OboePlayer_allocNativePlayer(
204 JNIEnv *env, jobject thiz, jlong native_audio_source, jint playerSubtype) {
205
206 return (jlong)new OboePlayer(env, (AudioSource*)native_audio_source, playerSubtype);
207 }
208
Java_org_hyphonate_megaaudio_player_OboePlayer_buildStreamN(JNIEnv * env,jobject thiz,jlong native_player,jint channel_count,jint channel_mask,jint sample_rate,jint performanceMode,jint sharingMode,jint routeDeviceId)209 JNIEXPORT jint JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_buildStreamN(
210 JNIEnv *env, jobject thiz, jlong native_player,
211 jint channel_count, jint channel_mask, jint sample_rate, jint performanceMode,
212 jint sharingMode, jint routeDeviceId) {
213
214 OboePlayer* player = (OboePlayer*)native_player;
215 return player->buildStream(channel_count, channel_mask, sample_rate, performanceMode,
216 sharingMode, routeDeviceId);
217 }
218
Java_org_hyphonate_megaaudio_player_OboePlayer_openStreamN(JNIEnv * env,jobject thiz,jlong native_player)219 JNIEXPORT jint JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_openStreamN(
220 JNIEnv *env, jobject thiz, jlong native_player) {
221
222 OboePlayer* player = (OboePlayer*)native_player;
223 return player->openStream();
224 }
225
Java_org_hyphonate_megaaudio_player_OboePlayer_teardownStreamN(JNIEnv * env,jobject thiz,jlong native_player)226 JNIEXPORT int JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_teardownStreamN(
227 JNIEnv *env, jobject thiz, jlong native_player) {
228
229 OboePlayer* player = (OboePlayer*)native_player;
230 return player->teardownStream();
231 }
232
Java_org_hyphonate_megaaudio_player_OboePlayer_startStreamN(JNIEnv * env,jobject thiz,jlong native_player,jint playerSubtype)233 JNIEXPORT JNICALL jint Java_org_hyphonate_megaaudio_player_OboePlayer_startStreamN(
234 JNIEnv *env, jobject thiz, jlong native_player, jint playerSubtype) {
235
236 return ((OboePlayer*)(native_player))->startStream();
237 }
238
239 JNIEXPORT JNICALL jint
Java_org_hyphonate_megaaudio_player_OboePlayer_stopStreamN(JNIEnv * env,jobject thiz,jlong native_player)240 Java_org_hyphonate_megaaudio_player_OboePlayer_stopStreamN(JNIEnv *env, jobject thiz,
241 jlong native_player) {
242
243 return ((OboePlayer*)(native_player))->stopStream();
244 }
245
246 JNIEXPORT JNICALL jint
Java_org_hyphonate_megaaudio_player_OboePlayer_closeStreamN(JNIEnv * env,jobject thiz,jlong native_player)247 Java_org_hyphonate_megaaudio_player_OboePlayer_closeStreamN(JNIEnv *env, jobject thiz,
248 jlong native_player) {
249
250 return ((OboePlayer*)(native_player))->closeStream();
251 }
252
253 JNIEXPORT jint JNICALL
Java_org_hyphonate_megaaudio_player_OboePlayer_getBufferFrameCountN(JNIEnv * env,jobject thiz,jlong native_player)254 Java_org_hyphonate_megaaudio_player_OboePlayer_getBufferFrameCountN(JNIEnv *env, jobject thiz,
255 jlong native_player) {
256 return ((OboePlayer*)(native_player))->getNumBufferFrames();
257 }
258
Java_org_hyphonate_megaaudio_player_OboePlayer_getRoutedDeviceIdN(JNIEnv * env,jobject thiz,jlong native_player)259 JNIEXPORT jint JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_getRoutedDeviceIdN(
260 JNIEnv *env, jobject thiz, jlong native_player) {
261 return ((OboePlayer*)(native_player))->getRoutedDeviceId();
262 }
263
Java_org_hyphonate_megaaudio_player_OboePlayer_getSharingModeN(JNIEnv * env,jobject thiz,jlong native_player)264 JNIEXPORT jint JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_getSharingModeN(
265 JNIEnv *env, jobject thiz, jlong native_player) {
266 return ((OboePlayer*)(native_player))->getSharingMode();
267 }
268
Java_org_hyphonate_megaaudio_player_OboePlayer_getChannelCountN(JNIEnv * env,jobject thiz,jlong native_player)269 JNIEXPORT jint JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_getChannelCountN(
270 JNIEnv *env, jobject thiz, jlong native_player) {
271 return ((OboePlayer*)(native_player))->getChannelCount();
272 }
273
Java_org_hyphonate_megaaudio_player_OboePlayer_isMMapN(JNIEnv * env,jobject thiz,jlong native_player)274 JNIEXPORT jboolean JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_isMMapN(
275 JNIEnv *env, jobject thiz, jlong native_player) {
276 return ((OboePlayer*)(native_player))->isMMap();
277 }
278
Java_org_hyphonate_megaaudio_player_OboePlayer_getTimestampN(JNIEnv * env,jobject thiz,jlong native_player,jobject timestamp)279 JNIEXPORT jboolean JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_getTimestampN(
280 JNIEnv *env, jobject thiz, jlong native_player, jobject timestamp) {
281 return ((OboePlayer*)native_player)->getJavaTimestamp(timestamp);
282 }
283
Java_org_hyphonate_megaaudio_player_OboePlayer_getStreamStateN(JNIEnv * env,jobject thiz,jlong native_player)284 JNIEXPORT jint JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_getStreamStateN(
285 JNIEnv *env, jobject thiz, jlong native_player) {
286 return (int)((OboePlayer*)(native_player))->getState();
287 }
288
Java_org_hyphonate_megaaudio_player_OboePlayer_getLastErrorCallbackResultN(JNIEnv * env,jobject thiz,jlong native_player)289 JNIEXPORT jint JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_getLastErrorCallbackResultN(
290 JNIEnv *env, jobject thiz, jlong native_player) {
291 return (int)((OboePlayer*)(native_player))->getLastErrorCallbackResult();
292 }
293
294 } // extern "C"
295