xref: /aosp_15_r20/cts/tests/tests/media/audio/src/android/media/audio/cts/LoudnessEnhancerTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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 }