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