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