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