blob: b64505cd265e8d90ccfed38ea9dfb988232edf9d [file] [log] [blame]
Eric Laurentdf9b81c2010-07-02 08:12:41 -07001/*
2 * Copyright (C) 2010 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#include <stdio.h>
18
19//#define LOG_NDEBUG 0
20#define LOG_TAG "visualizers-JNI"
21
22#include <utils/Log.h>
23#include <nativehelper/jni.h>
24#include <nativehelper/JNIHelp.h>
25#include <android_runtime/AndroidRuntime.h>
26#include "media/Visualizer.h"
27
28using namespace android;
29
30#define VISUALIZER_SUCCESS 0
31#define VISUALIZER_ERROR -1
32#define VISUALIZER_ERROR_ALREADY_EXISTS -2
33#define VISUALIZER_ERROR_NO_INIT -3
34#define VISUALIZER_ERROR_BAD_VALUE -4
35#define VISUALIZER_ERROR_INVALID_OPERATION -5
36#define VISUALIZER_ERROR_NO_MEMORY -6
37#define VISUALIZER_ERROR_DEAD_OBJECT -7
38
39#define NATIVE_EVENT_PCM_CAPTURE 0
40#define NATIVE_EVENT_FFT_CAPTURE 1
41
42// ----------------------------------------------------------------------------
Eric Laurent1a5149e2010-09-21 18:18:20 -070043static const char* const kClassPathName = "android/media/audiofx/Visualizer";
Eric Laurentdf9b81c2010-07-02 08:12:41 -070044
45struct fields_t {
46 // these fields provide access from C++ to the...
47 jclass clazzEffect; // Visualizer class
48 jmethodID midPostNativeEvent; // event post callback method
49 jfieldID fidNativeVisualizer; // stores in Java the native Visualizer object
50 jfieldID fidJniData; // stores in Java additional resources used by the native Visualizer
51};
52static fields_t fields;
53
54struct visualizer_callback_cookie {
55 jclass visualizer_class; // Visualizer class
56 jobject visualizer_ref; // Visualizer object instance
57 };
58
59// ----------------------------------------------------------------------------
60class visualizerJniStorage {
61 public:
62 visualizer_callback_cookie mCallbackData;
63
64 visualizerJniStorage() {
65 }
66
67 ~visualizerJniStorage() {
68 }
69
70};
71
72
73static jint translateError(int code) {
74 switch(code) {
75 case NO_ERROR:
76 return VISUALIZER_SUCCESS;
77 case ALREADY_EXISTS:
78 return VISUALIZER_ERROR_ALREADY_EXISTS;
79 case NO_INIT:
80 return VISUALIZER_ERROR_NO_INIT;
81 case BAD_VALUE:
82 return VISUALIZER_ERROR_BAD_VALUE;
83 case INVALID_OPERATION:
84 return VISUALIZER_ERROR_INVALID_OPERATION;
85 case NO_MEMORY:
86 return VISUALIZER_ERROR_NO_MEMORY;
87 case DEAD_OBJECT:
88 return VISUALIZER_ERROR_DEAD_OBJECT;
89 default:
90 return VISUALIZER_ERROR;
91 }
92}
93
94
95// ----------------------------------------------------------------------------
96static void captureCallback(void* user,
97 uint32_t waveformSize,
98 uint8_t *waveform,
99 uint32_t fftSize,
100 uint8_t *fft,
101 uint32_t samplingrate) {
102
103 int arg1 = 0;
104 int arg2 = 0;
105 size_t size;
106
107 visualizer_callback_cookie *callbackInfo = (visualizer_callback_cookie *)user;
108 JNIEnv *env = AndroidRuntime::getJNIEnv();
109
Steve Block71f2cf12011-10-20 11:56:00 +0100110 ALOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700111 callbackInfo,
112 callbackInfo->visualizer_ref,
113 callbackInfo->visualizer_class);
114
115 if (!user || !env) {
116 LOGW("captureCallback error user %p, env %p", user, env);
117 return;
118 }
119
120 if (waveformSize != 0 && waveform != NULL) {
121 jbyteArray jArray = env->NewByteArray(waveformSize);
122 if (jArray != NULL) {
123 jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
124 memcpy(nArray, waveform, waveformSize);
125 env->ReleaseByteArrayElements(jArray, nArray, 0);
126 env->CallStaticVoidMethod(
127 callbackInfo->visualizer_class,
128 fields.midPostNativeEvent,
129 callbackInfo->visualizer_ref,
130 NATIVE_EVENT_PCM_CAPTURE,
131 samplingrate,
132 0,
133 jArray);
Eric Laurentcc02ac52010-10-22 18:03:35 -0700134 env->DeleteLocalRef(jArray);
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700135 }
136 }
137
138 if (fftSize != 0 && fft != NULL) {
139 jbyteArray jArray = env->NewByteArray(fftSize);
140 if (jArray != NULL) {
141 jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
142 memcpy(nArray, fft, fftSize);
143 env->ReleaseByteArrayElements(jArray, nArray, 0);
144 env->CallStaticVoidMethod(
145 callbackInfo->visualizer_class,
146 fields.midPostNativeEvent,
147 callbackInfo->visualizer_ref,
148 NATIVE_EVENT_FFT_CAPTURE,
149 samplingrate,
150 0,
151 jArray);
152 env->DeleteLocalRef(jArray);
153 }
154 }
155
156 if (env->ExceptionCheck()) {
157 env->ExceptionDescribe();
158 env->ExceptionClear();
159 }
160}
161
162static Visualizer *getVisualizer(JNIEnv* env, jobject thiz)
163{
164 Visualizer *v = (Visualizer *)env->GetIntField(
165 thiz, fields.fidNativeVisualizer);
166 if (v == NULL) {
167 jniThrowException(env, "java/lang/IllegalStateException",
168 "Unable to retrieve Visualizer pointer");
169 }
170 return v;
171}
172
173// ----------------------------------------------------------------------------
174// This function gets some field IDs, which in turn causes class initialization.
175// It is called from a static block in Visualizer, which won't run until the
176// first time an instance of this class is used.
177static void
178android_media_visualizer_native_init(JNIEnv *env)
179{
180
Steve Block71f2cf12011-10-20 11:56:00 +0100181 ALOGV("android_media_visualizer_native_init");
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700182
183 fields.clazzEffect = NULL;
184
185 // Get the Visualizer class
186 jclass clazz = env->FindClass(kClassPathName);
187 if (clazz == NULL) {
188 LOGE("Can't find %s", kClassPathName);
189 return;
190 }
191
192 fields.clazzEffect = (jclass)env->NewGlobalRef(clazz);
193
194 // Get the postEvent method
195 fields.midPostNativeEvent = env->GetStaticMethodID(
196 fields.clazzEffect,
197 "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
198 if (fields.midPostNativeEvent == NULL) {
199 LOGE("Can't find Visualizer.%s", "postEventFromNative");
200 return;
201 }
202
203 // Get the variables fields
204 // nativeTrackInJavaObj
205 fields.fidNativeVisualizer = env->GetFieldID(
206 fields.clazzEffect,
207 "mNativeVisualizer", "I");
208 if (fields.fidNativeVisualizer == NULL) {
209 LOGE("Can't find Visualizer.%s", "mNativeVisualizer");
210 return;
211 }
212 // fidJniData;
213 fields.fidJniData = env->GetFieldID(
214 fields.clazzEffect,
215 "mJniData", "I");
216 if (fields.fidJniData == NULL) {
217 LOGE("Can't find Visualizer.%s", "mJniData");
218 return;
219 }
220
221}
222
223
224static jint
225android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
226 jint sessionId, jintArray jId)
227{
Steve Block71f2cf12011-10-20 11:56:00 +0100228 ALOGV("android_media_visualizer_native_setup");
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700229 visualizerJniStorage* lpJniStorage = NULL;
230 int lStatus = VISUALIZER_ERROR_NO_MEMORY;
231 Visualizer* lpVisualizer = NULL;
232 jint* nId = NULL;
233
234 lpJniStorage = new visualizerJniStorage();
235 if (lpJniStorage == NULL) {
236 LOGE("setup: Error creating JNI Storage");
237 goto setup_failure;
238 }
239
240 lpJniStorage->mCallbackData.visualizer_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
241 // we use a weak reference so the Visualizer object can be garbage collected.
242 lpJniStorage->mCallbackData.visualizer_ref = env->NewGlobalRef(weak_this);
243
Steve Block71f2cf12011-10-20 11:56:00 +0100244 ALOGV("setup: lpJniStorage: %p visualizer_ref %p visualizer_class %p, &mCallbackData %p",
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700245 lpJniStorage,
246 lpJniStorage->mCallbackData.visualizer_ref,
247 lpJniStorage->mCallbackData.visualizer_class,
248 &lpJniStorage->mCallbackData);
249
Eric Laurent2fb43ef2010-09-24 12:03:36 -0700250 if (jId == NULL) {
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700251 LOGE("setup: NULL java array for id pointer");
252 lStatus = VISUALIZER_ERROR_BAD_VALUE;
253 goto setup_failure;
254 }
255
256 // create the native Visualizer object
257 lpVisualizer = new Visualizer(0,
258 NULL,
259 NULL,
260 sessionId);
261 if (lpVisualizer == NULL) {
262 LOGE("Error creating Visualizer");
263 goto setup_failure;
264 }
265
266 lStatus = translateError(lpVisualizer->initCheck());
267 if (lStatus != VISUALIZER_SUCCESS && lStatus != VISUALIZER_ERROR_ALREADY_EXISTS) {
268 LOGE("Visualizer initCheck failed %d", lStatus);
269 goto setup_failure;
270 }
271
Eric Laurent2fb43ef2010-09-24 12:03:36 -0700272 nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
273 if (nId == NULL) {
274 LOGE("setup: Error retrieving id pointer");
275 lStatus = VISUALIZER_ERROR_BAD_VALUE;
276 goto setup_failure;
277 }
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700278 nId[0] = lpVisualizer->id();
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700279 env->ReleasePrimitiveArrayCritical(jId, nId, 0);
280 nId = NULL;
281
282 env->SetIntField(thiz, fields.fidNativeVisualizer, (int)lpVisualizer);
283
284 env->SetIntField(thiz, fields.fidJniData, (int)lpJniStorage);
285
286 return VISUALIZER_SUCCESS;
287
288 // failures:
289setup_failure:
290
291 if (nId != NULL) {
292 env->ReleasePrimitiveArrayCritical(jId, nId, 0);
293 }
294
295 if (lpVisualizer) {
296 delete lpVisualizer;
297 }
298 env->SetIntField(thiz, fields.fidNativeVisualizer, 0);
299
300 if (lpJniStorage) {
301 delete lpJniStorage;
302 }
303 env->SetIntField(thiz, fields.fidJniData, 0);
304
305 return lStatus;
306}
307
308// ----------------------------------------------------------------------------
309static void android_media_visualizer_native_finalize(JNIEnv *env, jobject thiz) {
Steve Block71f2cf12011-10-20 11:56:00 +0100310 ALOGV("android_media_visualizer_native_finalize jobject: %x\n", (int)thiz);
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700311
312 // delete the Visualizer object
313 Visualizer* lpVisualizer = (Visualizer *)env->GetIntField(
314 thiz, fields.fidNativeVisualizer);
315 if (lpVisualizer) {
Steve Block71f2cf12011-10-20 11:56:00 +0100316 ALOGV("deleting Visualizer: %x\n", (int)lpVisualizer);
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700317 delete lpVisualizer;
318 }
319
320 // delete the JNI data
321 visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(
322 thiz, fields.fidJniData);
323 if (lpJniStorage) {
Steve Block71f2cf12011-10-20 11:56:00 +0100324 ALOGV("deleting pJniStorage: %x\n", (int)lpJniStorage);
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700325 delete lpJniStorage;
326 }
327}
328
329// ----------------------------------------------------------------------------
330static void android_media_visualizer_native_release(JNIEnv *env, jobject thiz) {
331
332 // do everything a call to finalize would
333 android_media_visualizer_native_finalize(env, thiz);
334 // + reset the native resources in the Java object so any attempt to access
335 // them after a call to release fails.
336 env->SetIntField(thiz, fields.fidNativeVisualizer, 0);
337 env->SetIntField(thiz, fields.fidJniData, 0);
338}
339
340static jint
341android_media_visualizer_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
342{
343 Visualizer* lpVisualizer = getVisualizer(env, thiz);
344 if (lpVisualizer == NULL) {
345 return VISUALIZER_ERROR_NO_INIT;
346 }
347
348 return translateError(lpVisualizer->setEnabled(enabled));
349}
350
351static jboolean
352android_media_visualizer_native_getEnabled(JNIEnv *env, jobject thiz)
353{
354 Visualizer* lpVisualizer = getVisualizer(env, thiz);
355 if (lpVisualizer == NULL) {
356 return false;
357 }
358
359 return (jboolean)lpVisualizer->getEnabled();
360}
361
362static jintArray
363android_media_visualizer_native_getCaptureSizeRange(JNIEnv *env, jobject thiz)
364{
365 jintArray jRange = env->NewIntArray(2);
366 jint *nRange = env->GetIntArrayElements(jRange, NULL);
367 nRange[0] = Visualizer::getMinCaptureSize();
368 nRange[1] = Visualizer::getMaxCaptureSize();
Steve Block71f2cf12011-10-20 11:56:00 +0100369 ALOGV("getCaptureSizeRange() min %d max %d", nRange[0], nRange[1]);
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700370 env->ReleaseIntArrayElements(jRange, nRange, 0);
371 return jRange;
372}
373
374static jint
375android_media_visualizer_native_getMaxCaptureRate(JNIEnv *env, jobject thiz)
376{
377 return Visualizer::getMaxCaptureRate();
378}
379
380static jint
381android_media_visualizer_native_setCaptureSize(JNIEnv *env, jobject thiz, jint size)
382{
383 Visualizer* lpVisualizer = getVisualizer(env, thiz);
384 if (lpVisualizer == NULL) {
385 return VISUALIZER_ERROR_NO_INIT;
386 }
387
388 return translateError(lpVisualizer->setCaptureSize(size));
389}
390
391static jint
392android_media_visualizer_native_getCaptureSize(JNIEnv *env, jobject thiz)
393{
394 Visualizer* lpVisualizer = getVisualizer(env, thiz);
395 if (lpVisualizer == NULL) {
396 return -1;
397 }
398 return lpVisualizer->getCaptureSize();
399}
400
401static jint
402android_media_visualizer_native_getSamplingRate(JNIEnv *env, jobject thiz)
403{
404 Visualizer* lpVisualizer = getVisualizer(env, thiz);
405 if (lpVisualizer == NULL) {
406 return -1;
407 }
408 return lpVisualizer->getSamplingRate();
409}
410
411static jint
412android_media_visualizer_native_getWaveForm(JNIEnv *env, jobject thiz, jbyteArray jWaveform)
413{
414 Visualizer* lpVisualizer = getVisualizer(env, thiz);
415 if (lpVisualizer == NULL) {
416 return VISUALIZER_ERROR_NO_INIT;
417 }
418
419 jbyte* nWaveform = (jbyte *) env->GetPrimitiveArrayCritical(jWaveform, NULL);
420 if (nWaveform == NULL) {
421 return VISUALIZER_ERROR_NO_MEMORY;
422 }
423 jint status = translateError(lpVisualizer->getWaveForm((uint8_t *)nWaveform));
424
425 env->ReleasePrimitiveArrayCritical(jWaveform, nWaveform, 0);
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700426 return status;
427}
428
429static jint
430android_media_visualizer_native_getFft(JNIEnv *env, jobject thiz, jbyteArray jFft)
431{
432 Visualizer* lpVisualizer = getVisualizer(env, thiz);
433 if (lpVisualizer == NULL) {
434 return VISUALIZER_ERROR_NO_INIT;
435 }
436
437 jbyte* nFft = (jbyte *) env->GetPrimitiveArrayCritical(jFft, NULL);
438 if (nFft == NULL) {
439 return VISUALIZER_ERROR_NO_MEMORY;
440 }
441 jint status = translateError(lpVisualizer->getFft((uint8_t *)nFft));
442
443 env->ReleasePrimitiveArrayCritical(jFft, nFft, 0);
444
445 return status;
446}
447
448static jint
449android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean jWaveform, jboolean jFft)
450{
451 Visualizer* lpVisualizer = getVisualizer(env, thiz);
452 if (lpVisualizer == NULL) {
453 return VISUALIZER_ERROR_NO_INIT;
454 }
455 visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(thiz,
456 fields.fidJniData);
457 if (lpJniStorage == NULL) {
458 return VISUALIZER_ERROR_NO_INIT;
459 }
460
Steve Block71f2cf12011-10-20 11:56:00 +0100461 ALOGV("setPeriodicCapture: rate %d, jWaveform %d jFft %d",
Eric Laurentdf9b81c2010-07-02 08:12:41 -0700462 rate,
463 jWaveform,
464 jFft);
465
466 uint32_t flags = Visualizer::CAPTURE_CALL_JAVA;
467 if (jWaveform) flags |= Visualizer::CAPTURE_WAVEFORM;
468 if (jFft) flags |= Visualizer::CAPTURE_FFT;
469 Visualizer::capture_cbk_t cbk = captureCallback;
470 if (!jWaveform && !jFft) cbk = NULL;
471
472 return translateError(lpVisualizer->setCaptureCallBack(cbk,
473 &lpJniStorage->mCallbackData,
474 flags,
475 rate));
476}
477
478// ----------------------------------------------------------------------------
479
480// Dalvik VM type signatures
481static JNINativeMethod gMethods[] = {
482 {"native_init", "()V", (void *)android_media_visualizer_native_init},
483 {"native_setup", "(Ljava/lang/Object;I[I)I",
484 (void *)android_media_visualizer_native_setup},
485 {"native_finalize", "()V", (void *)android_media_visualizer_native_finalize},
486 {"native_release", "()V", (void *)android_media_visualizer_native_release},
487 {"native_setEnabled", "(Z)I", (void *)android_media_visualizer_native_setEnabled},
488 {"native_getEnabled", "()Z", (void *)android_media_visualizer_native_getEnabled},
489 {"getCaptureSizeRange", "()[I", (void *)android_media_visualizer_native_getCaptureSizeRange},
490 {"getMaxCaptureRate", "()I", (void *)android_media_visualizer_native_getMaxCaptureRate},
491 {"native_setCaptureSize", "(I)I", (void *)android_media_visualizer_native_setCaptureSize},
492 {"native_getCaptureSize", "()I", (void *)android_media_visualizer_native_getCaptureSize},
493 {"native_getSamplingRate", "()I", (void *)android_media_visualizer_native_getSamplingRate},
494 {"native_getWaveForm", "([B)I", (void *)android_media_visualizer_native_getWaveForm},
495 {"native_getFft", "([B)I", (void *)android_media_visualizer_native_getFft},
496 {"native_setPeriodicCapture","(IZZ)I",(void *)android_media_setPeriodicCapture},
497};
498
499// ----------------------------------------------------------------------------
500
501int register_android_media_visualizer(JNIEnv *env)
502{
503 return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
504}
505