MediaVisualizerTest.java revision db6028508c8eb31a0de1dcdfc410ddfe6df7c5ad
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 com.android.mediaframeworktest.functional.audio;
18
19import com.android.mediaframeworktest.MediaFrameworkTest;
20import com.android.mediaframeworktest.MediaNames;
21import android.content.Context;
22import android.content.res.AssetFileDescriptor;
23import android.media.audiofx.AudioEffect;
24import android.media.AudioManager;
25import android.media.audiofx.Visualizer;
26import android.media.MediaPlayer;
27
28import android.os.Looper;
29import android.test.suitebuilder.annotation.LargeTest;
30import android.test.suitebuilder.annotation.MediumTest;
31import android.test.suitebuilder.annotation.Suppress;
32import android.test.ActivityInstrumentationTestCase2;
33import android.util.Log;
34
35import java.nio.ByteOrder;
36import java.nio.ByteBuffer;
37import java.util.UUID;
38
39/**
40 * Junit / Instrumentation test case for the media AudioTrack api
41
42 */
43public class MediaVisualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
44    private String TAG = "MediaVisualizerTest";
45    private final static int MIN_CAPTURE_RATE_MAX = 20000;
46    private final static int MIN_SAMPLING_RATE = 8000000;
47    private final static int MAX_SAMPLING_RATE = 48000000;
48    private final static int MIN_CAPTURE_SIZE_MAX = 1024;
49    private final static int MAX_CAPTURE_SIZE_MIN = 128;
50
51    private Visualizer mVisualizer = null;
52    private int mSession = -1;
53    private boolean mInitialized = false;
54    private Looper mLooper = null;
55    private final Object lock = new Object();
56    private byte[] mWaveform = null;
57    private byte[] mFft = null;
58    private boolean mCaptureWaveform = false;
59    private boolean mCaptureFft = false;
60
61    public MediaVisualizerTest() {
62        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
63    }
64
65    @Override
66    protected void setUp() throws Exception {
67      super.setUp();
68    }
69
70    @Override
71    protected void tearDown() throws Exception {
72        super.tearDown();
73        releaseVisualizer();
74    }
75
76    private static void assumeTrue(String message, boolean cond) {
77        assertTrue("(assume)"+message, cond);
78    }
79
80    private void log(String testName, String message) {
81        Log.v(TAG, "["+testName+"] "+message);
82    }
83
84    private void loge(String testName, String message) {
85        Log.e(TAG, "["+testName+"] "+message);
86    }
87
88    //-----------------------------------------------------------------
89    // VISUALIZER TESTS:
90    //----------------------------------
91
92
93    //-----------------------------------------------------------------
94    // 0 - constructor
95    //----------------------------------
96
97    //Test case 0.0: test constructor and release
98    @LargeTest
99    public void test0_0ConstructorAndRelease() throws Exception {
100        boolean result = false;
101        String msg = "test1_0ConstructorAndRelease()";
102        Visualizer visualizer = null;
103         try {
104            visualizer = new Visualizer(0);
105            assertNotNull(msg + ": could not create Visualizer", visualizer);
106            result = true;
107        } catch (IllegalArgumentException e) {
108            msg = msg.concat(": Visualizer not found");
109        } catch (UnsupportedOperationException e) {
110            msg = msg.concat(": Effect library not loaded");
111        } finally {
112            if (visualizer != null) {
113                visualizer.release();
114            }
115        }
116        assertTrue(msg, result);
117    }
118
119
120    //-----------------------------------------------------------------
121    // 1 - get/set parameters
122    //----------------------------------
123
124    //Test case 1.0: check capture rate and sampling rate
125    @LargeTest
126    public void test1_0CaptureRates() throws Exception {
127        boolean result = false;
128        String msg = "test1_0CaptureRates()";
129        getVisualizer(0);
130        try {
131            int captureRate = mVisualizer.getMaxCaptureRate();
132            assertTrue(msg +": insufficient max capture rate",
133                    captureRate >= MIN_CAPTURE_RATE_MAX);
134            int samplingRate = mVisualizer.getSamplingRate();
135            assertTrue(msg +": invalid sampling rate",
136                    samplingRate >= MIN_SAMPLING_RATE && samplingRate <= MAX_SAMPLING_RATE);
137            result = true;
138        } catch (IllegalArgumentException e) {
139            msg = msg.concat(": Bad parameter value");
140            loge(msg, "Bad parameter value");
141        } catch (UnsupportedOperationException e) {
142            msg = msg.concat(": get parameter() rejected");
143            loge(msg, "get parameter() rejected");
144        } catch (IllegalStateException e) {
145            msg = msg.concat("get parameter() called in wrong state");
146            loge(msg, "get parameter() called in wrong state");
147        } finally {
148            releaseVisualizer();
149        }
150        assertTrue(msg, result);
151    }
152
153    //Test case 1.1: check capture size
154    @LargeTest
155    public void test1_1CaptureSize() throws Exception {
156        boolean result = false;
157        String msg = "test1_1CaptureSize()";
158        getVisualizer(0);
159        try {
160            int[] range = mVisualizer.getCaptureSizeRange();
161            assertTrue(msg +": insufficient min capture size",
162                    range[0] <= MAX_CAPTURE_SIZE_MIN);
163            assertTrue(msg +": insufficient min capture size",
164                    range[1] >= MIN_CAPTURE_SIZE_MAX);
165            mVisualizer.setCaptureSize(range[0]);
166            assertEquals(msg +": insufficient min capture size",
167                    range[0], mVisualizer.getCaptureSize());
168            mVisualizer.setCaptureSize(range[1]);
169            assertEquals(msg +": insufficient min capture size",
170                    range[1], mVisualizer.getCaptureSize());
171            result = true;
172        } catch (IllegalArgumentException e) {
173            msg = msg.concat(": Bad parameter value");
174            loge(msg, "Bad parameter value");
175        } catch (UnsupportedOperationException e) {
176            msg = msg.concat(": get parameter() rejected");
177            loge(msg, "get parameter() rejected");
178        } catch (IllegalStateException e) {
179            msg = msg.concat("get parameter() called in wrong state");
180            loge(msg, "get parameter() called in wrong state");
181        } finally {
182            releaseVisualizer();
183        }
184        assertTrue(msg, result);
185    }
186
187    //-----------------------------------------------------------------
188    // 2 - check capture
189    //----------------------------------
190
191    //Test case 2.0: test capture in polling mode
192    @LargeTest
193    public void test2_0PollingCapture() throws Exception {
194        boolean result = false;
195        String msg = "test2_0PollingCapture()";
196        AudioEffect vc = null;
197        MediaPlayer mp = null;
198        AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
199        int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
200        am.setStreamVolume(AudioManager.STREAM_MUSIC,
201                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
202                           0);
203
204        try {
205            // creating a volume controller on output mix ensures that ro.audio.silent mutes
206            // audio after the effects and not before
207            vc = new AudioEffect(
208                    AudioEffect.EFFECT_TYPE_NULL,
209                    UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
210                      0,
211                      0);
212            vc.setEnabled(true);
213
214            mp = new MediaPlayer();
215            mp.setDataSource(MediaNames.SINE_200_1000);
216            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
217            getVisualizer(mp.getAudioSessionId());
218            mVisualizer.setEnabled(true);
219            // check capture on silence
220            byte[] data = new byte[mVisualizer.getCaptureSize()];
221            mVisualizer.getWaveForm(data);
222            int energy = computeEnergy(data, true);
223            assertEquals(msg +": getWaveForm reports energy for silence",
224                    0, energy);
225            mVisualizer.getFft(data);
226            energy = computeEnergy(data, false);
227            assertEquals(msg +": getFft reports energy for silence",
228                    0, energy);
229            mp.prepare();
230            mp.start();
231            Thread.sleep(500);
232            // check capture on sound
233            mVisualizer.getWaveForm(data);
234            energy = computeEnergy(data, true);
235            assertTrue(msg +": getWaveForm reads insufficient level",
236                    energy > 0);
237            mVisualizer.getFft(data);
238            energy = computeEnergy(data, false);
239            assertTrue(msg +": getFft reads insufficient level",
240                    energy > 0);
241            result = true;
242        } catch (IllegalArgumentException e) {
243            msg = msg.concat(": Bad parameter value");
244            loge(msg, "Bad parameter value");
245        } catch (UnsupportedOperationException e) {
246            msg = msg.concat(": get parameter() rejected");
247            loge(msg, "get parameter() rejected");
248        } catch (IllegalStateException e) {
249            msg = msg.concat("get parameter() called in wrong state");
250            loge(msg, "get parameter() called in wrong state");
251        } catch (InterruptedException e) {
252            loge(msg, "sleep() interrupted");
253        }
254        finally {
255            releaseVisualizer();
256            if (mp != null) {
257                mp.release();
258            }
259            if (vc != null) {
260                vc.release();
261            }
262            am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
263        }
264        assertTrue(msg, result);
265    }
266
267    //Test case 2.1: test capture with listener
268    @LargeTest
269    public void test2_1ListenerCapture() throws Exception {
270        boolean result = false;
271        String msg = "test2_1ListenerCapture()";
272        AudioEffect vc = null;
273        MediaPlayer mp = null;
274        AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
275        int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
276        am.setStreamVolume(AudioManager.STREAM_MUSIC,
277                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
278                           0);
279
280        try {
281            // creating a volume controller on output mix ensures that ro.audio.silent mutes
282            // audio after the effects and not before
283            vc = new AudioEffect(
284                    AudioEffect.EFFECT_TYPE_NULL,
285                    UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"),
286                      0,
287                      0);
288            vc.setEnabled(true);
289
290            mp = new MediaPlayer();
291            mp.setDataSource(MediaNames.SINE_200_1000);
292            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
293
294            getVisualizer(mp.getAudioSessionId());
295            createListenerLooper();
296            synchronized(lock) {
297                try {
298                    lock.wait(1000);
299                } catch(Exception e) {
300                    Log.e(TAG, "Looper creation: wait was interrupted.");
301                }
302            }
303            assertTrue(mInitialized);
304
305            mVisualizer.setEnabled(true);
306
307            // check capture on silence
308            synchronized(lock) {
309                try {
310                    mCaptureWaveform = true;
311                    lock.wait(1000);
312                    mCaptureWaveform = false;
313                } catch(Exception e) {
314                    Log.e(TAG, "Capture waveform: wait was interrupted.");
315                }
316            }
317            assertNotNull(msg +": waveform capture failed", mWaveform);
318            int energy = computeEnergy(mWaveform, true);
319            assertEquals(msg +": getWaveForm reports energy for silence",
320                    0, energy);
321
322            synchronized(lock) {
323                try {
324                    mCaptureFft = true;
325                    lock.wait(1000);
326                    mCaptureFft = false;
327                } catch(Exception e) {
328                    Log.e(TAG, "Capture FFT: wait was interrupted.");
329                }
330            }
331            assertNotNull(msg +": FFT capture failed", mFft);
332            energy = computeEnergy(mFft, false);
333            assertEquals(msg +": getFft reports energy for silence",
334                    0, energy);
335
336            mp.prepare();
337            mp.start();
338            Thread.sleep(500);
339
340            // check capture on sound
341            synchronized(lock) {
342                try {
343                    mCaptureWaveform = true;
344                    lock.wait(1000);
345                    mCaptureWaveform = false;
346                } catch(Exception e) {
347                    Log.e(TAG, "Capture waveform: wait was interrupted.");
348                }
349            }
350            assertNotNull(msg +": waveform capture failed", mWaveform);
351            energy = computeEnergy(mWaveform, true);
352            assertTrue(msg +": getWaveForm reads insufficient level",
353                    energy > 0);
354
355            synchronized(lock) {
356                try {
357                    mCaptureFft = true;
358                    lock.wait(1000);
359                    mCaptureFft = false;
360                } catch(Exception e) {
361                    Log.e(TAG, "Capture FFT: wait was interrupted.");
362                }
363            }
364            assertNotNull(msg +": FFT capture failed", mFft);
365            energy = computeEnergy(mFft, false);
366            assertTrue(msg +": getFft reads insufficient level",
367                    energy > 0);
368
369            result = true;
370        } catch (IllegalArgumentException e) {
371            msg = msg.concat(": Bad parameter value");
372            loge(msg, "Bad parameter value");
373        } catch (UnsupportedOperationException e) {
374            msg = msg.concat(": get parameter() rejected");
375            loge(msg, "get parameter() rejected");
376        } catch (IllegalStateException e) {
377            msg = msg.concat("get parameter() called in wrong state");
378            loge(msg, "get parameter() called in wrong state");
379        } catch (InterruptedException e) {
380            loge(msg, "sleep() interrupted");
381        }
382        finally {
383            terminateListenerLooper();
384            releaseVisualizer();
385            if (mp != null) {
386                mp.release();
387            }
388            if (vc != null) {
389                vc.release();
390            }
391            am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
392        }
393        assertTrue(msg, result);
394    }
395
396    //-----------------------------------------------------------------
397    // private methods
398    //----------------------------------
399
400    private int computeEnergy(byte[] data, boolean unsigned) {
401        int energy = 0;
402        if (data.length != 0) {
403            for (int i = 0; i < data.length; i++) {
404                int tmp;
405                // convert from unsigned 8 bit to signed 16 bit
406                if (unsigned) {
407                    tmp = ((int)data[i] & 0xFF) - 128;
408                } else {
409                    tmp = (int)data[i];
410                }
411                energy += tmp*tmp;
412            }
413            energy /= data.length;
414        }
415        return energy;
416    }
417
418    private void getVisualizer(int session) {
419         if (mVisualizer == null || session != mSession) {
420             if (session != mSession && mVisualizer != null) {
421                 mVisualizer.release();
422                 mVisualizer = null;
423             }
424             try {
425                mVisualizer = new Visualizer(session);
426                mSession = session;
427            } catch (IllegalArgumentException e) {
428                Log.e(TAG, "getVisualizer() Visualizer not found exception: "+e);
429            } catch (UnsupportedOperationException e) {
430                Log.e(TAG, "getVisualizer() Effect library not loaded exception: "+e);
431            }
432         }
433         assertNotNull("could not create mVisualizer", mVisualizer);
434    }
435
436    private void releaseVisualizer() {
437        if (mVisualizer != null) {
438            mVisualizer.release();
439            mVisualizer = null;
440        }
441   }
442
443    private void createListenerLooper() {
444
445        new Thread() {
446            @Override
447            public void run() {
448                // Set up a looper to be used by mEffect.
449                Looper.prepare();
450
451                // Save the looper so that we can terminate this thread
452                // after we are done with it.
453                mLooper = Looper.myLooper();
454
455                if (mVisualizer != null) {
456                    mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
457                        public void onWaveFormDataCapture(
458                                Visualizer visualizer, byte[] waveform, int samplingRate) {
459                            synchronized(lock) {
460                                if (visualizer == mVisualizer) {
461                                    if (mCaptureWaveform) {
462                                        mWaveform = waveform;
463                                        lock.notify();
464                                    }
465                                }
466                            }
467                        }
468
469                        public void onFftDataCapture(
470                                Visualizer visualizer, byte[] fft, int samplingRate) {
471                            synchronized(lock) {
472                                if (visualizer == mVisualizer) {
473                                    if (mCaptureFft) {
474                                        mFft = fft;
475                                        lock.notify();
476                                    }
477                                }
478                            }
479                        }
480                    },
481                    10000,
482                    true,
483                    true);
484                }
485
486                synchronized(lock) {
487                    mInitialized = true;
488                    lock.notify();
489                }
490                Looper.loop();  // Blocks forever until Looper.quit() is called.
491            }
492        }.start();
493    }
494    /*
495     * Terminates the listener looper thread.
496     */
497    private void terminateListenerLooper() {
498        if (mLooper != null) {
499            mLooper.quit();
500            mLooper = null;
501        }
502    }
503
504}
505