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