Home | History | Annotate | Download | only in audio
      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 
     17 package com.android.mediaframeworktest.functional.audio;
     18 
     19 import com.android.mediaframeworktest.MediaFrameworkTest;
     20 import com.android.mediaframeworktest.MediaNames;
     21 import com.android.mediaframeworktest.functional.EnergyProbe;
     22 import android.content.Context;
     23 import android.content.res.AssetFileDescriptor;
     24 import android.media.audiofx.AudioEffect;
     25 import android.media.AudioManager;
     26 import android.media.audiofx.EnvironmentalReverb;
     27 import android.media.audiofx.Visualizer;
     28 import android.media.MediaPlayer;
     29 
     30 import android.os.Looper;
     31 import android.test.suitebuilder.annotation.LargeTest;
     32 import android.test.suitebuilder.annotation.MediumTest;
     33 import android.test.suitebuilder.annotation.Suppress;
     34 import android.test.ActivityInstrumentationTestCase2;
     35 import android.util.Log;
     36 
     37 import java.nio.ByteOrder;
     38 import java.nio.ByteBuffer;
     39 import java.util.UUID;
     40 
     41 /**
     42  * Junit / Instrumentation test case for the media AudioTrack api
     43 
     44  */
     45 public 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