blob: 3b60922698496661643e3360789dad4d0dbf505d [file] [log] [blame]
Hyundo Moon660a74e2017-12-13 11:29:45 +09001/*
2 * Copyright 2018 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
17#define LOG_TAG "JAudioTrack"
18
19#include "media/JAudioAttributes.h"
20#include "media/JAudioFormat.h"
21#include "media/JAudioTrack.h"
22
Hyundo Moon9b26e942017-12-14 10:46:54 +090023#include <android_media_AudioErrors.h>
Hyundo Moon660a74e2017-12-13 11:29:45 +090024#include <android_runtime/AndroidRuntime.h>
25#include <media/AudioResamplerPublic.h>
26
27namespace android {
28
Hyundo Moon9b26e942017-12-14 10:46:54 +090029// TODO: Store Java class/methodID as a member variable in the class.
Hyundo Moon660a74e2017-12-13 11:29:45 +090030// TODO: Add NULL && Exception checks after every JNI call.
31JAudioTrack::JAudioTrack( // < Usages of the arguments are below >
32 audio_stream_type_t streamType, // AudioAudioAttributes
33 uint32_t sampleRate, // AudioFormat && bufferSizeInBytes
34 audio_format_t format, // AudioFormat && bufferSizeInBytes
35 audio_channel_mask_t channelMask, // AudioFormat && bufferSizeInBytes
36 size_t frameCount, // bufferSizeInBytes
37 audio_session_t sessionId, // AudioTrack
38 const audio_attributes_t* pAttributes, // AudioAttributes
39 float maxRequiredSpeed) { // bufferSizeInBytes
40
41 JNIEnv *env = AndroidRuntime::getJNIEnv();
42 jclass jAudioTrackCls = env->FindClass("android/media/AudioTrack");
Hyundo Moon9b26e942017-12-14 10:46:54 +090043 mAudioTrackCls = (jclass) env->NewGlobalRef(jAudioTrackCls);
Hyundo Moon660a74e2017-12-13 11:29:45 +090044
45 maxRequiredSpeed = std::min(std::max(maxRequiredSpeed, 1.0f), AUDIO_TIMESTRETCH_SPEED_MAX);
46
47 int bufferSizeInBytes = 0;
48 if (sampleRate == 0 || frameCount > 0) {
49 // Manually calculate buffer size.
50 bufferSizeInBytes = audio_channel_count_from_out_mask(channelMask)
51 * audio_bytes_per_sample(format) * (frameCount > 0 ? frameCount : 1);
52 } else if (sampleRate > 0) {
53 // Call Java AudioTrack::getMinBufferSize().
54 jmethodID jGetMinBufferSize =
Hyundo Moon9b26e942017-12-14 10:46:54 +090055 env->GetStaticMethodID(mAudioTrackCls, "getMinBufferSize", "(III)I");
56 bufferSizeInBytes = env->CallStaticIntMethod(mAudioTrackCls, jGetMinBufferSize,
Hyundo Moon660a74e2017-12-13 11:29:45 +090057 sampleRate, outChannelMaskFromNative(channelMask), audioFormatFromNative(format));
58 }
59 bufferSizeInBytes = (int) (bufferSizeInBytes * maxRequiredSpeed);
60
61 // Create a Java AudioTrack object through its Builder.
62 jclass jBuilderCls = env->FindClass("android/media/AudioTrack$Builder");
63 jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
64 jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
65
66 jmethodID jSetAudioAttributes = env->GetMethodID(jBuilderCls, "setAudioAttributes",
67 "(Landroid/media/AudioAttributes;)Landroid/media/AudioTrack$Builder;");
68 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioAttributes,
69 JAudioAttributes::createAudioAttributesObj(env, pAttributes, streamType));
70
71 jmethodID jSetAudioFormat = env->GetMethodID(jBuilderCls, "setAudioFormat",
72 "(Landroid/media/AudioFormat;)Landroid/media/AudioTrack$Builder;");
73 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioFormat,
74 JAudioFormat::createAudioFormatObj(env, sampleRate, format, channelMask));
75
76 jmethodID jSetBufferSizeInBytes = env->GetMethodID(jBuilderCls, "setBufferSizeInBytes",
77 "(I)Landroid/media/AudioTrack$Builder;");
78 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetBufferSizeInBytes, bufferSizeInBytes);
79
80 // We only use streaming mode of Java AudioTrack.
Hyundo Moon9b26e942017-12-14 10:46:54 +090081 jfieldID jModeStream = env->GetStaticFieldID(mAudioTrackCls, "MODE_STREAM", "I");
82 jint transferMode = env->GetStaticIntField(mAudioTrackCls, jModeStream);
Hyundo Moon660a74e2017-12-13 11:29:45 +090083 jmethodID jSetTransferMode = env->GetMethodID(jBuilderCls, "setTransferMode",
84 "(I)Landroid/media/AudioTrack$Builder;");
85 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetTransferMode,
86 transferMode /* Java AudioTrack::MODE_STREAM */);
87
88 if (sessionId != 0) {
89 jmethodID jSetSessionId = env->GetMethodID(jBuilderCls, "setSessionId",
90 "(I)Landroid/media/AudioTrack$Builder;");
91 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetSessionId, sessionId);
92 }
93
94 jmethodID jBuild = env->GetMethodID(jBuilderCls, "build", "()Landroid/media/AudioTrack;");
95 mAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
96}
97
Hyundo Moon9b26e942017-12-14 10:46:54 +090098JAudioTrack::~JAudioTrack() {
99 JNIEnv *env = AndroidRuntime::getJNIEnv();
100 env->DeleteGlobalRef(mAudioTrackCls);
101}
102
103size_t JAudioTrack::frameCount() {
104 JNIEnv *env = AndroidRuntime::getJNIEnv();
105 jmethodID jGetBufferSizeInFrames = env->GetMethodID(
106 mAudioTrackCls, "getBufferSizeInFrames", "()I");
107 return env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames);
108}
109
110size_t JAudioTrack::channelCount() {
111 JNIEnv *env = AndroidRuntime::getJNIEnv();
112 jmethodID jGetChannelCount = env->GetMethodID(mAudioTrackCls, "getChannelCount", "()I");
113 return env->CallIntMethod(mAudioTrackObj, jGetChannelCount);
114}
115
116status_t JAudioTrack::getPosition(uint32_t *position) {
117 if (position == NULL) {
118 return BAD_VALUE;
119 }
120
121 JNIEnv *env = AndroidRuntime::getJNIEnv();
122 jmethodID jGetPlaybackHeadPosition = env->GetMethodID(
123 mAudioTrackCls, "getPlaybackHeadPosition", "()I");
124 *position = env->CallIntMethod(mAudioTrackObj, jGetPlaybackHeadPosition);
125
126 return NO_ERROR;
127}
128
129status_t JAudioTrack::setAuxEffectSendLevel(float level) {
130 JNIEnv *env = AndroidRuntime::getJNIEnv();
131 jmethodID jSetAuxEffectSendLevel = env->GetMethodID(
132 mAudioTrackCls, "setAuxEffectSendLevel", "(F)I");
133 int result = env->CallIntMethod(mAudioTrackObj, jSetAuxEffectSendLevel, level);
134 return javaToNativeStatus(result);
135}
136
137status_t JAudioTrack::attachAuxEffect(int effectId) {
138 JNIEnv *env = AndroidRuntime::getJNIEnv();
139 jmethodID jAttachAuxEffect = env->GetMethodID(mAudioTrackCls, "attachAuxEffect", "(I)I");
140 int result = env->CallIntMethod(mAudioTrackObj, jAttachAuxEffect, effectId);
141 return javaToNativeStatus(result);
142}
143
144status_t JAudioTrack::setVolume(float left, float right) {
145 JNIEnv *env = AndroidRuntime::getJNIEnv();
146 // TODO: Java setStereoVolume is deprecated. Do we really need this method?
147 jmethodID jSetStereoVolume = env->GetMethodID(mAudioTrackCls, "setStereoVolume", "(FF)I");
148 int result = env->CallIntMethod(mAudioTrackObj, jSetStereoVolume, left, right);
149 return javaToNativeStatus(result);
150}
151
152status_t JAudioTrack::setVolume(float volume) {
153 JNIEnv *env = AndroidRuntime::getJNIEnv();
154 jmethodID jSetVolume = env->GetMethodID(mAudioTrackCls, "setVolume", "(F)I");
155 int result = env->CallIntMethod(mAudioTrackObj, jSetVolume, volume);
156 return javaToNativeStatus(result);
157}
158
159status_t JAudioTrack::start() {
160 JNIEnv *env = AndroidRuntime::getJNIEnv();
161 jmethodID jPlay = env->GetMethodID(mAudioTrackCls, "play", "()V");
162 // TODO: Should we catch the Java IllegalStateException from play()?
163 env->CallVoidMethod(mAudioTrackObj, jPlay);
164 return NO_ERROR;
165}
166
Hyundo Moon660a74e2017-12-13 11:29:45 +0900167void JAudioTrack::stop() {
168 JNIEnv *env = AndroidRuntime::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900169 jmethodID jStop = env->GetMethodID(mAudioTrackCls, "stop", "()V");
Hyundo Moon660a74e2017-12-13 11:29:45 +0900170 env->CallVoidMethod(mAudioTrackObj, jStop);
Hyundo Moon9b26e942017-12-14 10:46:54 +0900171 // TODO: Should we catch IllegalStateException?
172}
173
174// TODO: Is the right implementation?
175bool JAudioTrack::stopped() const {
176 return !isPlaying();
177}
178
179void JAudioTrack::flush() {
180 JNIEnv *env = AndroidRuntime::getJNIEnv();
181 jmethodID jFlush = env->GetMethodID(mAudioTrackCls, "flush", "()V");
182 env->CallVoidMethod(mAudioTrackObj, jFlush);
183}
184
185void JAudioTrack::pause() {
186 JNIEnv *env = AndroidRuntime::getJNIEnv();
187 jmethodID jPause = env->GetMethodID(mAudioTrackCls, "pause", "()V");
188 env->CallVoidMethod(mAudioTrackObj, jPause);
189 // TODO: Should we catch IllegalStateException?
190}
191
192bool JAudioTrack::isPlaying() const {
193 JNIEnv *env = AndroidRuntime::getJNIEnv();
194 jmethodID jGetPlayState = env->GetMethodID(mAudioTrackCls, "getPlayState", "()I");
195 int currentPlayState = env->CallIntMethod(mAudioTrackObj, jGetPlayState);
196
197 // TODO: In Java AudioTrack, there is no STOPPING state.
198 // This means while stopping, isPlaying() will return different value in two class.
199 // - in existing native AudioTrack: true
200 // - in JAudioTrack: false
201 // If not okay, also modify the implementation of stopped().
202 jfieldID jPlayStatePlaying = env->GetStaticFieldID(mAudioTrackCls, "PLAYSTATE_PLAYING", "I");
203 int statePlaying = env->GetStaticIntField(mAudioTrackCls, jPlayStatePlaying);
204 return currentPlayState == statePlaying;
205}
206
207uint32_t JAudioTrack::getSampleRate() {
208 JNIEnv *env = AndroidRuntime::getJNIEnv();
209 jmethodID jGetSampleRate = env->GetMethodID(mAudioTrackCls, "getSampleRate", "()I");
210 return env->CallIntMethod(mAudioTrackObj, jGetSampleRate);
211}
212
213audio_format_t JAudioTrack::format() {
214 JNIEnv *env = AndroidRuntime::getJNIEnv();
215 jmethodID jGetAudioFormat = env->GetMethodID(mAudioTrackCls, "getAudioFormat", "()I");
216 int javaFormat = env->CallIntMethod(mAudioTrackObj, jGetAudioFormat);
217 return audioFormatToNative(javaFormat);
218}
219
220status_t JAudioTrack::javaToNativeStatus(int javaStatus) {
221 switch (javaStatus) {
222 case AUDIO_JAVA_SUCCESS:
223 return NO_ERROR;
224 case AUDIO_JAVA_BAD_VALUE:
225 return BAD_VALUE;
226 case AUDIO_JAVA_INVALID_OPERATION:
227 return INVALID_OPERATION;
228 case AUDIO_JAVA_PERMISSION_DENIED:
229 return PERMISSION_DENIED;
230 case AUDIO_JAVA_NO_INIT:
231 return NO_INIT;
232 case AUDIO_JAVA_WOULD_BLOCK:
233 return WOULD_BLOCK;
234 case AUDIO_JAVA_DEAD_OBJECT:
235 return DEAD_OBJECT;
236 default:
237 return UNKNOWN_ERROR;
238 }
Hyundo Moon660a74e2017-12-13 11:29:45 +0900239}
240
241} // namespace android