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 volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
    357         am.setStreamVolume(AudioManager.STREAM_MUSIC,
    358                            am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
    359                            0);
    360         getReverb(0);
    361         try {
    362             probe = new EnergyProbe(0);
    363             // creating a volume controller on output mix ensures that ro.audio.silent mutes
    364             // audio after the effects and not before
    365             vc = new AudioEffect(
    366                                 AudioEffect.EFFECT_TYPE_NULL,
    367                                 VOLUME_EFFECT_UUID,
    368                                 0,
    369                                 0);
    370             vc.setEnabled(true);
    371 
    372             mp = new MediaPlayer();
    373             mp.setDataSource(MediaNames.SINE_200_1000);
    374             mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
    375             mp.attachAuxEffect(mReverb.getId());
    376             mp.setAuxEffectSendLevel(1.0f);
    377             mReverb.setRoomLevel((short)0);
    378             mReverb.setReverbLevel((short)0);
    379             mReverb.setDecayTime(2000);
    380             mReverb.setEnabled(true);
    381             mp.prepare();
    382             mp.start();
    383             Thread.sleep(1000);
    384             mp.stop();
    385             Thread.sleep(300);
    386             // measure energy around 1kHz after media player was stopped for 300 ms
    387             int energy1000 = probe.capture(1000);
    388             assertTrue(msg + ": reverb has no effect", energy1000 > 0);
    389             result = true;
    390         } catch (IllegalArgumentException e) {
    391             msg = msg.concat(": Bad parameter value");
    392             loge(msg, "Bad parameter value");
    393         } catch (UnsupportedOperationException e) {
    394             msg = msg.concat(": get parameter() rejected");
    395             loge(msg, "get parameter() rejected");
    396         } catch (IllegalStateException e) {
    397             msg = msg.concat("get parameter() called in wrong state");
    398             loge(msg, "get parameter() called in wrong state");
    399         } catch (InterruptedException e) {
    400             loge(msg, "sleep() interrupted");
    401         }
    402         finally {
    403             releaseReverb();
    404             if (mp != null) {
    405                 mp.release();
    406             }
    407             if (vc != null) {
    408                 vc.release();
    409             }
    410             if (probe != null) {
    411                 probe.release();
    412             }
    413             am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
    414         }
    415         assertTrue(msg, result);
    416     }
    417 
    418     //Test case 2.1: test actual insert reverb influence on sound
    419     @LargeTest
    420     public void test2_1InsertSoundModification() throws Exception {
    421         boolean result = false;
    422         String msg = "test2_1InsertSoundModification()";
    423         EnergyProbe probe = null;
    424         AudioEffect vc = null;
    425         MediaPlayer mp = null;
    426         AudioEffect rvb = null;
    427         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
    428         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
    429         am.setStreamVolume(AudioManager.STREAM_MUSIC,
    430                            am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
    431                            0);
    432         try {
    433             // creating a volume controller on output mix ensures that ro.audio.silent mutes
    434             // audio after the effects and not before
    435             vc = new AudioEffect(
    436                                 AudioEffect.EFFECT_TYPE_NULL,
    437                                 VOLUME_EFFECT_UUID,
    438                                 0,
    439                                 0);
    440             vc.setEnabled(true);
    441 
    442             mp = new MediaPlayer();
    443             mp.setDataSource(MediaNames.SINE_200_1000);
    444             mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
    445 
    446             // create reverb with UUID instead of EnvironmentalReverb constructor otherwise an
    447             // auxiliary reverb will be chosen by the effect framework as we are on session 0
    448             rvb = new AudioEffect(
    449                         AudioEffect.EFFECT_TYPE_NULL,
    450                         ENV_REVERB_EFFECT_UUID,
    451                         0,
    452                         0);
    453 
    454             rvb.setParameter(EnvironmentalReverb.PARAM_ROOM_LEVEL, (short)0);
    455             rvb.setParameter(EnvironmentalReverb.PARAM_REVERB_LEVEL, (short)0);
    456             rvb.setParameter(EnvironmentalReverb.PARAM_DECAY_TIME, 2000);
    457             rvb.setEnabled(true);
    458 
    459             // create probe after reverb so that it is chained behind the reverb in the
    460             // effect chain
    461             probe = new EnergyProbe(0);
    462 
    463             mp.prepare();
    464             mp.start();
    465             Thread.sleep(1000);
    466             mp.stop();
    467             Thread.sleep(300);
    468             // measure energy around 1kHz after media player was stopped for 300 ms
    469             int energy1000 = probe.capture(1000);
    470             assertTrue(msg + ": reverb has no effect", energy1000 > 0);
    471             result = true;
    472         } catch (IllegalArgumentException e) {
    473             msg = msg.concat(": Bad parameter value");
    474             loge(msg, "Bad parameter value");
    475         } catch (UnsupportedOperationException e) {
    476             msg = msg.concat(": get parameter() rejected");
    477             loge(msg, "get parameter() rejected");
    478         } catch (IllegalStateException e) {
    479             msg = msg.concat("get parameter() called in wrong state");
    480             loge(msg, "get parameter() called in wrong state");
    481         } catch (InterruptedException e) {
    482             loge(msg, "sleep() interrupted");
    483         }
    484         finally {
    485             if (mp != null) {
    486                 mp.release();
    487             }
    488             if (vc != null) {
    489                 vc.release();
    490             }
    491             if (rvb != null) {
    492                 rvb.release();
    493             }
    494             if (probe != null) {
    495                 probe.release();
    496             }
    497             am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
    498         }
    499         assertTrue(msg, result);
    500     }
    501 
    502     //-----------------------------------------------------------------
    503     // private methods
    504     //----------------------------------
    505 
    506     private void getReverb(int session) {
    507          if (mReverb == null || session != mSession) {
    508              if (session != mSession && mReverb != null) {
    509                  mReverb.release();
    510                  mReverb = null;
    511              }
    512              try {
    513                 mReverb = new EnvironmentalReverb(0, session);
    514                 mSession = session;
    515             } catch (IllegalArgumentException e) {
    516                 Log.e(TAG, "getReverb() EnvironmentalReverb not found exception: "+e);
    517             } catch (UnsupportedOperationException e) {
    518                 Log.e(TAG, "getReverb() Effect library not loaded exception: "+e);
    519             }
    520          }
    521          assertNotNull("could not create mReverb", mReverb);
    522     }
    523 
    524     private void releaseReverb() {
    525         if (mReverb != null) {
    526             mReverb.release();
    527             mReverb = null;
    528         }
    529    }
    530 
    531 }
    532