1 /* 2 * Copyright (C) 2014 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.audio.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertTrue; 23 import static org.junit.Assert.fail; 24 25 import android.content.Context; 26 import android.media.AudioManager; 27 import android.media.MediaPlayer; 28 import android.media.audiofx.AudioEffect; 29 import android.media.audiofx.LoudnessEnhancer; 30 import android.media.audiofx.Visualizer; 31 import android.media.audiofx.Visualizer.MeasurementPeakRms; 32 import android.platform.test.annotations.AppModeFull; 33 import android.util.Log; 34 35 import androidx.test.runner.AndroidJUnit4; 36 37 import org.junit.Test; 38 import org.junit.runner.RunWith; 39 40 import java.util.UUID; 41 42 @AppModeFull(reason = "Dynamic config disabled.") 43 @RunWith(AndroidJUnit4.class) 44 public class LoudnessEnhancerTest extends PostProcTestBase { 45 46 private String TAG = "LoudnessEnhancerTest"; 47 private LoudnessEnhancer mLE; 48 49 private static final float EPSILON = 0.0001f; 50 51 //----------------------------------------------------------------- 52 // LOUDNESS ENHANCER TESTS: 53 //---------------------------------- 54 55 //----------------------------------------------------------------- 56 // 0 - constructor 57 //---------------------------------- 58 59 //Test case 0.0: test constructor and release 60 @Test test0_0ConstructorAndRelease()61 public void test0_0ConstructorAndRelease() throws Exception { 62 if (!hasAudioOutput()) { 63 return; 64 } 65 AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 66 assertNotNull("null AudioManager", am); 67 getLoudnessEnhancer(0); 68 releaseLoudnessEnhancer(); 69 70 int session = am.generateAudioSessionId(); 71 assertTrue("cannot generate new session", session != AudioManager.ERROR); 72 getLoudnessEnhancer(session); 73 releaseLoudnessEnhancer(); 74 } 75 76 //----------------------------------------------------------------- 77 // 1 - get/set parameters 78 //---------------------------------- 79 80 //Test case 1.0: test set/get target gain 81 @Test test1_0TargetGain()82 public void test1_0TargetGain() throws Exception { 83 if (!hasAudioOutput()) { 84 return; 85 } 86 getLoudnessEnhancer(0); 87 try { 88 mLE.setTargetGain(0); 89 assertEquals("target gain differs from value set", 90 0.0f, mLE.getTargetGain(), EPSILON); 91 mLE.setTargetGain(800); 92 assertEquals("target gain differs from value set", 93 800.0f, mLE.getTargetGain(), EPSILON); 94 } catch (IllegalArgumentException e) { 95 fail("target gain illegal argument"); 96 } catch (UnsupportedOperationException e) { 97 fail("target gain unsupported operation"); 98 } catch (IllegalStateException e) { 99 fail("target gain operation called in wrong state"); 100 } finally { 101 releaseLoudnessEnhancer(); 102 } 103 } 104 105 //----------------------------------------------------------------- 106 // 2 - Effect enable/disable 107 //---------------------------------- 108 109 //Test case 2.0: test setEnabled() and getEnabled() in valid state 110 @Test test2_0SetEnabledGetEnabled()111 public void test2_0SetEnabledGetEnabled() throws Exception { 112 if (!hasAudioOutput()) { 113 return; 114 } 115 getLoudnessEnhancer(getSessionId()); 116 try { 117 mLE.setEnabled(true); 118 assertTrue("invalid state from getEnabled", mLE.getEnabled()); 119 mLE.setEnabled(false); 120 assertFalse("invalid state to getEnabled", mLE.getEnabled()); 121 // test passed 122 } catch (IllegalStateException e) { 123 fail("setEnabled() in wrong state"); 124 } finally { 125 releaseLoudnessEnhancer(); 126 } 127 } 128 129 //Test case 2.1: test setEnabled() throws exception after release 130 @Test test2_1SetEnabledAfterRelease()131 public void test2_1SetEnabledAfterRelease() throws Exception { 132 if (!hasAudioOutput()) { 133 return; 134 } 135 getLoudnessEnhancer(getSessionId()); 136 mLE.release(); 137 try { 138 mLE.setEnabled(true); 139 fail("setEnabled() processed after release()"); 140 } catch (IllegalStateException e) { 141 // test passed 142 } finally { 143 releaseLoudnessEnhancer(); 144 } 145 } 146 147 //----------------------------------------------------------------- 148 // 3 - check effect using visualizer effect 149 //---------------------------------- 150 151 //Test case 3.0: test loudness gain change in audio 152 @Test test3_0MeasureGainChange()153 public void test3_0MeasureGainChange() throws Exception { 154 if (!hasAudioOutput()) { 155 return; 156 } 157 AudioEffect vc = null; 158 Visualizer visualizer = null; 159 MediaPlayer mp = null; 160 try { 161 // this test will play a 1kHz sine wave with peaks at -40dB, and apply 6 db gain 162 // using loudness enhancement 163 mp = MediaPlayer.create(getContext(), R.raw.sine1khzm40db); 164 final int LOUDNESS_GAIN = 600; 165 final int MAX_MEASUREMENT_ERROR_MB = 200; 166 assertNotNull("null MediaPlayer", mp); 167 168 // creating a volume controller on output mix ensures that ro.audio.silent mutes 169 // audio after the effects and not before 170 vc = new AudioEffect( 171 AudioEffect.EFFECT_TYPE_NULL, 172 UUID.fromString(BUNDLE_VOLUME_EFFECT_UUID), 173 0, 174 mp.getAudioSessionId()); 175 vc.setEnabled(true); 176 177 AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 178 assertNotNull("null AudioManager", am); 179 int originalVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC); 180 am.setStreamVolume(AudioManager.STREAM_MUSIC, 181 am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0); 182 int sessionId = mp.getAudioSessionId(); 183 184 getLoudnessEnhancer(sessionId); 185 visualizer = new Visualizer(sessionId); 186 187 mp.setLooping(true); 188 mp.start(); 189 190 visualizer.setEnabled(true); 191 assertTrue("visualizer not enabled", visualizer.getEnabled()); 192 Thread.sleep(100); 193 int status = visualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS); 194 Thread.sleep(500); 195 assertEquals("setMeasurementMode() for PEAK_RMS doesn't report success", 196 Visualizer.SUCCESS, status); 197 // make sure we're playing long enough so the measurement is valid 198 int currentPosition = mp.getCurrentPosition(); 199 int maxTry = 100; 200 int tryCount = 0; 201 while (currentPosition < 200 && tryCount < maxTry) { 202 Thread.sleep(50); 203 currentPosition = mp.getCurrentPosition(); 204 tryCount++; 205 } 206 assertTrue("MediaPlayer not ready", tryCount < maxTry); 207 208 MeasurementPeakRms measurement = new MeasurementPeakRms(); 209 status = visualizer.getMeasurementPeakRms(measurement); 210 assertEquals("getMeasurementPeakRms() reports failure", Visualizer.SUCCESS, status); 211 212 //run for a new set of 3 seconds, get new measurement 213 mLE.setTargetGain(LOUDNESS_GAIN); 214 assertEquals("target gain differs from value set", (float)LOUDNESS_GAIN, 215 mLE.getTargetGain(), EPSILON); 216 217 mLE.setEnabled(true); 218 219 visualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS); 220 Thread.sleep(500); 221 222 MeasurementPeakRms measurement2 = new MeasurementPeakRms(); 223 status = visualizer.getMeasurementPeakRms(measurement2); 224 assertEquals("getMeasurementPeakRms() reports failure", Visualizer.SUCCESS, status); 225 226 //compare both measurements 227 am.setStreamVolume(AudioManager.STREAM_MUSIC, originalVolume, 0); 228 assertEquals("getMeasurementPeakRms() reports failure", 229 Visualizer.SUCCESS, status); 230 Log.i("LETest", "peak="+measurement.mPeak+" rms="+measurement.mRms); 231 Log.i("LETest", "peak2="+measurement2.mPeak+" rms2="+measurement2.mRms); 232 233 int deltaPeak = Math.abs(measurement2.mPeak - (measurement.mPeak + LOUDNESS_GAIN) ); 234 assertTrue("peak deviation in mB = "+deltaPeak, deltaPeak < MAX_MEASUREMENT_ERROR_MB); 235 236 } catch (IllegalStateException e) { 237 fail("method called in wrong state"); 238 } catch (InterruptedException e) { 239 fail("sleep() interrupted"); 240 } finally { 241 if (mp != null) { 242 mp.stop(); 243 mp.release(); 244 } 245 246 if (vc != null) 247 vc.release(); 248 249 if (visualizer != null) 250 visualizer.release(); 251 252 releaseLoudnessEnhancer(); 253 } 254 } 255 256 //----------------------------------------------------------------- 257 // private methods 258 //---------------------------------- getLoudnessEnhancer(int session)259 private void getLoudnessEnhancer(int session) { 260 releaseLoudnessEnhancer(); 261 try { 262 mLE = new LoudnessEnhancer(session); 263 } catch (IllegalArgumentException e) { 264 Log.e(TAG, "getLoudnessEnhancer() LoudnessEnhancer not found exception: ", e); 265 } catch (UnsupportedOperationException e) { 266 Log.e(TAG, "getLoudnessEnhancer() Effect library not loaded exception: ", e); 267 } 268 assertNotNull("could not create LoudnessEnhancer", mLE); 269 } 270 releaseLoudnessEnhancer()271 private void releaseLoudnessEnhancer() { 272 if (mLE != null) { 273 mLE.release(); 274 mLE = null; 275 } 276 } 277 }