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