diff options
7 files changed, 970 insertions, 141 deletions
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java index c7f461ecf558..46135ff19015 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java @@ -27,7 +27,9 @@ import com.android.mediaframeworktest.functional.MediaPlayerInvokeTest; import com.android.mediaframeworktest.functional.MediaAudioManagerTest; import com.android.mediaframeworktest.functional.MediaAudioEffectTest; import com.android.mediaframeworktest.functional.MediaBassBoostTest; +import com.android.mediaframeworktest.functional.MediaEnvReverbTest; import com.android.mediaframeworktest.functional.MediaEqualizerTest; +import com.android.mediaframeworktest.functional.MediaPresetReverbTest; import com.android.mediaframeworktest.functional.MediaVirtualizerTest; import com.android.mediaframeworktest.functional.MediaVisualizerTest; import junit.framework.TestSuite; @@ -62,7 +64,9 @@ public class MediaFrameworkTestRunner extends InstrumentationTestRunner { suite.addTestSuite(MediaAudioManagerTest.class); suite.addTestSuite(MediaAudioEffectTest.class); suite.addTestSuite(MediaBassBoostTest.class); + suite.addTestSuite(MediaEnvReverbTest.class); suite.addTestSuite(MediaEqualizerTest.class); + suite.addTestSuite(MediaPresetReverbTest.class); suite.addTestSuite(MediaVirtualizerTest.class); suite.addTestSuite(MediaVisualizerTest.class); return suite; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java new file mode 100644 index 000000000000..d339e06a721c --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/EnergyProbe.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest.functional; + +import android.media.Visualizer; +import android.util.Log; + +/** + * The EnergyProbe class provides audio signal energy measurements based on the FFT returned + * by the Visualizer class. The measure is qualitative and not quantitative in that the returned + * value has no unit and is just proportional to the amount of energy present around the + * specified frequency. + */ + +public class EnergyProbe { + private String TAG = "EnergyProbe"; + + private static int CAPTURE_SIZE = 1024; + private static int MEASURE_COUNT = 5; + private static int AVERAGE_COUNT = 3; + + private Visualizer mVisualizer = null; + private int mMaxFrequency = 0; + private int mCapturePeriodMs; + private byte[] mFft = new byte[CAPTURE_SIZE]; + + public EnergyProbe(int session) { + try { + mVisualizer = new Visualizer(session); + if (mVisualizer != null) { + mVisualizer.setCaptureSize(CAPTURE_SIZE); + mMaxFrequency = mVisualizer.getSamplingRate() / 2000; + mCapturePeriodMs = 1000000 / mVisualizer.getMaxCaptureRate(); + } + } catch (UnsupportedOperationException e) { + Log.e(TAG, "Error creating visualizer"); + } catch (IllegalStateException e) { + Log.e(TAG, "Error configuring visualizer"); + } + } + + public int capture(int freq) throws InterruptedException { + int energy = 0; + int count = 0; + + if (freq > mMaxFrequency) { + return 0; + } + + if (mVisualizer != null) { + try { + mVisualizer.setEnabled(true); + for (int i = 0; i < MEASURE_COUNT; i++) { + if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) { + if (freq == mMaxFrequency) { + energy += (int)mFft[0] * (int)mFft[0]; + } else { + int bin = 2 * (freq * CAPTURE_SIZE / mMaxFrequency / 2); + if (bin < 2) bin = 2; + int tmp = 0; + int j; + for (j = 0; + (j < AVERAGE_COUNT) && ((bin + 2 * j) < CAPTURE_SIZE); + j++) { + tmp += (int)mFft[bin + 2 * j] * (int)mFft[bin + 2 * j] + + (int)mFft[bin + 2 * j + 1] * (int)mFft[bin + 2 * j + 1]; + } + // j is always != 0 + energy += tmp/j; + } + count++; + } + Thread.sleep(mCapturePeriodMs); + } + mVisualizer.setEnabled(false); + } catch (IllegalStateException e) { + Log.e(TAG, "Error capturing audio"); + } + } + if (count == 0) { + return 0; + } + return energy/count; + } + + public void release() { + if (mVisualizer != null) { + mVisualizer.release(); + mVisualizer = null; + } + } +} diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java index 8a68c5eef68a..aca729e3d953 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java @@ -43,7 +43,7 @@ import java.util.UUID; */ public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { private String TAG = "MediaBassBoostTest"; - private final static int MIN_ENERGY_RATIO_2 = 4; + private final static int MIN_ENERGY_RATIO_2 = 3; private final static short TEST_STRENGTH = 500; private BassBoost mBassBoost = null; @@ -259,52 +259,6 @@ public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFr // private methods //---------------------------------- - private class EnergyProbe { - Visualizer mVisualizer = null; - private byte[] mFft = new byte[1024]; - - public EnergyProbe(int session) { - mVisualizer = new Visualizer(session); - mVisualizer.setCaptureSize(1024); - } - - public int capture(int freq) throws InterruptedException { - int energy = 0; - int count = 0; - if (mVisualizer != null) { - mVisualizer.setEnabled(true); - for (int i = 0; i < 10; i++) { - if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) { - // TODO: check speex FFT as it seems to return only the number of points - // correspondong to valid part of the spectrum (< Fs). - // e.g., if the number of points is 1024, it covers the frequency range - // 0 to 22050 instead of 0 to 44100 as expected from an FFT. - int bin = freq / (22050 / 1024); - int tmp = 0; - for (int j = bin-2; j < bin+3; j++) { - tmp += (int)mFft[j] * (int)mFft[j]; - } - energy += tmp/5; - count++; - } - Thread.sleep(50); - } - mVisualizer.setEnabled(false); - } - if (count == 0) { - return 0; - } - return energy/count; - } - - public void release() { - if (mVisualizer != null) { - mVisualizer.release(); - mVisualizer = null; - } - } - } - private void getBassBoost(int session) { if (mBassBoost == null || session != mSession) { if (session != mSession && mBassBoost != null) { diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java new file mode 100644 index 000000000000..db0db701a065 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEnvReverbTest.java @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest.functional; + +import com.android.mediaframeworktest.MediaFrameworkTest; +import com.android.mediaframeworktest.MediaNames; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.media.AudioEffect; +import android.media.AudioManager; +import android.media.EnvironmentalReverb; +import android.media.Visualizer; +import android.media.MediaPlayer; + +import android.os.Looper; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.Suppress; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.util.UUID; + +/** + * Junit / Instrumentation test case for the media AudioTrack api + + */ +public class MediaEnvReverbTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { + private String TAG = "MediaEnvReverbTest"; + // allow +/- 100 millibel difference between set and get gains + private final static int MILLIBEL_TOLERANCE = 100; + // allow +/- 5% tolerance between set and get delays + private final static float DELAY_TOLERANCE = 1.05f; + // allow +/- 5% tolerance between set and get ratios + private final static float RATIO_TOLERANCE = 1.05f; + + private EnvironmentalReverb mReverb = null; + private int mSession = -1; + + public MediaEnvReverbTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + releaseReverb(); + } + + private static void assumeTrue(String message, boolean cond) { + assertTrue("(assume)"+message, cond); + } + + private void log(String testName, String message) { + Log.v(TAG, "["+testName+"] "+message); + } + + private void loge(String testName, String message) { + Log.e(TAG, "["+testName+"] "+message); + } + + //----------------------------------------------------------------- + // ENVIRONMENTAL REVEB TESTS: + //---------------------------------- + + + //----------------------------------------------------------------- + // 0 - constructor + //---------------------------------- + + //Test case 0.0: test constructor and release + @LargeTest + public void test0_0ConstructorAndRelease() throws Exception { + boolean result = false; + String msg = "test1_0ConstructorAndRelease()"; + EnvironmentalReverb reverb = null; + try { + reverb = new EnvironmentalReverb(0, 0); + assertNotNull(msg + ": could not create EnvironmentalReverb", reverb); + try { + assertTrue(msg +": invalid effect ID", (reverb.getId() != 0)); + } catch (IllegalStateException e) { + msg = msg.concat(": EnvironmentalReverb not initialized"); + } + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": EnvironmentalReverb not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + } finally { + if (reverb != null) { + reverb.release(); + } + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 1 - get/set parameters + //---------------------------------- + + //Test case 1.0: test room level and room HF level + @LargeTest + public void test1_0Room() throws Exception { + boolean result = false; + String msg = "test1_0Room()"; + getReverb(0); + try { + mReverb.setRoomLevel((short)0); + short level = mReverb.getRoomLevel(); + assertTrue(msg +": got incorrect room level", + (level > (0 - MILLIBEL_TOLERANCE)) && + (level < (0 + MILLIBEL_TOLERANCE))); + + mReverb.setRoomHFLevel((short)-6); + level = mReverb.getRoomHFLevel(); + assertTrue(msg +": got incorrect room HF level", + (level > (-6 - MILLIBEL_TOLERANCE)) && + (level < (-6 + MILLIBEL_TOLERANCE))); + + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseReverb(); + } + assertTrue(msg, result); + } + + //Test case 1.1: test decay time and ratio + @LargeTest + public void test1_1Decay() throws Exception { + boolean result = false; + String msg = "test1_1Decay()"; + getReverb(0); + try { + mReverb.setDecayTime(500); + int time = mReverb.getDecayTime(); + assertTrue(msg +": got incorrect decay time", + ((float)time > (float)(500 / DELAY_TOLERANCE)) && + ((float)time < (float)(500 * DELAY_TOLERANCE))); + + mReverb.setDecayHFRatio((short)1000); + short ratio = mReverb.getDecayHFRatio(); + assertTrue(msg +": got incorrect decay HF ratio", + ((float)ratio > (float)(1000 / RATIO_TOLERANCE)) && + ((float)ratio < (float)(1000 * RATIO_TOLERANCE))); + + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseReverb(); + } + assertTrue(msg, result); + } + + //Test case 1.2: test reflections + @LargeTest + public void test1_2Reflections() throws Exception { + // TODO: uncomment when early reflections are implemented +// boolean result = false; +// String msg = "test1_2Reflections()"; +// getReverb(0); +// try { +// mReverb.setReflectionsLevel((short)0); +// short level = mReverb.getReflectionsLevel(); +// assertTrue(msg +": got incorrect reflections level", +// (level > (0 - MILLIBEL_TOLERANCE)) && +// (level < (0 + MILLIBEL_TOLERANCE))); +// +// mReverb.setReflectionsDelay(30); +// int delay = mReverb.getReflectionsDelay(); +// assertTrue(msg +": got incorrect reflections delay", +// ((float)delay > (float)(30 / DELAY_TOLERANCE)) && +// ((float)delay < (float)(30 * DELAY_TOLERANCE))); +// +// result = true; +// } catch (IllegalArgumentException e) { +// msg = msg.concat(": Bad parameter value"); +// loge(msg, "Bad parameter value"); +// } catch (UnsupportedOperationException e) { +// msg = msg.concat(": get parameter() rejected"); +// loge(msg, "get parameter() rejected"); +// } catch (IllegalStateException e) { +// msg = msg.concat("get parameter() called in wrong state"); +// loge(msg, "get parameter() called in wrong state"); +// } finally { +// releaseReverb(); +// } +// assertTrue(msg, result); + } + + //Test case 1.3: test reverb + @LargeTest + public void test1_3Reverb() throws Exception { + boolean result = false; + String msg = "test1_3Reverb()"; + getReverb(0); + try { + mReverb.setReverbLevel((short)0); + short level = mReverb.getReverbLevel(); + assertTrue(msg +": got incorrect reverb level", + (level > (0 - MILLIBEL_TOLERANCE)) && + (level < (0 + MILLIBEL_TOLERANCE))); + + // TODO: change delay when early reflections are implemented + mReverb.setReverbDelay(0); + int delay = mReverb.getReverbDelay(); + assertTrue(msg +": got incorrect reverb delay", delay < 5); + + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseReverb(); + } + assertTrue(msg, result); + } + + //Test case 1.4: test diffusion and density + @LargeTest + public void test1_4DiffusionAndDensity() throws Exception { + boolean result = false; + String msg = "test1_4DiffusionAndDensity()"; + getReverb(0); + try { + mReverb.setDiffusion((short)500); + short diffusion = mReverb.getDiffusion(); + assertTrue(msg +": got incorrect diffusion", + ((float)diffusion > (float)(500 / RATIO_TOLERANCE)) && + ((float)diffusion < (float)(500 * RATIO_TOLERANCE))); + + mReverb.setDensity((short)500); + short density = mReverb.getDensity(); + assertTrue(msg +": got incorrect density", + ((float)density > (float)(500 / RATIO_TOLERANCE)) && + ((float)density < (float)(500 * RATIO_TOLERANCE))); + + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseReverb(); + } + assertTrue(msg, result); + } + + //Test case 1.5: test properties + @LargeTest + public void test1_5Properties() throws Exception { + boolean result = false; + String msg = "test1_5Properties()"; + getReverb(0); + try { + EnvironmentalReverb.Settings settings = mReverb.getProperties(); + short newRoomLevel = 0; + if (settings.roomLevel == 0) { + newRoomLevel = -1000; + } + String str = settings.toString(); + settings = new EnvironmentalReverb.Settings(str); + settings.roomLevel = newRoomLevel; + mReverb.setProperties(settings); + settings = mReverb.getProperties(); + assertTrue(msg +": setProperties failed", + (settings.roomLevel > (newRoomLevel - MILLIBEL_TOLERANCE)) && + (settings.roomLevel < (newRoomLevel + MILLIBEL_TOLERANCE))); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseReverb(); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 2 - Effect action + //---------------------------------- + + //Test case 2.0: test actual auxiliary reverb influence on sound + @LargeTest + public void test2_0AuxiliarySoundModification() throws Exception { + boolean result = false; + String msg = "test2_0AuxiliarySoundModification()"; + EnergyProbe probe = null; + AudioEffect vc = null; + MediaPlayer mp = null; + AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); + int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + am.setStreamVolume(AudioManager.STREAM_MUSIC, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), + 0); + getReverb(0); + try { + probe = new EnergyProbe(0); + // creating a volume controller on output mix ensures that ro.audio.silent mutes + // audio after the effects and not before + vc = new AudioEffect( + AudioEffect.EFFECT_TYPE_NULL, + UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"), + 0, + 0); + vc.setEnabled(true); + + mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SINE_200_1000); + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + mp.attachAuxEffect(mReverb.getId()); + mp.setAuxEffectSendLevel(1.0f); + mReverb.setRoomLevel((short)0); + mReverb.setReverbLevel((short)0); + mReverb.setDecayTime(2000); + mReverb.setEnabled(true); + mp.prepare(); + mp.start(); + Thread.sleep(1000); + mp.stop(); + Thread.sleep(300); + // measure energy around 1kHz after media player was stopped for 300 ms + int energy1000 = probe.capture(1000); + assertTrue(msg + ": reverb has no effect", energy1000 > 0); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } catch (InterruptedException e) { + loge(msg, "sleep() interrupted"); + } + finally { + releaseReverb(); + if (mp != null) { + mp.release(); + } + if (vc != null) { + vc.release(); + } + if (probe != null) { + probe.release(); + } + am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } + assertTrue(msg, result); + } + + //Test case 2.1: test actual insert reverb influence on sound + @LargeTest + public void test2_1InsertSoundModification() throws Exception { + boolean result = false; + String msg = "test2_1InsertSoundModification()"; + EnergyProbe probe = null; + AudioEffect vc = null; + MediaPlayer mp = null; + AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); + int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + am.setStreamVolume(AudioManager.STREAM_MUSIC, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), + 0); + try { + probe = new EnergyProbe(0); + // creating a volume controller on output mix ensures that ro.audio.silent mutes + // audio after the effects and not before + vc = new AudioEffect( + AudioEffect.EFFECT_TYPE_NULL, + UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"), + 0, + 0); + vc.setEnabled(true); + + mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SINE_200_1000); + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + getReverb(mp.getAudioSessionId()); + mReverb.setRoomLevel((short)0); + mReverb.setReverbLevel((short)0); + mReverb.setDecayTime(2000); + mReverb.setEnabled(true); + mp.prepare(); + mp.start(); + Thread.sleep(1000); + mp.stop(); + Thread.sleep(300); + // measure energy around 1kHz after media player was stopped for 300 ms + int energy1000 = probe.capture(1000); + assertTrue(msg + ": reverb has no effect", energy1000 > 0); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } catch (InterruptedException e) { + loge(msg, "sleep() interrupted"); + } + finally { + releaseReverb(); + if (mp != null) { + mp.release(); + } + if (vc != null) { + vc.release(); + } + if (probe != null) { + probe.release(); + } + am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // private methods + //---------------------------------- + + private void getReverb(int session) { + if (mReverb == null || session != mSession) { + if (session != mSession && mReverb != null) { + mReverb.release(); + mReverb = null; + } + try { + mReverb = new EnvironmentalReverb(0, session); + mSession = session; + } catch (IllegalArgumentException e) { + Log.e(TAG, "getReverb() EnvironmentalReverb not found exception: "+e); + } catch (UnsupportedOperationException e) { + Log.e(TAG, "getReverb() Effect library not loaded exception: "+e); + } + } + assertNotNull("could not create mReverb", mReverb); + } + + private void releaseReverb() { + if (mReverb != null) { + mReverb.release(); + mReverb = null; + } + } + +} + diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java index e46887b3e4d2..7b3945d71258 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java @@ -322,52 +322,6 @@ public class MediaEqualizerTest extends ActivityInstrumentationTestCase2<MediaFr // private methods //---------------------------------- - private class EnergyProbe { - Visualizer mVisualizer = null; - private byte[] mFft = new byte[1024]; - - public EnergyProbe(int session) { - mVisualizer = new Visualizer(session); - mVisualizer.setCaptureSize(1024); - } - - public int capture(int freq) throws InterruptedException { - int energy = 0; - int count = 0; - if (mVisualizer != null) { - mVisualizer.setEnabled(true); - for (int i = 0; i < 10; i++) { - if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) { - // TODO: check speex FFT as it seems to return only the number of points - // correspondong to valid part of the spectrum (< Fs). - // e.g., if the number of points is 1024, it covers the frequency range - // 0 to 22050 instead of 0 to 44100 as expected from an FFT. - int bin = freq / (22050 / 1024); - int tmp = 0; - for (int j = bin-2; j < bin+3; j++) { - tmp += (int)mFft[j] * (int)mFft[j]; - } - energy += tmp/5; - count++; - } - Thread.sleep(50); - } - mVisualizer.setEnabled(false); - } - if (count == 0) { - return 0; - } - return energy/count; - } - - public void release() { - if (mVisualizer != null) { - mVisualizer.release(); - mVisualizer = null; - } - } - } - private void getEqualizer(int session) { if (mEqualizer == null || session != mSession) { if (session != mSession && mEqualizer != null) { diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java new file mode 100644 index 000000000000..c14319ab483f --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPresetReverbTest.java @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mediaframeworktest.functional; + +import com.android.mediaframeworktest.MediaFrameworkTest; +import com.android.mediaframeworktest.MediaNames; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.media.AudioEffect; +import android.media.AudioManager; +import android.media.PresetReverb; +import android.media.Visualizer; +import android.media.MediaPlayer; + +import android.os.Looper; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.Suppress; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.util.UUID; + +/** + * Junit / Instrumentation test case for the media AudioTrack api + + */ +public class MediaPresetReverbTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { + private String TAG = "MediaPresetReverbTest"; + + private PresetReverb mReverb = null; + private int mSession = -1; + + public MediaPresetReverbTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + releaseReverb(); + } + + private static void assumeTrue(String message, boolean cond) { + assertTrue("(assume)"+message, cond); + } + + private void log(String testName, String message) { + Log.v(TAG, "["+testName+"] "+message); + } + + private void loge(String testName, String message) { + Log.e(TAG, "["+testName+"] "+message); + } + + //----------------------------------------------------------------- + // PRESET REVEB TESTS: + //---------------------------------- + + + //----------------------------------------------------------------- + // 0 - constructor + //---------------------------------- + + //Test case 0.0: test constructor and release + @LargeTest + public void test0_0ConstructorAndRelease() throws Exception { + boolean result = false; + String msg = "test1_0ConstructorAndRelease()"; + PresetReverb reverb = null; + try { + reverb = new PresetReverb(0, 0); + assertNotNull(msg + ": could not create PresetReverb", reverb); + try { + assertTrue(msg +": invalid effect ID", (reverb.getId() != 0)); + } catch (IllegalStateException e) { + msg = msg.concat(": PresetReverb not initialized"); + } + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": PresetReverb not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + } finally { + if (reverb != null) { + reverb.release(); + } + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 1 - get/set parameters + //---------------------------------- + + //Test case 1.0: test preset + @LargeTest + public void test1_0Preset() throws Exception { + boolean result = false; + String msg = "test1_0Preset()"; + getReverb(0); + try { + mReverb.setPreset((short)PresetReverb.PRESET_LARGEROOM); + short preset = mReverb.getPreset(); + assertEquals(msg +": got incorrect preset", + (short)PresetReverb.PRESET_LARGEROOM, + preset); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseReverb(); + } + assertTrue(msg, result); + } + + //Test case 1.1: test properties + @LargeTest + public void test1_1Properties() throws Exception { + boolean result = false; + String msg = "test1_1Properties()"; + getReverb(0); + try { + PresetReverb.Settings settings = mReverb.getProperties(); + short newPreset = (short)PresetReverb.PRESET_LARGEROOM; + if (settings.preset == (short)PresetReverb.PRESET_LARGEROOM) { + newPreset = (short)PresetReverb.PRESET_SMALLROOM; + } + String str = settings.toString(); + settings = new PresetReverb.Settings(str); + settings.preset = newPreset; + mReverb.setProperties(settings); + settings = mReverb.getProperties(); + assertEquals(msg +": setProperties failed", newPreset, settings.preset); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseReverb(); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 2 - Effect action + //---------------------------------- + + //Test case 2.0: test actual auxiliary reverb influence on sound + @LargeTest + public void test2_0AuxiliarySoundModification() throws Exception { + boolean result = false; + String msg = "test2_0AuxiliarySoundModification()"; + EnergyProbe probe = null; + AudioEffect vc = null; + MediaPlayer mp = null; + AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); + int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + am.setStreamVolume(AudioManager.STREAM_MUSIC, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), + 0); + getReverb(0); + try { + probe = new EnergyProbe(0); + // creating a volume controller on output mix ensures that ro.audio.silent mutes + // audio after the effects and not before + vc = new AudioEffect( + AudioEffect.EFFECT_TYPE_NULL, + UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"), + 0, + 0); + vc.setEnabled(true); + + mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SINE_200_1000); + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + mp.attachAuxEffect(mReverb.getId()); + mp.setAuxEffectSendLevel(1.0f); + mReverb.setPreset((short)PresetReverb.PRESET_PLATE); + mReverb.setEnabled(true); + mp.prepare(); + mp.start(); + Thread.sleep(1000); + mp.stop(); + Thread.sleep(200); + // measure energy around 1kHz after media player was stopped for 200 ms + int energy1000 = probe.capture(1000); + assertTrue(msg + ": reverb has no effect", energy1000 > 0); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } catch (InterruptedException e) { + loge(msg, "sleep() interrupted"); + } + finally { + releaseReverb(); + if (mp != null) { + mp.release(); + } + if (vc != null) { + vc.release(); + } + if (probe != null) { + probe.release(); + } + am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } + assertTrue(msg, result); + } + + //Test case 2.1: test actual insert reverb influence on sound + @LargeTest + public void test2_1InsertSoundModification() throws Exception { + boolean result = false; + String msg = "test2_1InsertSoundModification()"; + EnergyProbe probe = null; + AudioEffect vc = null; + MediaPlayer mp = null; + AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); + int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + am.setStreamVolume(AudioManager.STREAM_MUSIC, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), + 0); + try { + probe = new EnergyProbe(0); + // creating a volume controller on output mix ensures that ro.audio.silent mutes + // audio after the effects and not before + vc = new AudioEffect( + AudioEffect.EFFECT_TYPE_NULL, + UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"), + 0, + 0); + vc.setEnabled(true); + + mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SINE_200_1000); + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + getReverb(mp.getAudioSessionId()); + mReverb.setPreset((short)PresetReverb.PRESET_PLATE); + mReverb.setEnabled(true); + mp.prepare(); + mp.start(); + Thread.sleep(1000); + mp.stop(); + Thread.sleep(200); + // measure energy around 1kHz after media player was stopped for 200 ms + int energy1000 = probe.capture(1000); + assertTrue(msg + ": reverb has no effect", energy1000 > 0); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } catch (InterruptedException e) { + loge(msg, "sleep() interrupted"); + } + finally { + releaseReverb(); + if (mp != null) { + mp.release(); + } + if (vc != null) { + vc.release(); + } + if (probe != null) { + probe.release(); + } + am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // private methods + //---------------------------------- + + private void getReverb(int session) { + if (mReverb == null || session != mSession) { + if (session != mSession && mReverb != null) { + mReverb.release(); + mReverb = null; + } + try { + mReverb = new PresetReverb(0, session); + mSession = session; + } catch (IllegalArgumentException e) { + Log.e(TAG, "getReverb() PresetReverb not found exception: "+e); + } catch (UnsupportedOperationException e) { + Log.e(TAG, "getReverb() Effect library not loaded exception: "+e); + } + } + assertNotNull("could not create mReverb", mReverb); + } + + private void releaseReverb() { + if (mReverb != null) { + mReverb.release(); + mReverb = null; + } + } + +} + diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java index 6b8ae442a1dc..517d5757fd1c 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java @@ -43,7 +43,7 @@ import java.util.UUID; */ public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { private String TAG = "MediaVirtualizerTest"; - private final static int MIN_ENERGY_RATIO_2 = 4; + private final static int MIN_ENERGY_RATIO_2 = 3; private final static short TEST_STRENGTH = 500; private Virtualizer mVirtualizer = null; @@ -224,7 +224,7 @@ public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<Media int energy200 = probe.capture(200); int energy1000 = probe.capture(1000); // verify that the energy ration between low and high frequencies is at least - // four times higher with virtualizer on. + // MIN_ENERGY_RATIO_2 times higher with virtualizer on. // NOTE: this is what is observed with current virtualizer implementation and the test // audio file but is not the primary effect of the virtualizer. A better way would // be to have a stereo PCM capture and check that a strongly paned input is centered @@ -264,52 +264,6 @@ public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<Media // private methods //---------------------------------- - private class EnergyProbe { - Visualizer mVisualizer = null; - private byte[] mFft = new byte[1024]; - - public EnergyProbe(int session) { - mVisualizer = new Visualizer(session); - mVisualizer.setCaptureSize(1024); - } - - public int capture(int freq) throws InterruptedException { - int energy = 0; - int count = 0; - if (mVisualizer != null) { - mVisualizer.setEnabled(true); - for (int i = 0; i < 10; i++) { - if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) { - // TODO: check speex FFT as it seems to return only the number of points - // correspondong to valid part of the spectrum (< Fs). - // e.g., if the number of points is 1024, it covers the frequency range - // 0 to 22050 instead of 0 to 44100 as expected from an FFT. - int bin = freq / (22050 / 1024); - int tmp = 0; - for (int j = bin-2; j < bin+3; j++) { - tmp += (int)mFft[j] * (int)mFft[j]; - } - energy += tmp/5; - count++; - } - Thread.sleep(50); - } - mVisualizer.setEnabled(false); - } - if (count == 0) { - return 0; - } - return energy/count; - } - - public void release() { - if (mVisualizer != null) { - mVisualizer.release(); - mVisualizer = null; - } - } - } - private void getVirtualizer(int session) { if (mVirtualizer == null || session != mSession) { if (session != mSession && mVirtualizer != null) { |