Home | History | Annotate | Download | only in functional
      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;
     18 
     19 import com.android.mediaframeworktest.MediaFrameworkTest;
     20 import com.android.mediaframeworktest.MediaNames;
     21 import android.content.Context;
     22 import android.content.res.AssetFileDescriptor;
     23 import android.media.audiofx.AudioEffect;
     24 import android.media.AudioManager;
     25 import android.media.audiofx.Visualizer;
     26 import android.media.MediaPlayer;
     27 
     28 import android.os.Looper;
     29 import android.test.suitebuilder.annotation.LargeTest;
     30 import android.test.suitebuilder.annotation.MediumTest;
     31 import android.test.suitebuilder.annotation.Suppress;
     32 import android.test.ActivityInstrumentationTestCase2;
     33 import android.util.Log;
     34 
     35 import java.nio.ByteOrder;
     36 import java.nio.ByteBuffer;
     37 import java.util.UUID;
     38 
     39 /**
     40  * Junit / Instrumentation test case for the media AudioTrack api
     41 
     42  */
     43 public 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 
    506