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