1/*
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
17package android.media.audiofx;
18
19import android.app.ActivityThread;
20import android.util.Log;
21import java.lang.ref.WeakReference;
22import android.os.Handler;
23import android.os.Looper;
24import android.os.Message;
25
26/**
27 * The Visualizer class enables application to retrieve part of the currently playing audio for
28 * visualization purpose. It is not an audio recording interface and only returns partial and low
29 * quality audio content. However, to protect privacy of certain audio data (e.g voice mail) the use
30 * of the visualizer requires the permission android.permission.RECORD_AUDIO.
31 * <p>The audio session ID passed to the constructor indicates which audio content should be
32 * visualized:<br>
33 * <ul>
34 *   <li>If the session is 0, the audio output mix is visualized</li>
35 *   <li>If the session is not 0, the audio from a particular {@link android.media.MediaPlayer} or
36 *   {@link android.media.AudioTrack}
37 *   using this audio session is visualized </li>
38 * </ul>
39 * <p>Two types of representation of audio content can be captured: <br>
40 * <ul>
41 *   <li>Waveform data: consecutive 8-bit (unsigned) mono samples by using the
42 *   {@link #getWaveForm(byte[])} method</li>
43 *   <li>Frequency data: 8-bit magnitude FFT by using the {@link #getFft(byte[])} method</li>
44 * </ul>
45 * <p>The length of the capture can be retrieved or specified by calling respectively
46 * {@link #getCaptureSize()} and {@link #setCaptureSize(int)} methods. The capture size must be a
47 * power of 2 in the range returned by {@link #getCaptureSizeRange()}.
48 * <p>In addition to the polling capture mode described above with {@link #getWaveForm(byte[])} and
49 *  {@link #getFft(byte[])} methods, a callback mode is also available by installing a listener by
50 *  use of the {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method.
51 *  The rate at which the listener capture method is called as well as the type of data returned is
52 *  specified.
53 * <p>Before capturing data, the Visualizer must be enabled by calling the
54 * {@link #setEnabled(boolean)} method.
55 * When data capture is not needed any more, the Visualizer should be disabled.
56 * <p>It is good practice to call the {@link #release()} method when the Visualizer is not used
57 * anymore to free up native resources associated to the Visualizer instance.
58 * <p>Creating a Visualizer on the output mix (audio session 0) requires permission
59 * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
60 * <p>The Visualizer class can also be used to perform measurements on the audio being played back.
61 * The measurements to perform are defined by setting a mask of the requested measurement modes with
62 * {@link #setMeasurementMode(int)}. Supported values are {@link #MEASUREMENT_MODE_NONE} to cancel
63 * any measurement, and {@link #MEASUREMENT_MODE_PEAK_RMS} for peak and RMS monitoring.
64 * Measurements can be retrieved through {@link #getMeasurementPeakRms(MeasurementPeakRms)}.
65 */
66
67public class Visualizer {
68
69    static {
70        System.loadLibrary("audioeffect_jni");
71        native_init();
72    }
73
74    private final static String TAG = "Visualizer-JAVA";
75
76    /**
77     * State of a Visualizer object that was not successfully initialized upon creation
78     */
79    public static final int STATE_UNINITIALIZED = 0;
80    /**
81     * State of a Visualizer object that is ready to be used.
82     */
83    public static final int STATE_INITIALIZED   = 1;
84    /**
85     * State of a Visualizer object that is active.
86     */
87    public static final int STATE_ENABLED   = 2;
88
89    // to keep in sync with system/media/audio_effects/include/audio_effects/effect_visualizer.h
90    /**
91     * Defines a capture mode where amplification is applied based on the content of the captured
92     * data. This is the default Visualizer mode, and is suitable for music visualization.
93     */
94    public static final int SCALING_MODE_NORMALIZED = 0;
95    /**
96     * Defines a capture mode where the playback volume will affect (scale) the range of the
97     * captured data. A low playback volume will lead to low sample and fft values, and vice-versa.
98     */
99    public static final int SCALING_MODE_AS_PLAYED = 1;
100
101    /**
102     * Defines a measurement mode in which no measurements are performed.
103     */
104    public static final int MEASUREMENT_MODE_NONE = 0;
105
106    /**
107     * Defines a measurement mode which computes the peak and RMS value in mB, where 0mB is the
108     * maximum sample value, and -9600mB is the minimum value.
109     * Values for peak and RMS can be retrieved with
110     * {@link #getMeasurementPeakRms(MeasurementPeakRms)}.
111     */
112    public static final int MEASUREMENT_MODE_PEAK_RMS = 1 << 0;
113
114    // to keep in sync with frameworks/base/media/jni/audioeffect/android_media_Visualizer.cpp
115    private static final int NATIVE_EVENT_PCM_CAPTURE = 0;
116    private static final int NATIVE_EVENT_FFT_CAPTURE = 1;
117    private static final int NATIVE_EVENT_SERVER_DIED = 2;
118
119    // Error codes:
120    /**
121     * Successful operation.
122     */
123    public  static final int SUCCESS              = 0;
124    /**
125     * Unspecified error.
126     */
127    public  static final int ERROR                = -1;
128    /**
129     * Internal operation status. Not returned by any method.
130     */
131    public  static final int ALREADY_EXISTS       = -2;
132    /**
133     * Operation failed due to bad object initialization.
134     */
135    public  static final int ERROR_NO_INIT              = -3;
136    /**
137     * Operation failed due to bad parameter value.
138     */
139    public  static final int ERROR_BAD_VALUE            = -4;
140    /**
141     * Operation failed because it was requested in wrong state.
142     */
143    public  static final int ERROR_INVALID_OPERATION    = -5;
144    /**
145     * Operation failed due to lack of memory.
146     */
147    public  static final int ERROR_NO_MEMORY            = -6;
148    /**
149     * Operation failed due to dead remote object.
150     */
151    public  static final int ERROR_DEAD_OBJECT          = -7;
152
153    //--------------------------------------------------------------------------
154    // Member variables
155    //--------------------
156    /**
157     * Indicates the state of the Visualizer instance
158     */
159    private int mState = STATE_UNINITIALIZED;
160    /**
161     * Lock to synchronize access to mState
162     */
163    private final Object mStateLock = new Object();
164    /**
165     * System wide unique Identifier of the visualizer engine used by this Visualizer instance
166     */
167    private int mId;
168
169    /**
170     * Lock to protect listeners updates against event notifications
171     */
172    private final Object mListenerLock = new Object();
173    /**
174     * Handler for events coming from the native code
175     */
176    private NativeEventHandler mNativeEventHandler = null;
177    /**
178     *  PCM and FFT capture listener registered by client
179     */
180    private OnDataCaptureListener mCaptureListener = null;
181    /**
182     *  Server Died listener registered by client
183     */
184    private OnServerDiedListener mServerDiedListener = null;
185
186    // accessed by native methods
187    private long mNativeVisualizer;
188    private long mJniData;
189
190    //--------------------------------------------------------------------------
191    // Constructor, Finalize
192    //--------------------
193    /**
194     * Class constructor.
195     * @param audioSession system wide unique audio session identifier. If audioSession
196     *  is not 0, the visualizer will be attached to the MediaPlayer or AudioTrack in the
197     *  same audio session. Otherwise, the Visualizer will apply to the output mix.
198     *
199     * @throws java.lang.UnsupportedOperationException
200     * @throws java.lang.RuntimeException
201     */
202
203    public Visualizer(int audioSession)
204    throws UnsupportedOperationException, RuntimeException {
205        int[] id = new int[1];
206
207        synchronized (mStateLock) {
208            mState = STATE_UNINITIALIZED;
209            // native initialization
210            int result = native_setup(new WeakReference<Visualizer>(this), audioSession, id,
211                    ActivityThread.currentOpPackageName());
212            if (result != SUCCESS && result != ALREADY_EXISTS) {
213                Log.e(TAG, "Error code "+result+" when initializing Visualizer.");
214                switch (result) {
215                case ERROR_INVALID_OPERATION:
216                    throw (new UnsupportedOperationException("Effect library not loaded"));
217                default:
218                    throw (new RuntimeException("Cannot initialize Visualizer engine, error: "
219                            +result));
220                }
221            }
222            mId = id[0];
223            if (native_getEnabled()) {
224                mState = STATE_ENABLED;
225            } else {
226                mState = STATE_INITIALIZED;
227            }
228        }
229    }
230
231    /**
232     * Releases the native Visualizer resources. It is a good practice to release the
233     * visualization engine when not in use.
234     */
235    public void release() {
236        synchronized (mStateLock) {
237            native_release();
238            mState = STATE_UNINITIALIZED;
239        }
240    }
241
242    @Override
243    protected void finalize() {
244        native_finalize();
245    }
246
247    /**
248     * Enable or disable the visualization engine.
249     * @param enabled requested enable state
250     * @return {@link #SUCCESS} in case of success,
251     * {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} in case of failure.
252     * @throws IllegalStateException
253     */
254    public int setEnabled(boolean enabled)
255    throws IllegalStateException {
256        synchronized (mStateLock) {
257            if (mState == STATE_UNINITIALIZED) {
258                throw(new IllegalStateException("setEnabled() called in wrong state: "+mState));
259            }
260            int status = SUCCESS;
261            if ((enabled && (mState == STATE_INITIALIZED)) ||
262                    (!enabled && (mState == STATE_ENABLED))) {
263                status = native_setEnabled(enabled);
264                if (status == SUCCESS) {
265                    mState = enabled ? STATE_ENABLED : STATE_INITIALIZED;
266                }
267            }
268            return status;
269        }
270    }
271
272    /**
273     * Get current activation state of the visualizer.
274     * @return true if the visualizer is active, false otherwise
275     */
276    public boolean getEnabled()
277    {
278        synchronized (mStateLock) {
279            if (mState == STATE_UNINITIALIZED) {
280                throw(new IllegalStateException("getEnabled() called in wrong state: "+mState));
281            }
282            return native_getEnabled();
283        }
284    }
285
286    /**
287     * Returns the capture size range.
288     * @return the mininum capture size is returned in first array element and the maximum in second
289     * array element.
290     */
291    public static native int[] getCaptureSizeRange();
292
293    /**
294     * Returns the maximum capture rate for the callback capture method. This is the maximum value
295     * for the rate parameter of the
296     * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method.
297     * @return the maximum capture rate expressed in milliHertz
298     */
299    public static native int getMaxCaptureRate();
300
301    /**
302     * Sets the capture size, i.e. the number of bytes returned by {@link #getWaveForm(byte[])} and
303     * {@link #getFft(byte[])} methods. The capture size must be a power of 2 in the range returned
304     * by {@link #getCaptureSizeRange()}.
305     * This method must not be called when the Visualizer is enabled.
306     * @param size requested capture size
307     * @return {@link #SUCCESS} in case of success,
308     * {@link #ERROR_BAD_VALUE} in case of failure.
309     * @throws IllegalStateException
310     */
311    public int setCaptureSize(int size)
312    throws IllegalStateException {
313        synchronized (mStateLock) {
314            if (mState != STATE_INITIALIZED) {
315                throw(new IllegalStateException("setCaptureSize() called in wrong state: "+mState));
316            }
317            return native_setCaptureSize(size);
318        }
319    }
320
321    /**
322     * Returns current capture size.
323     * @return the capture size in bytes.
324     */
325    public int getCaptureSize()
326    throws IllegalStateException {
327        synchronized (mStateLock) {
328            if (mState == STATE_UNINITIALIZED) {
329                throw(new IllegalStateException("getCaptureSize() called in wrong state: "+mState));
330            }
331            return native_getCaptureSize();
332        }
333    }
334
335    /**
336     * Set the type of scaling applied on the captured visualization data.
337     * @param mode see {@link #SCALING_MODE_NORMALIZED}
338     *     and {@link #SCALING_MODE_AS_PLAYED}
339     * @return {@link #SUCCESS} in case of success,
340     *     {@link #ERROR_BAD_VALUE} in case of failure.
341     * @throws IllegalStateException
342     */
343    public int setScalingMode(int mode)
344    throws IllegalStateException {
345        synchronized (mStateLock) {
346            if (mState == STATE_UNINITIALIZED) {
347                throw(new IllegalStateException("setScalingMode() called in wrong state: "
348                        + mState));
349            }
350            return native_setScalingMode(mode);
351        }
352    }
353
354    /**
355     * Returns the current scaling mode on the captured visualization data.
356     * @return the scaling mode, see {@link #SCALING_MODE_NORMALIZED}
357     *     and {@link #SCALING_MODE_AS_PLAYED}.
358     * @throws IllegalStateException
359     */
360    public int getScalingMode()
361    throws IllegalStateException {
362        synchronized (mStateLock) {
363            if (mState == STATE_UNINITIALIZED) {
364                throw(new IllegalStateException("getScalingMode() called in wrong state: "
365                        + mState));
366            }
367            return native_getScalingMode();
368        }
369    }
370
371    /**
372     * Sets the combination of measurement modes to be performed by this audio effect.
373     * @param mode a mask of the measurements to perform. The valid values are
374     *     {@link #MEASUREMENT_MODE_NONE} (to cancel any measurement)
375     *     or {@link #MEASUREMENT_MODE_PEAK_RMS}.
376     * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE} in case of failure.
377     * @throws IllegalStateException
378     */
379    public int setMeasurementMode(int mode)
380            throws IllegalStateException {
381        synchronized (mStateLock) {
382            if (mState == STATE_UNINITIALIZED) {
383                throw(new IllegalStateException("setMeasurementMode() called in wrong state: "
384                        + mState));
385            }
386            return native_setMeasurementMode(mode);
387        }
388    }
389
390    /**
391     * Returns the current measurement modes performed by this audio effect
392     * @return the mask of the measurements,
393     *     {@link #MEASUREMENT_MODE_NONE} (when no measurements are performed)
394     *     or {@link #MEASUREMENT_MODE_PEAK_RMS}.
395     * @throws IllegalStateException
396     */
397    public int getMeasurementMode()
398            throws IllegalStateException {
399        synchronized (mStateLock) {
400            if (mState == STATE_UNINITIALIZED) {
401                throw(new IllegalStateException("getMeasurementMode() called in wrong state: "
402                        + mState));
403            }
404            return native_getMeasurementMode();
405        }
406    }
407
408    /**
409     * Returns the sampling rate of the captured audio.
410     * @return the sampling rate in milliHertz.
411     */
412    public int getSamplingRate()
413    throws IllegalStateException {
414        synchronized (mStateLock) {
415            if (mState == STATE_UNINITIALIZED) {
416                throw(new IllegalStateException("getSamplingRate() called in wrong state: "+mState));
417            }
418            return native_getSamplingRate();
419        }
420    }
421
422    /**
423     * Returns a waveform capture of currently playing audio content. The capture consists in
424     * a number of consecutive 8-bit (unsigned) mono PCM samples equal to the capture size returned
425     * by {@link #getCaptureSize()}.
426     * <p>This method must be called when the Visualizer is enabled.
427     * @param waveform array of bytes where the waveform should be returned
428     * @return {@link #SUCCESS} in case of success,
429     * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
430     * in case of failure.
431     * @throws IllegalStateException
432     */
433    public int getWaveForm(byte[] waveform)
434    throws IllegalStateException {
435        synchronized (mStateLock) {
436            if (mState != STATE_ENABLED) {
437                throw(new IllegalStateException("getWaveForm() called in wrong state: "+mState));
438            }
439            return native_getWaveForm(waveform);
440        }
441    }
442    /**
443     * Returns a frequency capture of currently playing audio content.
444     * <p>This method must be called when the Visualizer is enabled.
445     * <p>The capture is an 8-bit magnitude FFT, the frequency range covered being 0 (DC) to half of
446     * the sampling rate returned by {@link #getSamplingRate()}. The capture returns the real and
447     * imaginary parts of a number of frequency points equal to half of the capture size plus one.
448     * <p>Note: only the real part is returned for the first point (DC) and the last point
449     * (sampling frequency / 2).
450     * <p>The layout in the returned byte array is as follows:
451     * <ul>
452     *   <li> n is the capture size returned by getCaptureSize()</li>
453     *   <li> Rfk, Ifk are respectively  the real and imaginary parts of the kth frequency
454     *   component</li>
455     *   <li> If Fs is the sampling frequency retuned by getSamplingRate() the kth frequency is:
456     *   (k*Fs)/(n/2) </li>
457     * </ul>
458     * <table border="0" cellspacing="0" cellpadding="0">
459     * <tr><td>Index </p></td>
460     *     <td>0 </p></td>
461     *     <td>1 </p></td>
462     *     <td>2 </p></td>
463     *     <td>3 </p></td>
464     *     <td>4 </p></td>
465     *     <td>5 </p></td>
466     *     <td>... </p></td>
467     *     <td>n - 2 </p></td>
468     *     <td>n - 1 </p></td></tr>
469     * <tr><td>Data </p></td>
470     *     <td>Rf0 </p></td>
471     *     <td>Rf(n/2) </p></td>
472     *     <td>Rf1 </p></td>
473     *     <td>If1 </p></td>
474     *     <td>Rf2 </p></td>
475     *     <td>If2 </p></td>
476     *     <td>... </p></td>
477     *     <td>Rf(n-1)/2 </p></td>
478     *     <td>If(n-1)/2 </p></td></tr>
479     * </table>
480     * @param fft array of bytes where the FFT should be returned
481     * @return {@link #SUCCESS} in case of success,
482     * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
483     * in case of failure.
484     * @throws IllegalStateException
485     */
486    public int getFft(byte[] fft)
487    throws IllegalStateException {
488        synchronized (mStateLock) {
489            if (mState != STATE_ENABLED) {
490                throw(new IllegalStateException("getFft() called in wrong state: "+mState));
491            }
492            return native_getFft(fft);
493        }
494    }
495
496    /**
497     * A class to store peak and RMS values.
498     * Peak and RMS are expressed in mB, as described in the
499     * {@link Visualizer#MEASUREMENT_MODE_PEAK_RMS} measurement mode.
500     */
501    public static final class MeasurementPeakRms {
502        /**
503         * The peak value in mB.
504         */
505        public int mPeak;
506        /**
507         * The RMS value in mB.
508         */
509        public int mRms;
510    }
511
512    /**
513     * Retrieves the latest peak and RMS measurement.
514     * Sets the peak and RMS fields of the supplied {@link Visualizer.MeasurementPeakRms} to the
515     * latest measured values.
516     * @param measurement a non-null {@link Visualizer.MeasurementPeakRms} instance to store
517     *    the measurement values.
518     * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE},
519     *    {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
520     *    in case of failure.
521     */
522    public int getMeasurementPeakRms(MeasurementPeakRms measurement) {
523        if (measurement == null) {
524            Log.e(TAG, "Cannot store measurements in a null object");
525            return ERROR_BAD_VALUE;
526        }
527        synchronized (mStateLock) {
528            if (mState != STATE_ENABLED) {
529                throw (new IllegalStateException("getMeasurementPeakRms() called in wrong state: "
530                        + mState));
531            }
532            return native_getPeakRms(measurement);
533        }
534    }
535
536    //---------------------------------------------------------
537    // Interface definitions
538    //--------------------
539    /**
540     * The OnDataCaptureListener interface defines methods called by the Visualizer to periodically
541     * update the audio visualization capture.
542     * The client application can implement this interface and register the listener with the
543     * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method.
544     */
545    public interface OnDataCaptureListener  {
546        /**
547         * Method called when a new waveform capture is available.
548         * <p>Data in the waveform buffer is valid only within the scope of the callback.
549         * Applications which needs access to the waveform data after returning from the callback
550         * should make a copy of the data instead of holding a reference.
551         * @param visualizer Visualizer object on which the listener is registered.
552         * @param waveform array of bytes containing the waveform representation.
553         * @param samplingRate sampling rate of the audio visualized.
554         */
555        void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate);
556
557        /**
558         * Method called when a new frequency capture is available.
559         * <p>Data in the fft buffer is valid only within the scope of the callback.
560         * Applications which needs access to the fft data after returning from the callback
561         * should make a copy of the data instead of holding a reference.
562         * @param visualizer Visualizer object on which the listener is registered.
563         * @param fft array of bytes containing the frequency representation.
564         * @param samplingRate sampling rate of the audio visualized.
565         */
566        void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate);
567    }
568
569    /**
570     * Registers an OnDataCaptureListener interface and specifies the rate at which the capture
571     * should be updated as well as the type of capture requested.
572     * <p>Call this method with a null listener to stop receiving the capture updates.
573     * @param listener OnDataCaptureListener registered
574     * @param rate rate in milliHertz at which the capture should be updated
575     * @param waveform true if a waveform capture is requested: the onWaveFormDataCapture()
576     * method will be called on the OnDataCaptureListener interface.
577     * @param fft true if a frequency capture is requested: the onFftDataCapture() method will be
578     * called on the OnDataCaptureListener interface.
579     * @return {@link #SUCCESS} in case of success,
580     * {@link #ERROR_NO_INIT} or {@link #ERROR_BAD_VALUE} in case of failure.
581     */
582    public int setDataCaptureListener(OnDataCaptureListener listener,
583            int rate, boolean waveform, boolean fft) {
584        synchronized (mListenerLock) {
585            mCaptureListener = listener;
586        }
587        if (listener == null) {
588            // make sure capture callback is stopped in native code
589            waveform = false;
590            fft = false;
591        }
592        int status = native_setPeriodicCapture(rate, waveform, fft);
593        if (status == SUCCESS) {
594            if ((listener != null) && (mNativeEventHandler == null)) {
595                Looper looper;
596                if ((looper = Looper.myLooper()) != null) {
597                    mNativeEventHandler = new NativeEventHandler(this, looper);
598                } else if ((looper = Looper.getMainLooper()) != null) {
599                    mNativeEventHandler = new NativeEventHandler(this, looper);
600                } else {
601                    mNativeEventHandler = null;
602                    status = ERROR_NO_INIT;
603                }
604            }
605        }
606        return status;
607    }
608
609    /**
610     * @hide
611     *
612     * The OnServerDiedListener interface defines a method called by the Visualizer to indicate that
613     * the connection to the native media server has been broken and that the Visualizer object will
614     * need to be released and re-created.
615     * The client application can implement this interface and register the listener with the
616     * {@link #setServerDiedListener(OnServerDiedListener)} method.
617     */
618    public interface OnServerDiedListener  {
619        /**
620         * @hide
621         *
622         * Method called when the native media server has died.
623         * <p>If the native media server encounters a fatal error and needs to restart, the binder
624         * connection from the {@link #Visualizer} to the media server will be broken.  Data capture
625         * callbacks will stop happening, and client initiated calls to the {@link #Visualizer}
626         * instance will fail with the error code {@link #DEAD_OBJECT}.  To restore functionality,
627         * clients should {@link #release()} their old visualizer and create a new instance.
628         */
629        void onServerDied();
630    }
631
632    /**
633     * @hide
634     *
635     * Registers an OnServerDiedListener interface.
636     * <p>Call this method with a null listener to stop receiving server death notifications.
637     * @return {@link #SUCCESS} in case of success,
638     */
639    public int setServerDiedListener(OnServerDiedListener listener) {
640        synchronized (mListenerLock) {
641            mServerDiedListener = listener;
642        }
643        return SUCCESS;
644    }
645
646    /**
647     * Helper class to handle the forwarding of native events to the appropriate listeners
648     */
649    private class NativeEventHandler extends Handler
650    {
651        private Visualizer mVisualizer;
652
653        public NativeEventHandler(Visualizer v, Looper looper) {
654            super(looper);
655            mVisualizer = v;
656        }
657
658        private void handleCaptureMessage(Message msg) {
659            OnDataCaptureListener l = null;
660            synchronized (mListenerLock) {
661                l = mVisualizer.mCaptureListener;
662            }
663
664            if (l != null) {
665                byte[] data = (byte[])msg.obj;
666                int samplingRate = msg.arg1;
667
668                switch(msg.what) {
669                case NATIVE_EVENT_PCM_CAPTURE:
670                    l.onWaveFormDataCapture(mVisualizer, data, samplingRate);
671                    break;
672                case NATIVE_EVENT_FFT_CAPTURE:
673                    l.onFftDataCapture(mVisualizer, data, samplingRate);
674                    break;
675                default:
676                    Log.e(TAG,"Unknown native event in handleCaptureMessge: "+msg.what);
677                    break;
678                }
679            }
680        }
681
682        private void handleServerDiedMessage(Message msg) {
683            OnServerDiedListener l = null;
684            synchronized (mListenerLock) {
685                l = mVisualizer.mServerDiedListener;
686            }
687
688            if (l != null)
689                l.onServerDied();
690        }
691
692        @Override
693        public void handleMessage(Message msg) {
694            if (mVisualizer == null) {
695                return;
696            }
697
698            switch(msg.what) {
699            case NATIVE_EVENT_PCM_CAPTURE:
700            case NATIVE_EVENT_FFT_CAPTURE:
701                handleCaptureMessage(msg);
702                break;
703            case NATIVE_EVENT_SERVER_DIED:
704                handleServerDiedMessage(msg);
705                break;
706            default:
707                Log.e(TAG,"Unknown native event: "+msg.what);
708                break;
709            }
710        }
711    }
712
713    //---------------------------------------------------------
714    // Interface definitions
715    //--------------------
716
717    private static native final void native_init();
718
719    private native final int native_setup(Object audioeffect_this,
720                                          int audioSession,
721                                          int[] id,
722                                          String opPackageName);
723
724    private native final void native_finalize();
725
726    private native final void native_release();
727
728    private native final int native_setEnabled(boolean enabled);
729
730    private native final boolean native_getEnabled();
731
732    private native final int native_setCaptureSize(int size);
733
734    private native final int native_getCaptureSize();
735
736    private native final int native_setScalingMode(int mode);
737
738    private native final int native_getScalingMode();
739
740    private native final int native_setMeasurementMode(int mode);
741
742    private native final int native_getMeasurementMode();
743
744    private native final int native_getSamplingRate();
745
746    private native final int native_getWaveForm(byte[] waveform);
747
748    private native final int native_getFft(byte[] fft);
749
750    private native final int native_getPeakRms(MeasurementPeakRms measurement);
751
752    private native final int native_setPeriodicCapture(int rate, boolean waveForm, boolean fft);
753
754    //---------------------------------------------------------
755    // Java methods called from the native side
756    //--------------------
757    @SuppressWarnings("unused")
758    private static void postEventFromNative(Object effect_ref,
759            int what, int arg1, int arg2, Object obj) {
760        Visualizer visu = (Visualizer)((WeakReference)effect_ref).get();
761        if (visu == null) {
762            return;
763        }
764
765        if (visu.mNativeEventHandler != null) {
766            Message m = visu.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj);
767            visu.mNativeEventHandler.sendMessage(m);
768        }
769
770    }
771}
772
773