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