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