Home | History | Annotate | Download | only in cts
      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 android.media.cts;
     18 
     19 import android.media.cts.R;
     20 
     21 import android.content.Context;
     22 import android.media.audiofx.AudioEffect;
     23 import android.media.AudioFormat;
     24 import android.media.AudioManager;
     25 import android.media.MediaPlayer;
     26 import android.media.audiofx.Visualizer;
     27 import android.media.audiofx.Visualizer.MeasurementPeakRms;
     28 import android.os.Looper;
     29 import android.platform.test.annotations.AppModeFull;
     30 import android.test.AndroidTestCase;
     31 import java.util.UUID;
     32 import android.util.Log;
     33 
     34 @AppModeFull(reason = "TODO: evaluate and port to instant")
     35 public class VisualizerTest extends PostProcTestBase {
     36 
     37     private String TAG = "VisualizerTest";
     38     private final static int MIN_CAPTURE_RATE_MAX = 10000; // 10Hz
     39     private final static int MIN_CAPTURE_SIZE_MAX = 1024;
     40     private final static int MAX_CAPTURE_SIZE_MIN = 512;
     41     private final static int MAX_LOOPER_WAIT_COUNT = 10;
     42 
     43     private Visualizer mVisualizer = null;
     44     private byte[] mWaveform = null;
     45     private byte[] mFft = null;
     46     private boolean mCaptureWaveform = false;
     47     private boolean mCaptureFft = false;
     48     private Thread mListenerThread;
     49 
     50     //-----------------------------------------------------------------
     51     // VISUALIZER TESTS:
     52     //----------------------------------
     53 
     54     //-----------------------------------------------------------------
     55     // 0 - constructor
     56     //----------------------------------
     57 
     58     //Test case 0.0: test constructor and release
     59     public void test0_0ConstructorAndRelease() throws Exception {
     60         Visualizer visualizer = null;
     61         try {
     62             visualizer = new Visualizer(0);
     63         } catch (IllegalArgumentException e) {
     64             fail("Visualizer not found");
     65         } catch (UnsupportedOperationException e) {
     66             fail("Effect library not loaded");
     67         } finally {
     68             if (visualizer != null) {
     69                 visualizer.release();
     70             }
     71         }
     72     }
     73 
     74 
     75     //-----------------------------------------------------------------
     76     // 1 - get/set parameters
     77     //----------------------------------
     78 
     79     //Test case 1.0: capture rates
     80     public void test1_0CaptureRates() throws Exception {
     81         getVisualizer(0);
     82         try {
     83             int captureRate = mVisualizer.getMaxCaptureRate();
     84             assertTrue("insufficient max capture rate",
     85                     captureRate >= MIN_CAPTURE_RATE_MAX);
     86             int samplingRate = mVisualizer.getSamplingRate();
     87         } catch (IllegalArgumentException e) {
     88             fail("Bad parameter value");
     89         } catch (UnsupportedOperationException e) {
     90             fail("get parameter() rejected");
     91         } catch (IllegalStateException e) {
     92             fail("get parameter() called in wrong state");
     93         } finally {
     94             releaseVisualizer();
     95         }
     96     }
     97 
     98     //Test case 1.1: test capture size
     99     public void test1_1CaptureSize() throws Exception {
    100         getVisualizer(0);
    101         try {
    102             int[] range = mVisualizer.getCaptureSizeRange();
    103             assertTrue("insufficient min capture size",
    104                     range[0] <= MAX_CAPTURE_SIZE_MIN);
    105             assertTrue("insufficient min capture size",
    106                     range[1] >= MIN_CAPTURE_SIZE_MAX);
    107             mVisualizer.setCaptureSize(range[0]);
    108             assertEquals("insufficient min capture size",
    109                     range[0], mVisualizer.getCaptureSize());
    110             mVisualizer.setCaptureSize(range[1]);
    111             assertEquals("insufficient min capture size",
    112                     range[1], mVisualizer.getCaptureSize());
    113         } catch (IllegalArgumentException e) {
    114             fail("Bad parameter value");
    115         } catch (UnsupportedOperationException e) {
    116             fail("get parameter() rejected");
    117         } catch (IllegalStateException e) {
    118             fail("get parameter() called in wrong state");
    119         } finally {
    120             releaseVisualizer();
    121         }
    122     }
    123 
    124     //-----------------------------------------------------------------
    125     // 2 - check capture
    126     //----------------------------------
    127 
    128     //Test case 2.0: test capture in polling mode
    129     public void test2_0PollingCapture() throws Exception {
    130         if (!hasAudioOutput()) {
    131             Log.w(TAG,"AUDIO_OUTPUT feature not found. This system might not have a valid "
    132                     + "audio output HAL");
    133             return;
    134         }
    135         try {
    136             getVisualizer(0);
    137             mVisualizer.setEnabled(true);
    138             assertTrue("visualizer not enabled", mVisualizer.getEnabled());
    139             Thread.sleep(100);
    140             // check capture on silence
    141             byte[] data = new byte[mVisualizer.getCaptureSize()];
    142             mVisualizer.getWaveForm(data);
    143             int energy = computeEnergy(data, true);
    144             assertEquals("getWaveForm reports energy for silence",
    145                     0, energy);
    146             mVisualizer.getFft(data);
    147             energy = computeEnergy(data, false);
    148             assertEquals("getFft reports energy for silence",
    149                     0, energy);
    150 
    151         } catch (IllegalStateException e) {
    152             fail("method called in wrong state");
    153         } catch (InterruptedException e) {
    154             fail("sleep() interrupted");
    155         } finally {
    156             releaseVisualizer();
    157         }
    158     }
    159 
    160     //Test case 2.1: test capture with listener
    161     public void test2_1ListenerCapture() throws Exception {
    162         if (!hasAudioOutput()) {
    163             Log.w(TAG,"AUDIO_OUTPUT feature not found. This system might not have a valid "
    164                     + "audio output HAL");
    165             return;
    166         }
    167         try {
    168             getVisualizer(0);
    169             synchronized(mLock) {
    170                 mInitialized = false;
    171                 createListenerLooper();
    172                 waitForLooperInitialization_l();
    173             }
    174             mVisualizer.setEnabled(true);
    175             assertTrue("visualizer not enabled", mVisualizer.getEnabled());
    176 
    177             Thread.sleep(100);
    178 
    179             // check capture on silence
    180             synchronized(mLock) {
    181                 mCaptureWaveform = true;
    182                 int looperWaitCount = MAX_LOOPER_WAIT_COUNT;
    183                 while ((mWaveform == null) && (looperWaitCount-- > 0)) {
    184                     try {
    185                         mLock.wait();
    186                     } catch(Exception e) {
    187                     }
    188                 }
    189                 mCaptureWaveform = false;
    190             }
    191             assertNotNull("waveform capture failed", mWaveform);
    192             int energy = computeEnergy(mWaveform, true);
    193             assertEquals("getWaveForm reports energy for silence",
    194                     0, energy);
    195 
    196             synchronized(mLock) {
    197                 mCaptureFft = true;
    198                 int looperWaitCount = MAX_LOOPER_WAIT_COUNT;
    199                 while ((mFft == null) && (looperWaitCount-- > 0)) {
    200                     try {
    201                         mLock.wait();
    202                     } catch(Exception e) {
    203                     }
    204                 }
    205                 mCaptureFft = false;
    206             }
    207             assertNotNull("FFT capture failed", mFft);
    208             energy = computeEnergy(mFft, false);
    209             assertEquals("getFft reports energy for silence",
    210                     0, energy);
    211 
    212         } catch (IllegalStateException e) {
    213             fail("method called in wrong state");
    214         } catch (InterruptedException e) {
    215             fail("sleep() interrupted");
    216         } finally {
    217             terminateListenerLooper();
    218             releaseVisualizer();
    219         }
    220     }
    221 
    222     //-----------------------------------------------------------------
    223     // 3 - check measurement mode MEASUREMENT_MODE_NONE
    224     //----------------------------------
    225 
    226     //Test case 3.0: test setting NONE measurement mode
    227     public void test3_0MeasurementModeNone() throws Exception {
    228         if (!hasAudioOutput()) {
    229             return;
    230         }
    231         try {
    232             getVisualizer(0);
    233             mVisualizer.setEnabled(true);
    234             assertTrue("visualizer not enabled", mVisualizer.getEnabled());
    235             Thread.sleep(100);
    236 
    237             int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_NONE);
    238             assertEquals("setMeasurementMode for NONE doesn't report success",
    239                     Visualizer.SUCCESS, status);
    240 
    241             int mode = mVisualizer.getMeasurementMode();
    242             assertEquals("getMeasurementMode reports NONE",
    243                     Visualizer.MEASUREMENT_MODE_NONE, mode);
    244 
    245         } catch (IllegalStateException e) {
    246             fail("method called in wrong state");
    247         } catch (InterruptedException e) {
    248             fail("sleep() interrupted");
    249         } finally {
    250             releaseVisualizer();
    251         }
    252     }
    253 
    254     //-----------------------------------------------------------------
    255     // 4 - check measurement mode MEASUREMENT_MODE_PEAK_RMS
    256     //----------------------------------
    257 
    258     //Test case 4.0: test setting peak / RMS measurement mode
    259     public void test4_0MeasurementModePeakRms() throws Exception {
    260         if (!hasAudioOutput()) {
    261             return;
    262         }
    263         try {
    264             getVisualizer(0);
    265             mVisualizer.setEnabled(true);
    266             assertTrue("visualizer not enabled", mVisualizer.getEnabled());
    267             Thread.sleep(100);
    268 
    269             int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
    270             assertEquals("setMeasurementMode for PEAK_RMS doesn't report success",
    271                     Visualizer.SUCCESS, status);
    272 
    273             int mode = mVisualizer.getMeasurementMode();
    274             assertEquals("getMeasurementMode doesn't report PEAK_RMS",
    275                     Visualizer.MEASUREMENT_MODE_PEAK_RMS, mode);
    276 
    277         } catch (IllegalStateException e) {
    278             fail("method called in wrong state");
    279         } catch (InterruptedException e) {
    280             fail("sleep() interrupted");
    281         } finally {
    282             releaseVisualizer();
    283         }
    284     }
    285 
    286     //Test case 4.1: test measurement of peak / RMS
    287     public void test4_1MeasurePeakRms() throws Exception {
    288         if (!hasAudioOutput()) {
    289             return;
    290         }
    291         AudioEffect vc = null;
    292         try {
    293             // this test will play a 1kHz sine wave with peaks at -40dB
    294             MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.sine1khzm40db);
    295             final int EXPECTED_PEAK_MB = -4015;
    296             final int EXPECTED_RMS_MB =  -4300;
    297             final int MAX_MEASUREMENT_ERROR_MB = 2000;
    298             assertNotNull("null MediaPlayer", mp);
    299 
    300             // creating a volume controller on output mix ensures that ro.audio.silent mutes
    301             // audio after the effects and not before
    302             vc = new AudioEffect(
    303                     AudioEffect.EFFECT_TYPE_NULL,
    304                     UUID.fromString(BUNDLE_VOLUME_EFFECT_UUID),
    305                     0,
    306                     mp.getAudioSessionId());
    307             vc.setEnabled(true);
    308 
    309             AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
    310             assertNotNull("null AudioManager", am);
    311             int originalVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
    312             am.setStreamVolume(AudioManager.STREAM_MUSIC,
    313                     am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
    314             getVisualizer(mp.getAudioSessionId());
    315             mp.setLooping(true);
    316             mp.start();
    317 
    318             mVisualizer.setEnabled(true);
    319             assertTrue("visualizer not enabled", mVisualizer.getEnabled());
    320             Thread.sleep(100);
    321             int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
    322             assertEquals("setMeasurementMode() for PEAK_RMS doesn't report success",
    323                     Visualizer.SUCCESS, status);
    324             // make sure we're playing long enough so the measurement is valid
    325             int currentPosition = mp.getCurrentPosition();
    326             final int maxTry = 100;
    327             int tryCount = 0;
    328             while (currentPosition < 200 && tryCount < maxTry) {
    329                 Thread.sleep(50);
    330                 currentPosition = mp.getCurrentPosition();
    331                 tryCount++;
    332             }
    333             assertTrue("MediaPlayer not ready", tryCount < maxTry);
    334 
    335             MeasurementPeakRms measurement = new MeasurementPeakRms();
    336             status = mVisualizer.getMeasurementPeakRms(measurement);
    337             mp.stop();
    338             mp.release();
    339             am.setStreamVolume(AudioManager.STREAM_MUSIC, originalVolume, 0);
    340             assertEquals("getMeasurementPeakRms() reports failure",
    341                     Visualizer.SUCCESS, status);
    342             Log.i("VisTest", "peak="+measurement.mPeak+"  rms="+measurement.mRms);
    343             int deltaPeak = Math.abs(measurement.mPeak - EXPECTED_PEAK_MB);
    344             int deltaRms =  Math.abs(measurement.mRms - EXPECTED_RMS_MB);
    345             assertTrue("peak deviation in mB=" + deltaPeak, deltaPeak < MAX_MEASUREMENT_ERROR_MB);
    346             assertTrue("RMS deviation in mB=" + deltaRms, deltaRms < MAX_MEASUREMENT_ERROR_MB);
    347 
    348         } catch (IllegalStateException e) {
    349             fail("method called in wrong state");
    350         } catch (InterruptedException e) {
    351             fail("sleep() interrupted");
    352         } finally {
    353             if (vc != null)
    354                 vc.release();
    355             releaseVisualizer();
    356         }
    357     }
    358 
    359     //Test case 4.2: test measurement of peak / RMS in Long MP3
    360     public void test4_2MeasurePeakRmsLongMP3() throws Exception {
    361         if (!hasAudioOutput()) {
    362             return;
    363         }
    364         AudioEffect vc = null;
    365         try {
    366             // this test will play a 1kHz sine wave with peaks at -40dB
    367             MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.sine1khzs40dblong);
    368             final int EXPECTED_PEAK_MB = -4015;
    369             final int EXPECTED_RMS_MB =  -4300;
    370             final int MAX_MEASUREMENT_ERROR_MB = 2000;
    371             assertNotNull("null MediaPlayer", mp);
    372 
    373             // creating a volume controller on output mix ensures that ro.audio.silent mutes
    374             // audio after the effects and not before
    375             vc = new AudioEffect(
    376                     AudioEffect.EFFECT_TYPE_NULL,
    377                     UUID.fromString(BUNDLE_VOLUME_EFFECT_UUID),
    378                     0,
    379                     mp.getAudioSessionId());
    380             vc.setEnabled(true);
    381 
    382             AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
    383             assertNotNull("null AudioManager", am);
    384             int originalVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
    385             am.setStreamVolume(AudioManager.STREAM_MUSIC,
    386                     am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
    387             getVisualizer(mp.getAudioSessionId());
    388             mp.start();
    389 
    390             mVisualizer.setEnabled(true);
    391             assertTrue("visualizer not enabled", mVisualizer.getEnabled());
    392             Thread.sleep(100);
    393             int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
    394             assertEquals("setMeasurementMode() for PEAK_RMS doesn't report success",
    395                     Visualizer.SUCCESS, status);
    396             // make sure we're playing long enough so the measurement is valid
    397             int currentPosition = mp.getCurrentPosition();
    398             final int maxTry = 100;
    399             int tryCount = 0;
    400             while (currentPosition < 400 && tryCount < maxTry) {
    401                 Thread.sleep(50);
    402                 currentPosition = mp.getCurrentPosition();
    403                 tryCount++;
    404             }
    405             assertTrue("MediaPlayer not ready", tryCount < maxTry);
    406 
    407             MeasurementPeakRms measurement = new MeasurementPeakRms();
    408             status = mVisualizer.getMeasurementPeakRms(measurement);
    409             mp.stop();
    410             mp.release();
    411             am.setStreamVolume(AudioManager.STREAM_MUSIC, originalVolume, 0);
    412             assertEquals("getMeasurementPeakRms() reports failure",
    413                     Visualizer.SUCCESS, status);
    414             Log.i("VisTest", "peak="+measurement.mPeak+"  rms="+measurement.mRms);
    415             int deltaPeak = Math.abs(measurement.mPeak - EXPECTED_PEAK_MB);
    416             int deltaRms =  Math.abs(measurement.mRms - EXPECTED_RMS_MB);
    417             assertTrue("peak deviation in mB=" + deltaPeak, deltaPeak < MAX_MEASUREMENT_ERROR_MB);
    418             assertTrue("RMS deviation in mB=" + deltaRms, deltaRms < MAX_MEASUREMENT_ERROR_MB);
    419 
    420         } catch (IllegalStateException e) {
    421             fail("method called in wrong state");
    422         } catch (InterruptedException e) {
    423             fail("sleep() interrupted");
    424         } finally {
    425             if (vc != null)
    426                 vc.release();
    427             releaseVisualizer();
    428         }
    429     }
    430 
    431     //-----------------------------------------------------------------
    432     // private methods
    433     //----------------------------------
    434 
    435     private int computeEnergy(byte[] data, boolean pcm) {
    436         int energy = 0;
    437         if (data.length != 0) {
    438             if (pcm) {
    439                 for (int i = 0; i < data.length; i++) {
    440                     int tmp = ((int)data[i] & 0xFF) - 128;
    441                     energy += tmp*tmp;
    442                 }
    443             } else {
    444                 // Note that data[0] is real part of FFT at DC
    445                 // and data[1] is real part of FFT at Nyquist,
    446                 // but for the purposes of energy calculation we
    447                 // don't need to treat them specially.
    448                 for (int i = 0; i < data.length; i += 2) {
    449                     int real = (int)data[i];
    450                     int img = (int)data[i + 1];
    451                     energy += real * real + img * img;
    452                 }
    453             }
    454         }
    455         return energy;
    456     }
    457 
    458     private void getVisualizer(int session) {
    459          if (mVisualizer == null || session != mSession) {
    460              if (session != mSession && mVisualizer != null) {
    461                  mVisualizer.release();
    462                  mVisualizer = null;
    463              }
    464              try {
    465                 mVisualizer = new Visualizer(session);
    466                 mSession = session;
    467             } catch (IllegalArgumentException e) {
    468                 Log.e(TAG, "getVisualizer() Visualizer not found exception: "+e);
    469             } catch (UnsupportedOperationException e) {
    470                 Log.e(TAG, "getVisualizer() Effect library not loaded exception: "+e);
    471             }
    472          }
    473          assertNotNull("could not create mVisualizer", mVisualizer);
    474     }
    475 
    476     private void releaseVisualizer() {
    477         if (mVisualizer != null) {
    478             mVisualizer.release();
    479             mVisualizer = null;
    480         }
    481     }
    482 
    483     private void waitForLooperInitialization_l() {
    484         int looperWaitCount = MAX_LOOPER_WAIT_COUNT;
    485         while (!mInitialized && (looperWaitCount-- > 0)) {
    486             try {
    487                 mLock.wait();
    488             } catch(Exception e) {
    489             }
    490         }
    491         assertTrue(mInitialized);
    492     }
    493 
    494     private void createListenerLooper() {
    495         mListenerThread = new Thread() {
    496             @Override
    497             public void run() {
    498                 // Set up a looper to be used by mEffect.
    499                 Looper.prepare();
    500 
    501                 // Save the looper so that we can terminate this thread
    502                 // after we are done with it.
    503                 mLooper = Looper.myLooper();
    504 
    505                 synchronized(mLock) {
    506                     if (mVisualizer != null) {
    507                         mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
    508                             public void onWaveFormDataCapture(
    509                                     Visualizer visualizer, byte[] waveform, int samplingRate) {
    510                                 synchronized(mLock) {
    511                                     if (visualizer == mVisualizer) {
    512                                         if (mCaptureWaveform) {
    513                                             mWaveform = waveform;
    514                                             mLock.notify();
    515                                         }
    516                                     }
    517                                 }
    518                             }
    519 
    520                             public void onFftDataCapture(
    521                                     Visualizer visualizer, byte[] fft, int samplingRate) {
    522                                 synchronized(mLock) {
    523                                     Log.e(TAG, "onFftDataCapture 2 mCaptureFft: "+mCaptureFft);
    524                                     if (visualizer == mVisualizer) {
    525                                         if (mCaptureFft) {
    526                                             mFft = fft;
    527                                             mLock.notify();
    528                                         }
    529                                     }
    530                                 }
    531                             }
    532                         },
    533                         10000,
    534                         true,
    535                         true);
    536                     }
    537                     mInitialized = true;
    538                     mLock.notify();
    539                 }
    540                 Looper.loop();  // Blocks forever until Looper.quit() is called.
    541             }
    542         };
    543         mListenerThread.start();
    544     }
    545     /*
    546      * Terminates the listener looper thread.
    547      */
    548     private void terminateListenerLooper() {
    549         if (mListenerThread != null) {
    550             if (mLooper != null) {
    551                 mLooper.quit();
    552                 mLooper = null;
    553             }
    554             try {
    555                 mListenerThread.join();
    556             } catch(InterruptedException e) {
    557             }
    558             mListenerThread = null;
    559         }
    560     }
    561 }
    562