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 com.android.mediaframeworktest.functional.EnergyProbe;
22import android.content.Context;
23import android.content.res.AssetFileDescriptor;
24import android.media.audiofx.AudioEffect;
25import android.media.AudioManager;
26import android.media.audiofx.EnvironmentalReverb;
27import android.media.audiofx.Visualizer;
28import android.media.MediaPlayer;
29
30import android.os.Looper;
31import android.test.suitebuilder.annotation.LargeTest;
32import android.test.suitebuilder.annotation.MediumTest;
33import android.test.suitebuilder.annotation.Suppress;
34import android.test.ActivityInstrumentationTestCase2;
35import android.util.Log;
36
37import java.nio.ByteOrder;
38import java.nio.ByteBuffer;
39import java.util.UUID;
40
41/**
42 * Junit / Instrumentation test case for the media AudioTrack api
43
44 */
45public class MediaEnvReverbTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
46    private String TAG = "MediaEnvReverbTest";
47    // allow +/- 100 millibel difference between set and get gains
48    private final static int MILLIBEL_TOLERANCE = 100;
49    // allow +/- 5% tolerance between set and get delays
50    private final static float DELAY_TOLERANCE = 1.05f;
51    // allow +/- 5% tolerance between set and get ratios
52    private final static float RATIO_TOLERANCE = 1.05f;
53    // Implementor UUID for volume controller effect defined in
54    // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
55    private final static UUID VOLUME_EFFECT_UUID =
56        UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b");
57    // Implementor UUID for environmental reverb effect defined in
58    // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
59    private final static UUID ENV_REVERB_EFFECT_UUID =
60        UUID.fromString("c7a511a0-a3bb-11df-860e-0002a5d5c51b");
61
62    private EnvironmentalReverb mReverb = null;
63    private int mSession = -1;
64
65    public MediaEnvReverbTest() {
66        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
67    }
68
69    @Override
70    protected void setUp() throws Exception {
71      super.setUp();
72    }
73
74    @Override
75    protected void tearDown() throws Exception {
76        super.tearDown();
77        releaseReverb();
78    }
79
80    private static void assumeTrue(String message, boolean cond) {
81        assertTrue("(assume)"+message, cond);
82    }
83
84    private void log(String testName, String message) {
85        Log.v(TAG, "["+testName+"] "+message);
86    }
87
88    private void loge(String testName, String message) {
89        Log.e(TAG, "["+testName+"] "+message);
90    }
91
92    //-----------------------------------------------------------------
93    // ENVIRONMENTAL REVEB TESTS:
94    //----------------------------------
95
96
97    //-----------------------------------------------------------------
98    // 0 - constructor
99    //----------------------------------
100
101    //Test case 0.0: test constructor and release
102    @LargeTest
103    public void test0_0ConstructorAndRelease() throws Exception {
104        boolean result = false;
105        String msg = "test1_0ConstructorAndRelease()";
106        EnvironmentalReverb reverb = null;
107         try {
108            reverb = new EnvironmentalReverb(0, 0);
109            assertNotNull(msg + ": could not create EnvironmentalReverb", reverb);
110            try {
111                assertTrue(msg +": invalid effect ID", (reverb.getId() != 0));
112            } catch (IllegalStateException e) {
113                msg = msg.concat(": EnvironmentalReverb not initialized");
114            }
115            result = true;
116        } catch (IllegalArgumentException e) {
117            msg = msg.concat(": EnvironmentalReverb not found");
118        } catch (UnsupportedOperationException e) {
119            msg = msg.concat(": Effect library not loaded");
120        } finally {
121            if (reverb != null) {
122                reverb.release();
123            }
124        }
125        assertTrue(msg, result);
126    }
127
128    //-----------------------------------------------------------------
129    // 1 - get/set parameters
130    //----------------------------------
131
132    //Test case 1.0: test room level and room HF level
133    @LargeTest
134    public void test1_0Room() throws Exception {
135        boolean result = false;
136        String msg = "test1_0Room()";
137        getReverb(0);
138        try {
139            mReverb.setRoomLevel((short)0);
140            short level = mReverb.getRoomLevel();
141            assertTrue(msg +": got incorrect room level",
142                    (level > (0 - MILLIBEL_TOLERANCE)) &&
143                    (level < (0 + MILLIBEL_TOLERANCE)));
144
145            mReverb.setRoomHFLevel((short)-6);
146            level = mReverb.getRoomHFLevel();
147            assertTrue(msg +": got incorrect room HF level",
148                    (level > (-6 - MILLIBEL_TOLERANCE)) &&
149                    (level < (-6 + MILLIBEL_TOLERANCE)));
150
151            result = true;
152        } catch (IllegalArgumentException e) {
153            msg = msg.concat(": Bad parameter value");
154            loge(msg, "Bad parameter value");
155        } catch (UnsupportedOperationException e) {
156            msg = msg.concat(": get parameter() rejected");
157            loge(msg, "get parameter() rejected");
158        } catch (IllegalStateException e) {
159            msg = msg.concat("get parameter() called in wrong state");
160            loge(msg, "get parameter() called in wrong state");
161        } finally {
162            releaseReverb();
163        }
164        assertTrue(msg, result);
165    }
166
167    //Test case 1.1: test decay time and ratio
168    @LargeTest
169    public void test1_1Decay() throws Exception {
170        boolean result = false;
171        String msg = "test1_1Decay()";
172        getReverb(0);
173        try {
174            mReverb.setDecayTime(500);
175            int time = mReverb.getDecayTime();
176            assertTrue(msg +": got incorrect decay time",
177                    ((float)time > (float)(500 / DELAY_TOLERANCE)) &&
178                    ((float)time < (float)(500 * DELAY_TOLERANCE)));
179
180            mReverb.setDecayHFRatio((short)1000);
181            short ratio = mReverb.getDecayHFRatio();
182            assertTrue(msg +": got incorrect decay HF ratio",
183                    ((float)ratio > (float)(1000 / RATIO_TOLERANCE)) &&
184                    ((float)ratio < (float)(1000 * RATIO_TOLERANCE)));
185
186            result = true;
187        } catch (IllegalArgumentException e) {
188            msg = msg.concat(": Bad parameter value");
189            loge(msg, "Bad parameter value");
190        } catch (UnsupportedOperationException e) {
191            msg = msg.concat(": get parameter() rejected");
192            loge(msg, "get parameter() rejected");
193        } catch (IllegalStateException e) {
194            msg = msg.concat("get parameter() called in wrong state");
195            loge(msg, "get parameter() called in wrong state");
196        } finally {
197            releaseReverb();
198        }
199        assertTrue(msg, result);
200    }
201
202    //Test case 1.2: test reflections
203    @LargeTest
204    public void test1_2Reflections() throws Exception {
205        // TODO: uncomment when early reflections are implemented
206//        boolean result = false;
207//        String msg = "test1_2Reflections()";
208//        getReverb(0);
209//        try {
210//            mReverb.setReflectionsLevel((short)0);
211//            short level = mReverb.getReflectionsLevel();
212//            assertTrue(msg +": got incorrect reflections level",
213//                    (level > (0 - MILLIBEL_TOLERANCE)) &&
214//                    (level < (0 + MILLIBEL_TOLERANCE)));
215//
216//            mReverb.setReflectionsDelay(30);
217//            int delay = mReverb.getReflectionsDelay();
218//            assertTrue(msg +": got incorrect reflections delay",
219//                    ((float)delay > (float)(30 / DELAY_TOLERANCE)) &&
220//                    ((float)delay < (float)(30 * DELAY_TOLERANCE)));
221//
222//            result = true;
223//        } catch (IllegalArgumentException e) {
224//            msg = msg.concat(": Bad parameter value");
225//            loge(msg, "Bad parameter value");
226//        } catch (UnsupportedOperationException e) {
227//            msg = msg.concat(": get parameter() rejected");
228//            loge(msg, "get parameter() rejected");
229//        } catch (IllegalStateException e) {
230//            msg = msg.concat("get parameter() called in wrong state");
231//            loge(msg, "get parameter() called in wrong state");
232//        } finally {
233//            releaseReverb();
234//        }
235//        assertTrue(msg, result);
236    }
237
238    //Test case 1.3: test reverb
239    @LargeTest
240    public void test1_3Reverb() throws Exception {
241        boolean result = false;
242        String msg = "test1_3Reverb()";
243        getReverb(0);
244        try {
245            mReverb.setReverbLevel((short)0);
246            short level = mReverb.getReverbLevel();
247            assertTrue(msg +": got incorrect reverb level",
248                    (level > (0 - MILLIBEL_TOLERANCE)) &&
249                    (level < (0 + MILLIBEL_TOLERANCE)));
250
251            // TODO: change delay when early reflections are implemented
252            mReverb.setReverbDelay(0);
253            int delay = mReverb.getReverbDelay();
254            assertTrue(msg +": got incorrect reverb delay", delay < 5);
255
256            result = true;
257        } catch (IllegalArgumentException e) {
258            msg = msg.concat(": Bad parameter value");
259            loge(msg, "Bad parameter value");
260        } catch (UnsupportedOperationException e) {
261            msg = msg.concat(": get parameter() rejected");
262            loge(msg, "get parameter() rejected");
263        } catch (IllegalStateException e) {
264            msg = msg.concat("get parameter() called in wrong state");
265            loge(msg, "get parameter() called in wrong state");
266        } finally {
267            releaseReverb();
268        }
269        assertTrue(msg, result);
270    }
271
272    //Test case 1.4: test diffusion and density
273    @LargeTest
274    public void test1_4DiffusionAndDensity() throws Exception {
275        boolean result = false;
276        String msg = "test1_4DiffusionAndDensity()";
277        getReverb(0);
278        try {
279            mReverb.setDiffusion((short)500);
280            short diffusion = mReverb.getDiffusion();
281            assertTrue(msg +": got incorrect diffusion",
282                    ((float)diffusion > (float)(500 / RATIO_TOLERANCE)) &&
283                    ((float)diffusion < (float)(500 * RATIO_TOLERANCE)));
284
285            mReverb.setDensity((short)500);
286            short density = mReverb.getDensity();
287            assertTrue(msg +": got incorrect density",
288                    ((float)density > (float)(500 / RATIO_TOLERANCE)) &&
289                    ((float)density < (float)(500 * RATIO_TOLERANCE)));
290
291            result = true;
292        } catch (IllegalArgumentException e) {
293            msg = msg.concat(": Bad parameter value");
294            loge(msg, "Bad parameter value");
295        } catch (UnsupportedOperationException e) {
296            msg = msg.concat(": get parameter() rejected");
297            loge(msg, "get parameter() rejected");
298        } catch (IllegalStateException e) {
299            msg = msg.concat("get parameter() called in wrong state");
300            loge(msg, "get parameter() called in wrong state");
301        } finally {
302            releaseReverb();
303        }
304        assertTrue(msg, result);
305    }
306
307    //Test case 1.5: test properties
308    @LargeTest
309    public void test1_5Properties() throws Exception {
310        boolean result = false;
311        String msg = "test1_5Properties()";
312        getReverb(0);
313        try {
314            EnvironmentalReverb.Settings settings = mReverb.getProperties();
315            short newRoomLevel = 0;
316            if (settings.roomLevel == 0) {
317                newRoomLevel = -1000;
318            }
319            String str = settings.toString();
320            settings = new EnvironmentalReverb.Settings(str);
321            settings.roomLevel = newRoomLevel;
322            mReverb.setProperties(settings);
323            settings = mReverb.getProperties();
324            assertTrue(msg +": setProperties failed",
325                    (settings.roomLevel > (newRoomLevel - MILLIBEL_TOLERANCE)) &&
326                    (settings.roomLevel < (newRoomLevel + MILLIBEL_TOLERANCE)));
327            result = true;
328        } catch (IllegalArgumentException e) {
329            msg = msg.concat(": Bad parameter value");
330            loge(msg, "Bad parameter value");
331        } catch (UnsupportedOperationException e) {
332            msg = msg.concat(": get parameter() rejected");
333            loge(msg, "get parameter() rejected");
334        } catch (IllegalStateException e) {
335            msg = msg.concat("get parameter() called in wrong state");
336            loge(msg, "get parameter() called in wrong state");
337        } finally {
338            releaseReverb();
339        }
340        assertTrue(msg, result);
341    }
342
343    //-----------------------------------------------------------------
344    // 2 - Effect action
345    //----------------------------------
346
347    //Test case 2.0: test actual auxiliary reverb influence on sound
348    @LargeTest
349    public void test2_0AuxiliarySoundModification() throws Exception {
350        boolean result = false;
351        String msg = "test2_0AuxiliarySoundModification()";
352        EnergyProbe probe = null;
353        AudioEffect vc = null;
354        MediaPlayer mp = null;
355        AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
356        int ringerMode = am.getRingerMode();
357        am.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
358        int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
359        am.setStreamVolume(AudioManager.STREAM_MUSIC,
360                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
361                           0);
362        getReverb(0);
363        try {
364            probe = new EnergyProbe(0);
365            // creating a volume controller on output mix ensures that ro.audio.silent mutes
366            // audio after the effects and not before
367            vc = new AudioEffect(
368                                AudioEffect.EFFECT_TYPE_NULL,
369                                VOLUME_EFFECT_UUID,
370                                0,
371                                0);
372            vc.setEnabled(true);
373
374            mp = new MediaPlayer();
375            mp.setDataSource(MediaNames.SINE_200_1000);
376            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
377            mp.attachAuxEffect(mReverb.getId());
378            mp.setAuxEffectSendLevel(1.0f);
379            mReverb.setRoomLevel((short)0);
380            mReverb.setReverbLevel((short)0);
381            mReverb.setDecayTime(2000);
382            mReverb.setEnabled(true);
383            mp.prepare();
384            mp.start();
385            Thread.sleep(1000);
386            mp.stop();
387            Thread.sleep(300);
388            // measure energy around 1kHz after media player was stopped for 300 ms
389            int energy1000 = probe.capture(1000);
390            assertTrue(msg + ": reverb has no effect", energy1000 > 0);
391            result = true;
392        } catch (IllegalArgumentException e) {
393            msg = msg.concat(": Bad parameter value");
394            loge(msg, "Bad parameter value");
395        } catch (UnsupportedOperationException e) {
396            msg = msg.concat(": get parameter() rejected");
397            loge(msg, "get parameter() rejected");
398        } catch (IllegalStateException e) {
399            msg = msg.concat("get parameter() called in wrong state");
400            loge(msg, "get parameter() called in wrong state");
401        } catch (InterruptedException e) {
402            loge(msg, "sleep() interrupted");
403        }
404        finally {
405            releaseReverb();
406            if (mp != null) {
407                mp.release();
408            }
409            if (vc != null) {
410                vc.release();
411            }
412            if (probe != null) {
413                probe.release();
414            }
415            am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
416            am.setRingerMode(ringerMode);
417        }
418        assertTrue(msg, result);
419    }
420
421    //Test case 2.1: test actual insert reverb influence on sound
422    @LargeTest
423    public void test2_1InsertSoundModification() throws Exception {
424        boolean result = false;
425        String msg = "test2_1InsertSoundModification()";
426        EnergyProbe probe = null;
427        AudioEffect vc = null;
428        MediaPlayer mp = null;
429        AudioEffect rvb = null;
430        AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
431        int ringerMode = am.getRingerMode();
432        am.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
433        int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
434        am.setStreamVolume(AudioManager.STREAM_MUSIC,
435                           am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
436                           0);
437        try {
438            // creating a volume controller on output mix ensures that ro.audio.silent mutes
439            // audio after the effects and not before
440            vc = new AudioEffect(
441                                AudioEffect.EFFECT_TYPE_NULL,
442                                VOLUME_EFFECT_UUID,
443                                0,
444                                0);
445            vc.setEnabled(true);
446
447            mp = new MediaPlayer();
448            mp.setDataSource(MediaNames.SINE_200_1000);
449            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
450
451            // create reverb with UUID instead of EnvironmentalReverb constructor otherwise an
452            // auxiliary reverb will be chosen by the effect framework as we are on session 0
453            rvb = new AudioEffect(
454                        AudioEffect.EFFECT_TYPE_NULL,
455                        ENV_REVERB_EFFECT_UUID,
456                        0,
457                        0);
458
459            rvb.setParameter(EnvironmentalReverb.PARAM_ROOM_LEVEL, (short)0);
460            rvb.setParameter(EnvironmentalReverb.PARAM_REVERB_LEVEL, (short)0);
461            rvb.setParameter(EnvironmentalReverb.PARAM_DECAY_TIME, 2000);
462            rvb.setEnabled(true);
463
464            // create probe after reverb so that it is chained behind the reverb in the
465            // effect chain
466            probe = new EnergyProbe(0);
467
468            mp.prepare();
469            mp.start();
470            Thread.sleep(1000);
471            mp.stop();
472            Thread.sleep(300);
473            // measure energy around 1kHz after media player was stopped for 300 ms
474            int energy1000 = probe.capture(1000);
475            assertTrue(msg + ": reverb has no effect", energy1000 > 0);
476            result = true;
477        } catch (IllegalArgumentException e) {
478            msg = msg.concat(": Bad parameter value");
479            loge(msg, "Bad parameter value");
480        } catch (UnsupportedOperationException e) {
481            msg = msg.concat(": get parameter() rejected");
482            loge(msg, "get parameter() rejected");
483        } catch (IllegalStateException e) {
484            msg = msg.concat("get parameter() called in wrong state");
485            loge(msg, "get parameter() called in wrong state");
486        } catch (InterruptedException e) {
487            loge(msg, "sleep() interrupted");
488        }
489        finally {
490            if (mp != null) {
491                mp.release();
492            }
493            if (vc != null) {
494                vc.release();
495            }
496            if (rvb != null) {
497                rvb.release();
498            }
499            if (probe != null) {
500                probe.release();
501            }
502            am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
503            am.setRingerMode(ringerMode);
504        }
505        assertTrue(msg, result);
506    }
507
508    //-----------------------------------------------------------------
509    // private methods
510    //----------------------------------
511
512    private void getReverb(int session) {
513         if (mReverb == null || session != mSession) {
514             if (session != mSession && mReverb != null) {
515                 mReverb.release();
516                 mReverb = null;
517             }
518             try {
519                mReverb = new EnvironmentalReverb(0, session);
520                mSession = session;
521            } catch (IllegalArgumentException e) {
522                Log.e(TAG, "getReverb() EnvironmentalReverb not found exception: "+e);
523            } catch (UnsupportedOperationException e) {
524                Log.e(TAG, "getReverb() Effect library not loaded exception: "+e);
525            }
526         }
527         assertNotNull("could not create mReverb", mReverb);
528    }
529
530    private void releaseReverb() {
531        if (mReverb != null) {
532            mReverb.release();
533            mReverb = null;
534        }
535   }
536
537}
538