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 return ERROR_INVALID_STATE;
81 } else {
82 std::lock_guard<std::mutex> lock(mStreamLock);
83
84 mChannelCount = channelCount;
85 mChannelMask = channelMask;
86
87 mSampleRate = sampleRate;
88 mRouteDeviceId = routeDeviceId;
89
90 // Create an audio stream
91 AudioStreamBuilder builder;
92 if (mChannelCount != 0) {
93 builder.setChannelCount(mChannelCount);
94 mNumExchangeChannels = mChannelCount;
95 } else {
96 builder.setChannelMask(javaChannelMaskToOboeChannelMask(mChannelMask));
97 mNumExchangeChannels = javaChannelMaskToChannelCount(mChannelMask);
98 }
99 builder.setSampleRate(mSampleRate);
100 builder.setCallback(this);
101
102 builder.setSampleRateConversionQuality(SampleRateConversionQuality::None);
103 builder.setDirection(Direction::Output);
104 switch (mSubtype) {
105 case SUB_TYPE_OBOE_AAUDIO:
106 builder.setAudioApi(AudioApi::AAudio);
107 break;
108
109 case SUB_TYPE_OBOE_OPENSL_ES:
110 builder.setAudioApi(AudioApi::OpenSLES);
111 break;
112 }
113
114 builder.setPerformanceMode((PerformanceMode) performanceMode);
115 builder.setSharingMode((SharingMode) sharingMode);
116
117 if (mRouteDeviceId != ROUTING_DEVICE_NONE) {
118 builder.setDeviceId(mRouteDeviceId);
119 }
120
121 result = builder.openStream(mAudioStream);
122 if (result != oboe::Result::OK){
123 __android_log_print(
124 ANDROID_LOG_ERROR,
125 TAG,
126 "openStream failed. Error: %s", convertToText(result));
127 } else {
128 // Reduce stream latency by setting the buffer size to a multiple of the burst size
129 // Note: this will fail with ErrorUnimplemented if we are using a callback with
130 // OpenSL ES. See oboe::AudioStreamBuffered::setBufferSizeInFrames
131 // This doesn't affect the success of opening the stream.
132 int32_t desiredSize = mAudioStream->getFramesPerBurst() * kBufferSizeInBursts;
133 mAudioStream->setBufferSizeInFrames(desiredSize);
134
135 mAudioSource->init(desiredSize , mNumExchangeChannels);
136 }
137 }
138 __android_log_print(ANDROID_LOG_INFO, TAG, " Done - error:%d", static_cast<int>(result));
139 return OboeErrorToMegaAudioError(result);
140 }
141
startStream()142 StreamBase::Result OboePlayer::startStream() {
143 StreamBase::Result result = Player::startStream();
144
145 return result;
146 }
147
getJavaTimestamp(jobject timestampObj)148 bool OboePlayer::getJavaTimestamp(jobject timestampObj) {
149 oboe::FrameTimestamp nativeStamp;
150 StreamBase::Result result = Player::getTimeStamp(&nativeStamp);
151 if (result == OK) {
152 JNIEnv* env;
153 mJvm->AttachCurrentThread(&env, NULL);
154
155 env->SetLongField(timestampObj, mFidFramePosition, nativeStamp.position);
156 env->SetLongField(timestampObj, mFidNanoTime, nativeStamp.timestamp);
157 }
158
159 return result == OK;
160 }
161
getLastErrorCallbackResult()162 int OboePlayer::getLastErrorCallbackResult() {
163 return (int)(mAudioStream->getLastErrorCallbackResult());
164 }
165
166 //
167 // JNI functions
168 //
169 #include <jni.h>
170
171 extern "C" {
172 JNIEXPORT JNICALL jlong
Java_org_hyphonate_megaaudio_player_OboePlayer_allocNativePlayer(JNIEnv * env,jobject thiz,jlong native_audio_source,jint playerSubtype)173 Java_org_hyphonate_megaaudio_player_OboePlayer_allocNativePlayer(
174 JNIEnv *env, jobject thiz, jlong native_audio_source, jint playerSubtype) {
175
176 return (jlong)new OboePlayer(env, (AudioSource*)native_audio_source, playerSubtype);
177 }
178
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)179 JNIEXPORT jint JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_setupStreamN(
180 JNIEnv *env, jobject thiz, jlong native_player,
181 jint channel_count, jint channel_mask, jint sample_rate, jint performanceMode,
182 jint sharingMode, jint routeDeviceId) {
183
184 OboePlayer* player = (OboePlayer*)native_player;
185 return player->setupStream(channel_count, channel_mask, sample_rate, performanceMode,
186 sharingMode, routeDeviceId);
187 }
188
Java_org_hyphonate_megaaudio_player_OboePlayer_teardownStreamN(JNIEnv * env,jobject thiz,jlong native_player)189 JNIEXPORT int JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_teardownStreamN(
190 JNIEnv *env, jobject thiz, jlong native_player) {
191
192 OboePlayer* player = (OboePlayer*)native_player;
193 return player->teardownStream();
194 }
195
Java_org_hyphonate_megaaudio_player_OboePlayer_startStreamN(JNIEnv * env,jobject thiz,jlong native_player,jint playerSubtype)196 JNIEXPORT JNICALL jint Java_org_hyphonate_megaaudio_player_OboePlayer_startStreamN(
197 JNIEnv *env, jobject thiz, jlong native_player, jint playerSubtype) {
198
199 return ((OboePlayer*)(native_player))->startStream();
200 }
201
202 JNIEXPORT JNICALL jint
Java_org_hyphonate_megaaudio_player_OboePlayer_stopN(JNIEnv * env,jobject thiz,jlong native_player)203 Java_org_hyphonate_megaaudio_player_OboePlayer_stopN(JNIEnv *env, jobject thiz,
204 jlong native_player) {
205
206 return ((OboePlayer*)(native_player))->stopStream();
207 }
208
209 JNIEXPORT jint JNICALL
Java_org_hyphonate_megaaudio_player_OboePlayer_getBufferFrameCountN(JNIEnv * env,jobject thiz,jlong native_player)210 Java_org_hyphonate_megaaudio_player_OboePlayer_getBufferFrameCountN(JNIEnv *env, jobject thiz,
211 jlong native_player) {
212 return ((OboePlayer*)(native_player))->getNumBufferFrames();
213 }
214
Java_org_hyphonate_megaaudio_player_OboePlayer_getRoutedDeviceIdN(JNIEnv * env,jobject thiz,jlong native_player)215 JNIEXPORT jint JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_getRoutedDeviceIdN(
216 JNIEnv *env, jobject thiz, jlong native_player) {
217 return ((OboePlayer*)(native_player))->getRoutedDeviceId();
218 }
219
Java_org_hyphonate_megaaudio_player_OboePlayer_getSharingModeN(JNIEnv * env,jobject thiz,jlong native_player)220 JNIEXPORT jint JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_getSharingModeN(
221 JNIEnv *env, jobject thiz, jlong native_player) {
222 return ((OboePlayer*)(native_player))->getSharingMode();
223 }
224
Java_org_hyphonate_megaaudio_player_OboePlayer_getChannelCountN(JNIEnv * env,jobject thiz,jlong native_player)225 JNIEXPORT jint JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_getChannelCountN(
226 JNIEnv *env, jobject thiz, jlong native_player) {
227 return ((OboePlayer*)(native_player))->getChannelCount();
228 }
229
Java_org_hyphonate_megaaudio_player_OboePlayer_isMMapN(JNIEnv * env,jobject thiz,jlong native_player)230 JNIEXPORT jboolean JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_isMMapN(
231 JNIEnv *env, jobject thiz, jlong native_player) {
232 return ((OboePlayer*)(native_player))->isMMap();
233 }
234
Java_org_hyphonate_megaaudio_player_OboePlayer_getTimestampN(JNIEnv * env,jobject thiz,jlong native_player,jobject timestamp)235 JNIEXPORT jboolean JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_getTimestampN(
236 JNIEnv *env, jobject thiz, jlong native_player, jobject timestamp) {
237 return ((OboePlayer*)native_player)->getJavaTimestamp(timestamp);
238 }
239
Java_org_hyphonate_megaaudio_player_OboePlayer_getStreamStateN(JNIEnv * env,jobject thiz,jlong native_player)240 JNIEXPORT jint JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_getStreamStateN(
241 JNIEnv *env, jobject thiz, jlong native_player) {
242 return (int)((OboePlayer*)(native_player))->getState();
243 }
244
Java_org_hyphonate_megaaudio_player_OboePlayer_getLastErrorCallbackResultN(JNIEnv * env,jobject thiz,jlong native_player)245 JNIEXPORT jint JNICALL Java_org_hyphonate_megaaudio_player_OboePlayer_getLastErrorCallbackResultN(
246 JNIEnv *env, jobject thiz, jlong native_player) {
247 return (int)((OboePlayer*)(native_player))->getLastErrorCallbackResult();
248 }
249
250 } // extern "C"
251