xref: /aosp_15_r20/cts/tests/tests/media/audio/src/android/media/audio/cts/EnvReverbTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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.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.media.audiofx.AudioEffect;
26 import android.media.audiofx.EnvironmentalReverb;
27 import android.os.Looper;
28 import android.platform.test.annotations.AppModeFull;
29 import android.util.Log;
30 
31 import androidx.test.runner.AndroidJUnit4;
32 
33 import com.android.compatibility.common.util.NonMainlineTest;
34 
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 
38 @NonMainlineTest
39 @AppModeFull(reason = "Fails in instant mode")
40 @RunWith(AndroidJUnit4.class)
41 public class EnvReverbTest extends PostProcTestBase {
42 
43     private String TAG = "EnvReverbTest";
44     private final static int MILLIBEL_TOLERANCE = 100;            // +/-1dB
45     private final static float DELAY_TOLERANCE = 1.05f;           // 5%
46     private final static float RATIO_TOLERANCE = 1.05f;           // 5%
47     private final static int MAX_LOOPER_WAIT_COUNT = 10;
48 
49     private EnvironmentalReverb mReverb = null;
50     private EnvironmentalReverb mReverb2 = null;
51     private ListenerThread mEffectListenerLooper = null;
52 
53 
54     //-----------------------------------------------------------------
55     // ENVIRONMENTAL REVERB TESTS:
56     //----------------------------------
57 
58     //-----------------------------------------------------------------
59     // 0 - constructor
60     //----------------------------------
61 
62     //Test case 0.0: test constructor and release
63     @Test
test0_0ConstructorAndRelease()64     public void test0_0ConstructorAndRelease() throws Exception {
65         if (!isEnvReverbAvailable()) {
66             return;
67         }
68         EnvironmentalReverb envReverb = null;
69          try {
70             envReverb = new EnvironmentalReverb(0, 0);
71             try {
72                 assertTrue("invalid effect ID", (envReverb.getId() != 0));
73             } catch (IllegalStateException e) {
74                 fail("EnvironmentalReverb not initialized");
75             }
76         } catch (IllegalArgumentException e) {
77             fail("EnvironmentalReverb not found");
78         } catch (UnsupportedOperationException e) {
79             fail("Effect library not loaded");
80         } finally {
81             if (envReverb != null) {
82                 envReverb.release();
83             }
84         }
85     }
86 
87 
88     //-----------------------------------------------------------------
89     // 1 - get/set parameters
90     //----------------------------------
91 
92     //Test case 1.0: test room level and room HF level
93     @Test
test1_0Room()94     public void test1_0Room() throws Exception {
95         if (!isEnvReverbAvailable()) {
96             return;
97         }
98         getReverb(0);
99         try {
100             short level = mReverb.getRoomLevel();
101             level = (short)((level == 0) ? -1000 : 0);
102             mReverb.setRoomLevel(level);
103             short level2 = mReverb.getRoomLevel();
104             assertTrue("got incorrect room level",
105                     (level2 > (level - MILLIBEL_TOLERANCE)) &&
106                     (level2 < (level + MILLIBEL_TOLERANCE)));
107 
108             level = mReverb.getRoomHFLevel();
109             level = (short)((level == 0) ? -1000 : 0);
110             mReverb.setRoomHFLevel(level);
111             level2 = mReverb.getRoomHFLevel();
112             assertTrue("got incorrect room HF level",
113                     (level2 > (level - MILLIBEL_TOLERANCE)) &&
114                     (level2 < (level + MILLIBEL_TOLERANCE)));
115 
116         } catch (IllegalArgumentException e) {
117             fail("Bad parameter value");
118         } catch (UnsupportedOperationException e) {
119             fail("get parameter() rejected");
120         } catch (IllegalStateException e) {
121             fail("get parameter() called in wrong state");
122         } finally {
123             releaseReverb();
124         }
125     }
126 
127     //Test case 1.1: test decay time and ratio
128     @Test
test1_1Decay()129     public void test1_1Decay() throws Exception {
130         if (!isEnvReverbAvailable()) {
131             return;
132         }
133         getReverb(0);
134         try {
135             int time = mReverb.getDecayTime();
136             time = (time == 500) ? 1000 : 500;
137             mReverb.setDecayTime(time);
138             int time2 = mReverb.getDecayTime();
139             assertTrue("got incorrect decay time",
140                     ((float)time2 > (float)(time / DELAY_TOLERANCE)) &&
141                     ((float)time2 < (float)(time * DELAY_TOLERANCE)));
142             short ratio = mReverb.getDecayHFRatio();
143             ratio = (short)((ratio == 500) ? 1000 : 500);
144             mReverb.setDecayHFRatio(ratio);
145             short ratio2 = mReverb.getDecayHFRatio();
146             assertTrue("got incorrect decay HF ratio",
147                     ((float)ratio2 > (float)(ratio / RATIO_TOLERANCE)) &&
148                     ((float)ratio2 < (float)(ratio * RATIO_TOLERANCE)));
149 
150         } catch (IllegalArgumentException e) {
151             fail("Bad parameter value");
152         } catch (UnsupportedOperationException e) {
153             fail("get parameter() rejected");
154         } catch (IllegalStateException e) {
155             fail("get parameter() called in wrong state");
156         } finally {
157             releaseReverb();
158         }
159     }
160 
161 
162     //Test case 1.2: test reverb level and delay
163     @Test
test1_2Reverb()164     public void test1_2Reverb() throws Exception {
165         if (!isEnvReverbAvailable()) {
166             return;
167         }
168         getReverb(0);
169         try {
170             short level = mReverb.getReverbLevel();
171             level = (short)((level == 0) ? -1000 : 0);
172             mReverb.setReverbLevel(level);
173             short level2 = mReverb.getReverbLevel();
174             assertTrue("got incorrect reverb level",
175                     (level2 > (level - MILLIBEL_TOLERANCE)) &&
176                     (level2 < (level + MILLIBEL_TOLERANCE)));
177 
178 // FIXME:uncomment actual test when early reflections are implemented in the reverb
179 //            int time = mReverb.getReverbDelay();
180 //             mReverb.setReverbDelay(time);
181 //            int time2 = mReverb.getReverbDelay();
182 //            assertTrue("got incorrect reverb delay",
183 //                    ((float)time2 > (float)(time / DELAY_TOLERANCE)) &&
184 //                    ((float)time2 < (float)(time * DELAY_TOLERANCE)));
185             mReverb.setReverbDelay(0);
186             int time2 = mReverb.getReverbDelay();
187             assertEquals("got incorrect reverb delay", mReverb.getReverbDelay(), 0);
188         } catch (IllegalArgumentException e) {
189             fail("Bad parameter value");
190         } catch (UnsupportedOperationException e) {
191             fail("get parameter() rejected");
192         } catch (IllegalStateException e) {
193             fail("get parameter() called in wrong state");
194         } finally {
195             releaseReverb();
196         }
197     }
198 
199     //Test case 1.3: test early reflections level and delay
200     @Test
test1_3Reflections()201     public void test1_3Reflections() throws Exception {
202         if (!isEnvReverbAvailable()) {
203             return;
204         }
205         getReverb(0);
206         try {
207 // FIXME:uncomment actual test when early reflections are implemented in the reverb
208 //            short level = mReverb.getReflectionsLevel();
209 //            level = (short)((level == 0) ? -1000 : 0);
210 //            mReverb.setReflectionsLevel(level);
211 //            short level2 = mReverb.getReflectionsLevel();
212 //            assertTrue("got incorrect reflections level",
213 //                    (level2 > (level - MILLIBEL_TOLERANCE)) &&
214 //                    (level2 < (level + MILLIBEL_TOLERANCE)));
215 //
216 //            int time = mReverb.getReflectionsDelay();
217 //            time = (time == 20) ? 0 : 20;
218 //            mReverb.setReflectionsDelay(time);
219 //            int time2 = mReverb.getReflectionsDelay();
220 //            assertTrue("got incorrect reflections delay",
221 //                    ((float)time2 > (float)(time / DELAY_TOLERANCE)) &&
222 //                    ((float)time2 < (float)(time * DELAY_TOLERANCE)));
223             mReverb.setReflectionsLevel((short) 0);
224             assertEquals("got incorrect reverb delay",
225                     mReverb.getReflectionsLevel(), (short) 0);
226             mReverb.setReflectionsDelay(0);
227             assertEquals("got incorrect reverb delay",
228                     mReverb.getReflectionsDelay(), 0);
229 
230         } catch (IllegalArgumentException e) {
231             fail("Bad parameter value");
232         } catch (UnsupportedOperationException e) {
233             fail("get parameter() rejected");
234         } catch (IllegalStateException e) {
235             fail("get parameter() called in wrong state");
236         } finally {
237             releaseReverb();
238         }
239     }
240 
241     //Test case 1.4: test diffusion and density
242     @Test
test1_4DiffusionAndDensity()243     public void test1_4DiffusionAndDensity() throws Exception {
244         if (!isEnvReverbAvailable()) {
245             return;
246         }
247         getReverb(0);
248         try {
249             short ratio = mReverb.getDiffusion();
250             ratio = (short)((ratio == 500) ? 1000 : 500);
251             mReverb.setDiffusion(ratio);
252             short ratio2 = mReverb.getDiffusion();
253             assertTrue("got incorrect diffusion",
254                     ((float)ratio2 > (float)(ratio / RATIO_TOLERANCE)) &&
255                     ((float)ratio2 < (float)(ratio * RATIO_TOLERANCE)));
256 
257             ratio = mReverb.getDensity();
258             ratio = (short)((ratio == 500) ? 1000 : 500);
259             mReverb.setDensity(ratio);
260             ratio2 = mReverb.getDensity();
261             assertTrue("got incorrect density",
262                     ((float)ratio2 > (float)(ratio / RATIO_TOLERANCE)) &&
263                     ((float)ratio2 < (float)(ratio * RATIO_TOLERANCE)));
264 
265         } catch (IllegalArgumentException e) {
266             fail("Bad parameter value");
267         } catch (UnsupportedOperationException e) {
268             fail("get parameter() rejected");
269         } catch (IllegalStateException e) {
270             fail("get parameter() called in wrong state");
271         } finally {
272             releaseReverb();
273         }
274     }
275 
276     //Test case 1.5: test properties
277     @Test
test1_5Properties()278     public void test1_5Properties() throws Exception {
279         if (!isEnvReverbAvailable()) {
280             return;
281         }
282         getReverb(0);
283         try {
284             EnvironmentalReverb.Settings settings = mReverb.getProperties();
285             String str = settings.toString();
286             settings = new EnvironmentalReverb.Settings(str);
287             short level = (short)((settings.roomLevel == 0) ? -1000 : 0);
288             settings.roomLevel = level;
289             mReverb.setProperties(settings);
290             settings = mReverb.getProperties();
291             assertTrue("setProperties failed",
292                     (settings.roomLevel >= (level - MILLIBEL_TOLERANCE)) &&
293                     (settings.roomLevel <= (level + MILLIBEL_TOLERANCE)));
294         } catch (IllegalArgumentException e) {
295             fail("Bad parameter value");
296         } catch (UnsupportedOperationException e) {
297             fail("get parameter() rejected");
298         } catch (IllegalStateException e) {
299             fail("get parameter() called in wrong state");
300         } finally {
301             releaseReverb();
302         }
303     }
304 
305     //-----------------------------------------------------------------
306     // 2 - Effect enable/disable
307     //----------------------------------
308 
309     //Test case 2.0: test setEnabled() and getEnabled() in valid state
310     @Test
test2_0SetEnabledGetEnabled()311     public void test2_0SetEnabledGetEnabled() throws Exception {
312         if (!isEnvReverbAvailable()) {
313             return;
314         }
315         getReverb(0);
316         try {
317             mReverb.setEnabled(true);
318             assertTrue("invalid state from getEnabled", mReverb.getEnabled());
319             mReverb.setEnabled(false);
320             assertFalse("invalid state to getEnabled", mReverb.getEnabled());
321         } catch (IllegalStateException e) {
322             fail("setEnabled() in wrong state");
323         } finally {
324             releaseReverb();
325         }
326     }
327 
328     //Test case 2.1: test setEnabled() throws exception after release
329     @Test
test2_1SetEnabledAfterRelease()330     public void test2_1SetEnabledAfterRelease() throws Exception {
331         if (!isEnvReverbAvailable()) {
332             return;
333         }
334         getReverb(0);
335         mReverb.release();
336         try {
337             mReverb.setEnabled(true);
338             fail("setEnabled() processed after release()");
339         } catch (IllegalStateException e) {
340             // test passed
341         } finally {
342             releaseReverb();
343         }
344     }
345 
346     //-----------------------------------------------------------------
347     // 3 priority and listeners
348     //----------------------------------
349 
350     //Test case 3.0: test control status listener
351     @Test
test3_0ControlStatusListener()352     public void test3_0ControlStatusListener() throws Exception {
353         if (!isEnvReverbAvailable()) {
354             return;
355         }
356         synchronized(mLock) {
357             mHasControl = true;
358             mInitialized = false;
359             createListenerLooper(true, false, false);
360             waitForLooperInitialization_l();
361 
362             getReverb(0);
363             int looperWaitCount = MAX_LOOPER_WAIT_COUNT;
364             while (mHasControl && (looperWaitCount-- > 0)) {
365                 try {
366                     mLock.wait();
367                 } catch(Exception e) {
368                 }
369             }
370             terminateListenerLooper();
371             releaseReverb();
372         }
373         assertFalse("effect control not lost by effect1", mHasControl);
374     }
375 
376     //Test case 3.1: test enable status listener
377     @Test
test3_1EnableStatusListener()378     public void test3_1EnableStatusListener() throws Exception {
379         if (!isEnvReverbAvailable()) {
380             return;
381         }
382          synchronized(mLock) {
383             mInitialized = false;
384             createListenerLooper(false, true, false);
385             waitForLooperInitialization_l();
386 
387             mReverb2.setEnabled(true);
388             mIsEnabled = true;
389             getReverb(0);
390             mReverb.setEnabled(false);
391             int looperWaitCount = MAX_LOOPER_WAIT_COUNT;
392             while (mIsEnabled && (looperWaitCount-- > 0)) {
393                 try {
394                     mLock.wait();
395                 } catch(Exception e) {
396                 }
397             }
398             terminateListenerLooper();
399             releaseReverb();
400         }
401         assertFalse("enable status not updated", mIsEnabled);
402     }
403 
404     //Test case 3.2: test parameter changed listener
405     @Test
test3_2ParameterChangedListener()406     public void test3_2ParameterChangedListener() throws Exception {
407         if (!isEnvReverbAvailable()) {
408             return;
409         }
410         synchronized(mLock) {
411             mInitialized = false;
412             createListenerLooper(false, false, true);
413             waitForLooperInitialization_l();
414 
415             getReverb(0);
416             mChangedParameter = -1;
417             mReverb.setRoomLevel((short)0);
418 
419             int looperWaitCount = MAX_LOOPER_WAIT_COUNT;
420             while ((mChangedParameter == -1) && (looperWaitCount-- > 0)) {
421                 try {
422                     mLock.wait();
423                 } catch(Exception e) {
424                 }
425             }
426             terminateListenerLooper();
427             releaseReverb();
428         }
429         assertEquals("parameter change not received",
430                 EnvironmentalReverb.PARAM_ROOM_LEVEL, mChangedParameter);
431     }
432 
433     //-----------------------------------------------------------------
434     // private methods
435     //----------------------------------
436 
getReverb(int session)437     private void getReverb(int session) {
438          if (mReverb == null || session != mSession) {
439              if (session != mSession && mReverb != null) {
440                  mReverb.release();
441                  mReverb = null;
442              }
443              try {
444                 mReverb = new EnvironmentalReverb(0, session);
445                 mSession = session;
446             } catch (IllegalArgumentException e) {
447                 Log.e(TAG, "getReverb() EnvironmentalReverb not found exception: "+e);
448             } catch (UnsupportedOperationException e) {
449                 Log.e(TAG, "getReverb() Effect library not loaded exception: "+e);
450             }
451          }
452          assertNotNull("could not create mReverb", mReverb);
453     }
454 
releaseReverb()455     private void releaseReverb() {
456         if (mReverb != null) {
457             mReverb.release();
458             mReverb = null;
459         }
460     }
461 
waitForLooperInitialization_l()462     private void waitForLooperInitialization_l() {
463         int looperWaitCount = MAX_LOOPER_WAIT_COUNT;
464         while (!mInitialized && (looperWaitCount-- > 0)) {
465             try {
466                 mLock.wait();
467             } catch(Exception e) {
468             }
469         }
470         assertTrue(mInitialized);
471     }
472 
473     // Initializes the reverb listener looper
474     class ListenerThread extends Thread {
475         boolean mControl;
476         boolean mEnable;
477         boolean mParameter;
478 
ListenerThread(boolean control, boolean enable, boolean parameter)479         public ListenerThread(boolean control, boolean enable, boolean parameter) {
480             super();
481             mControl = control;
482             mEnable = enable;
483             mParameter = parameter;
484         }
485 
cleanUp()486         public void cleanUp() {
487             if (mReverb2 != null) {
488                 mReverb2.setControlStatusListener(null);
489                 mReverb2.setEnableStatusListener(null);
490                 mReverb2.setParameterListener(
491                             (EnvironmentalReverb.OnParameterChangeListener)null);
492             }
493         }
494     }
495 
createListenerLooper(boolean control, boolean enable, boolean parameter)496     private void createListenerLooper(boolean control, boolean enable, boolean parameter) {
497         mEffectListenerLooper = new ListenerThread(control, enable, parameter) {
498             @Override
499             public void run() {
500                 // Set up a looper
501                 Looper.prepare();
502 
503                 // Save the looper so that we can terminate this thread
504                 // after we are done with it.
505                 mLooper = Looper.myLooper();
506 
507                 mReverb2 = new EnvironmentalReverb(0, 0);
508                 assertNotNull("could not create reverb2", mReverb2);
509 
510                 synchronized(mLock) {
511                     if (mControl) {
512                         mReverb2.setControlStatusListener(
513                                 new AudioEffect.OnControlStatusChangeListener() {
514                             public void onControlStatusChange(
515                                     AudioEffect effect, boolean controlGranted) {
516                                 synchronized(mLock) {
517                                     if (effect == mReverb2) {
518                                         mHasControl = controlGranted;
519                                         mLock.notify();
520                                     }
521                                 }
522                             }
523                         });
524                     }
525                     if (mEnable) {
526                         mReverb2.setEnableStatusListener(
527                                 new AudioEffect.OnEnableStatusChangeListener() {
528                             public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
529                                 synchronized(mLock) {
530                                     if (effect == mReverb2) {
531                                         mIsEnabled = enabled;
532                                         mLock.notify();
533                                     }
534                                 }
535                             }
536                         });
537                     }
538                     if (mParameter) {
539                         mReverb2.setParameterListener(new EnvironmentalReverb.OnParameterChangeListener() {
540                             public void onParameterChange(EnvironmentalReverb effect,
541                                     int status, int param, int value)
542                             {
543                                 synchronized(mLock) {
544                                     if (effect == mReverb2) {
545                                         mChangedParameter = param;
546                                         mLock.notify();
547                                     }
548                                 }
549                             }
550                         });
551                     }
552 
553                     mInitialized = true;
554                     mLock.notify();
555                 }
556                 Looper.loop();  // Blocks forever until Looper.quit() is called.
557             }
558         };
559         mEffectListenerLooper.start();
560     }
561 
562     // Terminates the listener looper thread.
terminateListenerLooper()563     private void terminateListenerLooper() {
564         if (mEffectListenerLooper != null) {
565             mEffectListenerLooper.cleanUp();
566             if (mLooper != null) {
567                 mLooper.quit();
568                 mLooper = null;
569             }
570             try {
571                 mEffectListenerLooper.join();
572             } catch(InterruptedException e) {
573             }
574             mEffectListenerLooper = null;
575         }
576         if (mReverb2 != null) {
577             mReverb2.release();
578             mReverb2 = null;
579         }
580     }
581 }
582