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