MediaVisualizerTest.java revision c81e0ad9bd438575389623f523a28e2e6ca69236
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 android.content.Context; 22import android.content.res.AssetFileDescriptor; 23import android.media.audiofx.AudioEffect; 24import android.media.AudioManager; 25import android.media.audiofx.Visualizer; 26import android.media.MediaPlayer; 27 28import android.os.Looper; 29import android.test.suitebuilder.annotation.LargeTest; 30import android.test.suitebuilder.annotation.MediumTest; 31import android.test.suitebuilder.annotation.Suppress; 32import android.test.ActivityInstrumentationTestCase2; 33import android.util.Log; 34 35import java.nio.ByteOrder; 36import java.nio.ByteBuffer; 37import java.util.UUID; 38 39/** 40 * Junit / Instrumentation test case for the media AudioTrack api 41 42 */ 43public class MediaVisualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { 44 private String TAG = "MediaVisualizerTest"; 45 private final static int MIN_CAPTURE_RATE_MAX = 20000; 46 private final static int MIN_SAMPLING_RATE = 8000000; 47 private final static int MAX_SAMPLING_RATE = 48000000; 48 private final static int MIN_CAPTURE_SIZE_MAX = 1024; 49 private final static int MAX_CAPTURE_SIZE_MIN = 128; 50 // Implementor UUID for volume controller effect defined in 51 // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp 52 private final static UUID VOLUME_EFFECT_UUID = 53 UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"); 54 55 private Visualizer mVisualizer = null; 56 private int mSession = -1; 57 private boolean mInitialized = false; 58 private Looper mLooper = null; 59 private final Object lock = new Object(); 60 private byte[] mWaveform = null; 61 private byte[] mFft = null; 62 private boolean mCaptureWaveform = false; 63 private boolean mCaptureFft = false; 64 65 public MediaVisualizerTest() { 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 releaseVisualizer(); 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 // VISUALIZER 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 Visualizer visualizer = null; 107 try { 108 visualizer = new Visualizer(0); 109 assertNotNull(msg + ": could not create Visualizer", visualizer); 110 result = true; 111 } catch (IllegalArgumentException e) { 112 msg = msg.concat(": Visualizer not found"); 113 } catch (UnsupportedOperationException e) { 114 msg = msg.concat(": Effect library not loaded"); 115 } finally { 116 if (visualizer != null) { 117 visualizer.release(); 118 } 119 } 120 assertTrue(msg, result); 121 } 122 123 124 //----------------------------------------------------------------- 125 // 1 - get/set parameters 126 //---------------------------------- 127 128 //Test case 1.0: check capture rate and sampling rate 129 @LargeTest 130 public void test1_0CaptureRates() throws Exception { 131 boolean result = false; 132 String msg = "test1_0CaptureRates()"; 133 getVisualizer(0); 134 try { 135 int captureRate = mVisualizer.getMaxCaptureRate(); 136 assertTrue(msg +": insufficient max capture rate", 137 captureRate >= MIN_CAPTURE_RATE_MAX); 138 int samplingRate = mVisualizer.getSamplingRate(); 139 assertTrue(msg +": invalid sampling rate", 140 samplingRate >= MIN_SAMPLING_RATE && samplingRate <= MAX_SAMPLING_RATE); 141 result = true; 142 } catch (IllegalArgumentException e) { 143 msg = msg.concat(": Bad parameter value"); 144 loge(msg, "Bad parameter value"); 145 } catch (UnsupportedOperationException e) { 146 msg = msg.concat(": get parameter() rejected"); 147 loge(msg, "get parameter() rejected"); 148 } catch (IllegalStateException e) { 149 msg = msg.concat("get parameter() called in wrong state"); 150 loge(msg, "get parameter() called in wrong state"); 151 } finally { 152 releaseVisualizer(); 153 } 154 assertTrue(msg, result); 155 } 156 157 //Test case 1.1: check capture size 158 @LargeTest 159 public void test1_1CaptureSize() throws Exception { 160 boolean result = false; 161 String msg = "test1_1CaptureSize()"; 162 getVisualizer(0); 163 try { 164 int[] range = mVisualizer.getCaptureSizeRange(); 165 assertTrue(msg +": insufficient min capture size", 166 range[0] <= MAX_CAPTURE_SIZE_MIN); 167 assertTrue(msg +": insufficient min capture size", 168 range[1] >= MIN_CAPTURE_SIZE_MAX); 169 mVisualizer.setCaptureSize(range[0]); 170 assertEquals(msg +": insufficient min capture size", 171 range[0], mVisualizer.getCaptureSize()); 172 mVisualizer.setCaptureSize(range[1]); 173 assertEquals(msg +": insufficient min capture size", 174 range[1], mVisualizer.getCaptureSize()); 175 result = true; 176 } catch (IllegalArgumentException e) { 177 msg = msg.concat(": Bad parameter value"); 178 loge(msg, "Bad parameter value"); 179 } catch (UnsupportedOperationException e) { 180 msg = msg.concat(": get parameter() rejected"); 181 loge(msg, "get parameter() rejected"); 182 } catch (IllegalStateException e) { 183 msg = msg.concat("get parameter() called in wrong state"); 184 loge(msg, "get parameter() called in wrong state"); 185 } finally { 186 releaseVisualizer(); 187 } 188 assertTrue(msg, result); 189 } 190 191 //----------------------------------------------------------------- 192 // 2 - check capture 193 //---------------------------------- 194 195 //Test case 2.0: test capture in polling mode 196 @LargeTest 197 public void test2_0PollingCapture() throws Exception { 198 boolean result = false; 199 String msg = "test2_0PollingCapture()"; 200 AudioEffect vc = null; 201 MediaPlayer mp = null; 202 AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); 203 int ringerMode = am.getRingerMode(); 204 am.setRingerMode(AudioManager.RINGER_MODE_NORMAL); 205 int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); 206 am.setStreamVolume(AudioManager.STREAM_MUSIC, 207 am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 208 0); 209 210 try { 211 // creating a volume controller on output mix ensures that ro.audio.silent mutes 212 // audio after the effects and not before 213 vc = new AudioEffect( 214 AudioEffect.EFFECT_TYPE_NULL, 215 VOLUME_EFFECT_UUID, 216 0, 217 0); 218 vc.setEnabled(true); 219 220 mp = new MediaPlayer(); 221 mp.setDataSource(MediaNames.SINE_200_1000); 222 mp.setAudioStreamType(AudioManager.STREAM_MUSIC); 223 getVisualizer(mp.getAudioSessionId()); 224 mVisualizer.setEnabled(true); 225 // check capture on silence 226 byte[] data = new byte[mVisualizer.getCaptureSize()]; 227 mVisualizer.getWaveForm(data); 228 int energy = computeEnergy(data, true); 229 assertEquals(msg +": getWaveForm reports energy for silence", 230 0, energy); 231 mVisualizer.getFft(data); 232 energy = computeEnergy(data, false); 233 assertEquals(msg +": getFft reports energy for silence", 234 0, energy); 235 mp.prepare(); 236 mp.start(); 237 Thread.sleep(500); 238 // check capture on sound 239 mVisualizer.getWaveForm(data); 240 energy = computeEnergy(data, true); 241 assertTrue(msg +": getWaveForm reads insufficient level", 242 energy > 0); 243 mVisualizer.getFft(data); 244 energy = computeEnergy(data, false); 245 assertTrue(msg +": getFft reads insufficient level", 246 energy > 0); 247 result = true; 248 } catch (IllegalArgumentException e) { 249 msg = msg.concat(": Bad parameter value"); 250 loge(msg, "Bad parameter value"); 251 } catch (UnsupportedOperationException e) { 252 msg = msg.concat(": get parameter() rejected"); 253 loge(msg, "get parameter() rejected"); 254 } catch (IllegalStateException e) { 255 msg = msg.concat("get parameter() called in wrong state"); 256 loge(msg, "get parameter() called in wrong state"); 257 } catch (InterruptedException e) { 258 loge(msg, "sleep() interrupted"); 259 } 260 finally { 261 releaseVisualizer(); 262 if (mp != null) { 263 mp.release(); 264 } 265 if (vc != null) { 266 vc.release(); 267 } 268 am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); 269 am.setRingerMode(ringerMode); 270 } 271 assertTrue(msg, result); 272 } 273 274 //Test case 2.1: test capture with listener 275 @LargeTest 276 public void test2_1ListenerCapture() throws Exception { 277 boolean result = false; 278 String msg = "test2_1ListenerCapture()"; 279 AudioEffect vc = null; 280 MediaPlayer mp = null; 281 AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); 282 int ringerMode = am.getRingerMode(); 283 am.setRingerMode(AudioManager.RINGER_MODE_NORMAL); 284 int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); 285 am.setStreamVolume(AudioManager.STREAM_MUSIC, 286 am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 287 0); 288 289 try { 290 // creating a volume controller on output mix ensures that ro.audio.silent mutes 291 // audio after the effects and not before 292 vc = new AudioEffect( 293 AudioEffect.EFFECT_TYPE_NULL, 294 VOLUME_EFFECT_UUID, 295 0, 296 0); 297 vc.setEnabled(true); 298 299 mp = new MediaPlayer(); 300 mp.setDataSource(MediaNames.SINE_200_1000); 301 mp.setAudioStreamType(AudioManager.STREAM_MUSIC); 302 303 getVisualizer(mp.getAudioSessionId()); 304 createListenerLooper(); 305 synchronized(lock) { 306 try { 307 lock.wait(1000); 308 } catch(Exception e) { 309 Log.e(TAG, "Looper creation: wait was interrupted."); 310 } 311 } 312 assertTrue(mInitialized); 313 314 mVisualizer.setEnabled(true); 315 316 // check capture on silence 317 synchronized(lock) { 318 try { 319 mCaptureWaveform = true; 320 lock.wait(1000); 321 mCaptureWaveform = false; 322 } catch(Exception e) { 323 Log.e(TAG, "Capture waveform: wait was interrupted."); 324 } 325 } 326 assertNotNull(msg +": waveform capture failed", mWaveform); 327 int energy = computeEnergy(mWaveform, true); 328 assertEquals(msg +": getWaveForm reports energy for silence", 329 0, energy); 330 331 synchronized(lock) { 332 try { 333 mCaptureFft = true; 334 lock.wait(1000); 335 mCaptureFft = false; 336 } catch(Exception e) { 337 Log.e(TAG, "Capture FFT: wait was interrupted."); 338 } 339 } 340 assertNotNull(msg +": FFT capture failed", mFft); 341 energy = computeEnergy(mFft, false); 342 assertEquals(msg +": getFft reports energy for silence", 343 0, energy); 344 345 mp.prepare(); 346 mp.start(); 347 Thread.sleep(500); 348 349 // check capture on sound 350 synchronized(lock) { 351 try { 352 mCaptureWaveform = true; 353 lock.wait(1000); 354 mCaptureWaveform = false; 355 } catch(Exception e) { 356 Log.e(TAG, "Capture waveform: wait was interrupted."); 357 } 358 } 359 assertNotNull(msg +": waveform capture failed", mWaveform); 360 energy = computeEnergy(mWaveform, true); 361 assertTrue(msg +": getWaveForm reads insufficient level", 362 energy > 0); 363 364 synchronized(lock) { 365 try { 366 mCaptureFft = true; 367 lock.wait(1000); 368 mCaptureFft = false; 369 } catch(Exception e) { 370 Log.e(TAG, "Capture FFT: wait was interrupted."); 371 } 372 } 373 assertNotNull(msg +": FFT capture failed", mFft); 374 energy = computeEnergy(mFft, false); 375 assertTrue(msg +": getFft reads insufficient level", 376 energy > 0); 377 378 result = true; 379 } catch (IllegalArgumentException e) { 380 msg = msg.concat(": Bad parameter value"); 381 loge(msg, "Bad parameter value"); 382 } catch (UnsupportedOperationException e) { 383 msg = msg.concat(": get parameter() rejected"); 384 loge(msg, "get parameter() rejected"); 385 } catch (IllegalStateException e) { 386 msg = msg.concat("get parameter() called in wrong state"); 387 loge(msg, "get parameter() called in wrong state"); 388 } catch (InterruptedException e) { 389 loge(msg, "sleep() interrupted"); 390 } 391 finally { 392 terminateListenerLooper(); 393 releaseVisualizer(); 394 if (mp != null) { 395 mp.release(); 396 } 397 if (vc != null) { 398 vc.release(); 399 } 400 am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); 401 am.setRingerMode(ringerMode); 402 } 403 assertTrue(msg, result); 404 } 405 406 //----------------------------------------------------------------- 407 // private methods 408 //---------------------------------- 409 410 private int computeEnergy(byte[] data, boolean unsigned) { 411 int energy = 0; 412 if (data.length != 0) { 413 for (int i = 0; i < data.length; i++) { 414 int tmp; 415 // convert from unsigned 8 bit to signed 16 bit 416 if (unsigned) { 417 tmp = ((int)data[i] & 0xFF) - 128; 418 } else { 419 tmp = (int)data[i]; 420 } 421 energy += tmp*tmp; 422 } 423 energy /= data.length; 424 } 425 return energy; 426 } 427 428 private void getVisualizer(int session) { 429 if (mVisualizer == null || session != mSession) { 430 if (session != mSession && mVisualizer != null) { 431 mVisualizer.release(); 432 mVisualizer = null; 433 } 434 try { 435 mVisualizer = new Visualizer(session); 436 mSession = session; 437 } catch (IllegalArgumentException e) { 438 Log.e(TAG, "getVisualizer() Visualizer not found exception: "+e); 439 } catch (UnsupportedOperationException e) { 440 Log.e(TAG, "getVisualizer() Effect library not loaded exception: "+e); 441 } 442 } 443 assertNotNull("could not create mVisualizer", mVisualizer); 444 } 445 446 private void releaseVisualizer() { 447 if (mVisualizer != null) { 448 mVisualizer.release(); 449 mVisualizer = null; 450 } 451 } 452 453 private void createListenerLooper() { 454 455 new Thread() { 456 @Override 457 public void run() { 458 // Set up a looper to be used by mEffect. 459 Looper.prepare(); 460 461 // Save the looper so that we can terminate this thread 462 // after we are done with it. 463 mLooper = Looper.myLooper(); 464 465 if (mVisualizer != null) { 466 mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() { 467 public void onWaveFormDataCapture( 468 Visualizer visualizer, byte[] waveform, int samplingRate) { 469 synchronized(lock) { 470 if (visualizer == mVisualizer) { 471 if (mCaptureWaveform) { 472 mWaveform = waveform; 473 lock.notify(); 474 } 475 } 476 } 477 } 478 479 public void onFftDataCapture( 480 Visualizer visualizer, byte[] fft, int samplingRate) { 481 synchronized(lock) { 482 if (visualizer == mVisualizer) { 483 if (mCaptureFft) { 484 mFft = fft; 485 lock.notify(); 486 } 487 } 488 } 489 } 490 }, 491 10000, 492 true, 493 true); 494 } 495 496 synchronized(lock) { 497 mInitialized = true; 498 lock.notify(); 499 } 500 Looper.loop(); // Blocks forever until Looper.quit() is called. 501 } 502 }.start(); 503 } 504 /* 505 * Terminates the listener looper thread. 506 */ 507 private void terminateListenerLooper() { 508 if (mLooper != null) { 509 mLooper.quit(); 510 mLooper = null; 511 } 512 } 513 514} 515