From e3559444b3a9a216e86bdc3bc7c087443076f9e4 Mon Sep 17 00:00:00 2001 From: Shunkai Yao Date: Thu, 13 Oct 2022 21:11:11 +0000 Subject: [PATCH 1/2] AIDL effect: Add Equalizer parameters definition Bug: 238913361 Test: atest VtsHalAudioEffectTargetTest atest VtsHalAudioEffectFactoryTargetTest atest VtsHalEqualizerTargetTest Change-Id: Ice305308379598a8cd286fc14818798e589ffc18 --- .../hardware/audio/effect/Descriptor.aidl | 4 + .../hardware/audio/effect/Equalizer.aidl | 28 +++- .../android/hardware/audio/effect/Flags.aidl | 37 +++++ .../hardware/audio/effect/Parameter.aidl | 18 ++- .../hardware/audio/effect/CommandId.aidl | 13 +- .../hardware/audio/effect/Descriptor.aidl | 73 ++++++++-- .../hardware/audio/effect/Equalizer.aidl | 68 ++++++++-- .../android/hardware/audio/effect/Flags.aidl | 127 +++++++++++++++++- .../hardware/audio/effect/IEffect.aidl | 16 ++- .../hardware/audio/effect/IFactory.aidl | 2 +- .../hardware/audio/effect/Parameter.aidl | 83 ++++++++++-- 11 files changed, 417 insertions(+), 52 deletions(-) diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl index 07b25f8896..4707011963 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Descriptor.aidl @@ -59,5 +59,9 @@ parcelable Descriptor { @VintfStability parcelable Common { android.hardware.audio.effect.Descriptor.Identity id; + int cpuLoad; + int memoryUsage; + @utf8InCpp String name; + @utf8InCpp String implementor; } } diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Equalizer.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Equalizer.aidl index 31732d3663..f7af30058e 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Equalizer.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Equalizer.aidl @@ -35,12 +35,32 @@ package android.hardware.audio.effect; @VintfStability union Equalizer { android.hardware.audio.effect.Equalizer.VendorExtension vendor; - @VintfStability - parcelable Capability { - ParcelableHolder extension; - } + android.hardware.audio.effect.Equalizer.BandLevel[] bandLevels; + int preset; @VintfStability parcelable VendorExtension { ParcelableHolder extension; } + @VintfStability + parcelable Capability { + ParcelableHolder extension; + android.hardware.audio.effect.Equalizer.BandFrequency[] bandFrequencies; + android.hardware.audio.effect.Equalizer.Preset[] presets; + } + @VintfStability + parcelable BandLevel { + int index; + int level; + } + @VintfStability + parcelable BandFrequency { + int index; + int min; + int max; + } + @VintfStability + parcelable Preset { + int index; + @utf8InCpp String name; + } } diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl index af774e8d7d..285ff18e0f 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Flags.aidl @@ -34,4 +34,41 @@ package android.hardware.audio.effect; @VintfStability parcelable Flags { + android.hardware.audio.effect.Flags.Type type = android.hardware.audio.effect.Flags.Type.INSERT; + android.hardware.audio.effect.Flags.Insert insert = android.hardware.audio.effect.Flags.Insert.ANY; + android.hardware.audio.effect.Flags.Volume volume = android.hardware.audio.effect.Flags.Volume.NONE; + android.hardware.audio.effect.Flags.HardwareAccelerator hwAcceleratorMode = android.hardware.audio.effect.Flags.HardwareAccelerator.NONE; + boolean offloadIndication; + boolean deviceIndication; + boolean audioModeIndication; + boolean audioSourceIndication; + boolean noProcessing; + @Backing(type="byte") @VintfStability + enum Type { + INSERT = 0, + AUXILIARY = 1, + REPLACE = 2, + PRE_PROC = 3, + POST_PROC = 4, + } + @Backing(type="byte") @VintfStability + enum Insert { + ANY = 0, + FIRST = 1, + LAST = 2, + EXCLUSIVE = 3, + } + @Backing(type="byte") @VintfStability + enum Volume { + NONE = 0, + CTRL = 1, + IND = 2, + MONITOR = 3, + } + @Backing(type="byte") @VintfStability + enum HardwareAccelerator { + NONE = 0, + SIMPLE = 1, + TUNNEL = 2, + } } diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl index 16bd3bbd33..547112a6ae 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Parameter.aidl @@ -35,28 +35,42 @@ package android.hardware.audio.effect; @VintfStability union Parameter { android.hardware.audio.effect.Parameter.Common common; + android.media.audio.common.AudioDeviceType device; + android.media.audio.common.AudioMode mode; + android.media.audio.common.AudioSource source; + android.hardware.audio.effect.Parameter.Volume volume; + boolean offload; android.hardware.audio.effect.Parameter.VendorEffectParameter vendorEffect; android.hardware.audio.effect.Parameter.Specific specific; @VintfStability union Id { int commonTag; int vendorTag; - android.hardware.audio.effect.Parameter.Specific.Tag specificTag; + android.hardware.audio.effect.Parameter.Specific.Id specificId; } @VintfStability parcelable Common { int session; int ioHandle; - android.media.audio.common.AudioDeviceDescription device; android.media.audio.common.AudioConfig input; android.media.audio.common.AudioConfig output; } @VintfStability + parcelable Volume { + float left; + float right; + } + @VintfStability parcelable VendorEffectParameter { ParcelableHolder extension; } @VintfStability union Specific { + android.hardware.audio.effect.Parameter.Specific.Id id; android.hardware.audio.effect.Equalizer equalizer; + @VintfStability + union Id { + android.hardware.audio.effect.Equalizer.Tag equalizerTag = android.hardware.audio.effect.Equalizer.Tag.vendor; + } } } diff --git a/audio/aidl/android/hardware/audio/effect/CommandId.aidl b/audio/aidl/android/hardware/audio/effect/CommandId.aidl index 208c1637b4..d940b422b4 100644 --- a/audio/aidl/android/hardware/audio/effect/CommandId.aidl +++ b/audio/aidl/android/hardware/audio/effect/CommandId.aidl @@ -27,7 +27,9 @@ package android.hardware.audio.effect; @VintfStability @Backing(type="int") enum CommandId { - /// MUST be supported by all effects + /** + * Commands MUST be supported by all effects. + */ /** * Start effect engine processing. * An effect instance must start processing data and transfer to PROCESSING state if it is in @@ -53,10 +55,13 @@ enum CommandId { */ RESET = 2, - /// MUST be supported by a specific type of effect. - // Commands must supported by Equalizer. + /** + * Commands MUST be supported by a specific type of effect. + */ - /// Extension commands for vendor. + /** + * Extension commands for vendor. + */ VENDOR_COMMAND_0 = 0x100, VENDOR_COMMAND_1, VENDOR_COMMAND_2, diff --git a/audio/aidl/android/hardware/audio/effect/Descriptor.aidl b/audio/aidl/android/hardware/audio/effect/Descriptor.aidl index 9c18f2ef0f..562c2499cb 100644 --- a/audio/aidl/android/hardware/audio/effect/Descriptor.aidl +++ b/audio/aidl/android/hardware/audio/effect/Descriptor.aidl @@ -36,31 +36,57 @@ parcelable Descriptor { /** * UUID for effect types, these definitions are in sync with SDK, see @c AudioEffect.java. */ - // UUID for environmental reverberation effect type. + /** + * UUID for environmental reverberation effect type. + */ const String EFFECT_TYPE_UUID_ENV_REVERB = "c2e5d5f0-94bd-4763-9cac-4e234d06839e"; - // UUID for preset reverberation effect type. + /** + * UUID for preset reverberation effect type. + */ const String EFFECT_TYPE_UUID_PRESET_REVERB = "47382d60-ddd8-11db-bf3a-0002a5d5c51b"; - // UUID for equalizer effect type. + /** + * UUID for equalizer effect type. + */ const String EFFECT_TYPE_UUID_EQUALIZER = "0bed4300-ddd6-11db-8f34-0002a5d5c51b"; - // UUID for bass boost effect type. + /** + * UUID for bass boost effect type. + */ const String EFFECT_TYPE_UUID_BASS_BOOST = "0634f220-ddd4-11db-a0fc-0002a5d5c51b"; - // UUID for virtualizer effect type. + /** + * UUID for virtualizer effect type. + */ const String EFFECT_TYPE_UUID_VIRTUALIZER = "37cc2c00-dddd-11db-8577-0002a5d5c51b"; - // UUID for Automatic Gain Control (AGC) type. + /** + * UUID for Automatic Gain Control (AGC) type. + */ const String EFFECT_TYPE_UUID_AGC = "0a8abfe0-654c-11e0-ba26-0002a5d5c51b"; - // UUID for Acoustic Echo Canceler (AEC) type. + /** + * UUID for Acoustic Echo Canceler (AEC) type. + */ const String EFFECT_TYPE_UUID_AEC = "7b491460-8d4d-11e0-bd61-0002a5d5c51b"; - // UUID for Noise Suppressor (NS) type. + /** + * UUID for Noise Suppressor (NS) type. + */ const String EFFECT_TYPE_UUID_NS = "58b4b260-8e06-11e0-aa8e-0002a5d5c51b"; - // UUID for Loudness Enhancer type. + /** + * UUID for Loudness Enhancer type. + */ const String EFFECT_TYPE_UUID_LOUDNESS_ENHANCER = "fe3199be-aed0-413f-87bb-11260eb63cf1"; - // UUID for Dynamics Processing type. + /** + * UUID for Dynamics Processing type. + */ const String EFFECT_TYPE_UUID_DYNAMICS_PROCESSING = "7261676f-6d75-7369-6364-28e2fd3ac39e"; - // UUID for Haptic Generator type. + /** + * UUID for Haptic Generator type. + */ const String EFFECT_TYPE_UUID_HAPTIC_GENERATOR = "1411e6d6-aecd-4021-a1cf-a6aceb0d71e5"; - // UUID for Spatializer type. + /** + * UUID for Spatializer type. + */ const String EFFECT_TYPE_UUID_SPATIALIZER = "ccd4cf09-a79d-46c2-9aae-06a1698d6c8f"; - // UUID for Volume type. The volume effect is used for automated tests only. + /** + * UUID for Volume type. The volume effect is used for automated tests only. + */ const String EFFECT_TYPE_UUID_VOLUME = "09e8ede0-ddde-11db-b4f6-0002a5d5c51b"; /** @@ -87,13 +113,32 @@ parcelable Descriptor { Flags flags; } - // Common attributes of all effect implementation. + /** + * Common attributes of all effect implementation. + */ @VintfStability parcelable Common { /** * Identity of effect implementation. */ Identity id; + /** + * CPU load indication expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE) + * with 0 WS. + */ + int cpuLoad; + /** + * Data memory usage expressed in KB and includes only dynamically allocated memory. + */ + int memoryUsage; + /** + * Human readable effect name, no intended to display on UI directly. + */ + @utf8InCpp String name; + /** + * Human readable effect implementor name, no intended to display on UI directly. + */ + @utf8InCpp String implementor; } Common common; diff --git a/audio/aidl/android/hardware/audio/effect/Equalizer.aidl b/audio/aidl/android/hardware/audio/effect/Equalizer.aidl index 309874d183..7fe9bb2b67 100644 --- a/audio/aidl/android/hardware/audio/effect/Equalizer.aidl +++ b/audio/aidl/android/hardware/audio/effect/Equalizer.aidl @@ -20,14 +20,23 @@ import android.media.audio.common.AudioProfile; /** * Equalizer specific definitions. + * + * All parameters defined in union Equalizer must be gettable and settable. The capabilities defined + * in Equalizer.Capability can only acquired with IEffect.getDescriptor() and not settable. */ @VintfStability union Equalizer { /** - * Defines Equalizer implementation capabilities, it MUST be supported by all equalizer - * implementations. - * - * Equalizer.Capability definition is used by android.hardware.audio.effect.Capability. + * Vendor Equalizer implementation definition for additional parameters. + */ + @VintfStability + parcelable VendorExtension { + ParcelableHolder extension; + } + VendorExtension vendor; + + /** + * Capability MUST be supported by Equalizer implementation. */ @VintfStability parcelable Capability { @@ -36,12 +45,55 @@ union Equalizer { * definition not enough. */ ParcelableHolder extension; + + /** + * Bands frequency ranges supported. + */ + BandFrequency[] bandFrequencies; + + /** + * Presets name and index. + */ + Preset[] presets; } - // Vendor Equalizer implementation definition for additional parameters. + /** + * Level setting for each band. + */ @VintfStability - parcelable VendorExtension { - ParcelableHolder extension; + parcelable BandLevel { + int index; + int level; } - VendorExtension vendor; + + /** + * Supported minimal and maximal frequency for each band. + */ + @VintfStability + parcelable BandFrequency { + int index; + int min; + int max; + } + + /** + * Factory presets supported. + */ + @VintfStability + parcelable Preset { + int index; + /** + * Preset name, used to identify presets but no intended to display on UI directly. + */ + @utf8InCpp String name; + } + + /** + * Level for each band. + */ + BandLevel[] bandLevels; + /** + * Index of current preset. + */ + int preset; } diff --git a/audio/aidl/android/hardware/audio/effect/Flags.aidl b/audio/aidl/android/hardware/audio/effect/Flags.aidl index 8add975752..f449c2d774 100644 --- a/audio/aidl/android/hardware/audio/effect/Flags.aidl +++ b/audio/aidl/android/hardware/audio/effect/Flags.aidl @@ -17,10 +17,131 @@ package android.hardware.audio.effect; /** - * The common part of available capability/configuration for effects. For effect type specific - * capability, see @c android.hardware.audio.effect.Capability. + * Some common capability for an effect instance. */ @VintfStability parcelable Flags { - // TODO: add Effect engine defined capabilities/requirements flags. + /** + * Type of connection. + */ + @VintfStability + @Backing(type="byte") + enum Type { + /** + * After track process. + */ + INSERT = 0, + /** + * Connect to track auxiliary output and use send level. + */ + AUXILIARY = 1, + /** + * Rreplaces track process function; must implement SRC, volume and mono to stereo. + */ + REPLACE = 2, + /** + * Applied below audio HAL on in. + */ + PRE_PROC = 3, + /** + * Applied below audio HAL on out. + */ + POST_PROC = 4, + } + Type type = Type.INSERT; + + /** + * Insertion preference. + */ + @VintfStability + @Backing(type="byte") + enum Insert { + ANY = 0, + /** + * First of the chain. + */ + FIRST = 1, + /** + * Last of the chain. + */ + LAST = 2, + /** + * Exclusive (only effect in the insert chain. + */ + EXCLUSIVE = 3, + } + Insert insert = Insert.ANY; + + @VintfStability + @Backing(type="byte") + enum Volume { + NONE = 0, + /** + * Implements volume control. + */ + CTRL = 1, + /** + * Requires volume indication. + */ + IND = 2, + /** + * Monitors requested volume. + */ + MONITOR = 3, + } + Volume volume = Volume.NONE; + + @VintfStability + @Backing(type="byte") + enum HardwareAccelerator { + /** + * No hardware acceleration + */ + NONE = 0, + /** + * Non tunneled hw acceleration: effect reads the samples, send them to HW accelerated + * effect processor, reads back the processed samples and returns them to the output buffer. + */ + SIMPLE = 1, + /** + * The effect interface is only used to control the effect engine. This mode is relevant for + * global effects actually applied by the audio hardware on the output stream. + */ + TUNNEL = 2, + } + HardwareAccelerator hwAcceleratorMode = HardwareAccelerator.NONE; + + /** + * Effect instance set this flag to true if it requires update on if the playback thread the + * effect attached to is offloaded or not. In this case the framework must call + * IEffect.setParameter(Parameter.offload) to notify effect instance when playback thread + * offload changes. + */ + boolean offloadIndication; + + /** + * Effect instance set this flag to true if it requires device change update. In this case the + * framework must call IEffect.setParameter(Parameter.device) to notify effect instance when the + * device changes. + */ + boolean deviceIndication; + + /** + * Effect instance set this flag to true if it requires audio mode change update. In this case + * the framework must call IEffect.setParameter(Parameter.mode) to notify effect instance when + * the audio mode changes. + */ + boolean audioModeIndication; + + /** + * Effect instance set this flag to true if it requires audio source change update. In this case + * the framework must call IEffect.setParameter(Parameter.source) to notify effect instance when + * the audio source changes. + */ + boolean audioSourceIndication; + + /** + * Set to true if no processing done for this effect instance. + */ + boolean noProcessing; } diff --git a/audio/aidl/android/hardware/audio/effect/IEffect.aidl b/audio/aidl/android/hardware/audio/effect/IEffect.aidl index 9ab8b02249..5dd390f544 100644 --- a/audio/aidl/android/hardware/audio/effect/IEffect.aidl +++ b/audio/aidl/android/hardware/audio/effect/IEffect.aidl @@ -53,14 +53,22 @@ interface IEffect { int fmqByteProduced; } - // Return data structure of IEffect.open() interface. + /** + * Return data structure of IEffect.open() interface. + */ @VintfStability parcelable OpenEffectReturn { - // Message queue for effect processing status. + /** + * Message queue for effect processing status. + */ MQDescriptor statusMQ; - // Message queue for input data buffer. + /** + * Message queue for input data buffer. + */ MQDescriptor inputDataMQ; - // Message queue for output data buffer. + /** + * Message queue for output data buffer. + */ MQDescriptor outputDataMQ; } diff --git a/audio/aidl/android/hardware/audio/effect/IFactory.aidl b/audio/aidl/android/hardware/audio/effect/IFactory.aidl index db26e613de..e56c24f5e5 100644 --- a/audio/aidl/android/hardware/audio/effect/IFactory.aidl +++ b/audio/aidl/android/hardware/audio/effect/IFactory.aidl @@ -50,7 +50,7 @@ interface IFactory { * An effect can exist more than once in the returned list, which means this effect must be used * in more than one processing type. * - * @param type Type of processing to query, can be AudioStreamType or AudioSource. + * @param type Type of processing to query, can be AudioStreamType, AudioSource, or null. * @return list of processing defined with the optional filter by Processing.Type. */ Processing[] queryProcessing(in @nullable Processing.Type type); diff --git a/audio/aidl/android/hardware/audio/effect/Parameter.aidl b/audio/aidl/android/hardware/audio/effect/Parameter.aidl index 2951660b6e..739c9ff4c2 100644 --- a/audio/aidl/android/hardware/audio/effect/Parameter.aidl +++ b/audio/aidl/android/hardware/audio/effect/Parameter.aidl @@ -18,7 +18,10 @@ package android.hardware.audio.effect; import android.hardware.audio.effect.Equalizer; import android.media.audio.common.AudioConfig; -import android.media.audio.common.AudioDeviceDescription; +import android.media.audio.common.AudioDeviceType; +import android.media.audio.common.AudioMode; +import android.media.audio.common.AudioSource; + /** * Defines all parameters supported by the effect instance. * @@ -40,12 +43,18 @@ union Parameter { */ @VintfStability union Id { - // Common parameter tag. + /** + * Common parameter tag. + */ int commonTag; - // Vendor defined parameter tag. + /** + * Vendor defined parameter tag. + */ int vendorTag; - // Specific effect parameter tag. - Specific.Tag specificTag; + /** + * Specific effect parameter tag. + */ + Specific.Id specificId; } /** @@ -53,19 +62,61 @@ union Parameter { */ @VintfStability parcelable Common { - // Type of Audio device. + /** + * Type of Audio device. + */ int session; - // I/O Handle. + /** + * I/O Handle. + */ int ioHandle; - // Type of Audio device. - AudioDeviceDescription device; - // Input config. + /** + * Input config. + */ AudioConfig input; - // Output config. + /** + * Output config. + */ AudioConfig output; } Common common; + /** + * Used by audio framework to set the device type to effect engine. + * Effect must implement setParameter(device) if Flags.deviceIndication set to true. + */ + AudioDeviceType device; + /** + * Used by audio framework to set the audio mode to effect engine. + * Effect must implement setParameter(mode) if Flags.audioModeIndication set to true. + */ + AudioMode mode; + /** + * Used by audio framework to set the audio source to effect engine. + * Effect must implement setParameter(source) if Flags.audioSourceIndication set to true. + */ + AudioSource source; + + /** + * The volume gain for left and right channel, left and right equals to same value if it's mono. + */ + @VintfStability + parcelable Volume { + float left; + float right; + } + /** + * Used by audio framework to delegate volume control to effect engine. + * Effect must implement setParameter(volume) if Flags.volume set to Volume.IND. + */ + Volume volume; + + /** + * Used by audio framework to delegate offload information to effect engine. + * Effect must implement setParameter(offload) if Flags.offloadSupported set to true. + */ + boolean offload; + /** * Parameters for vendor extension effect implementation usage. */ @@ -80,8 +131,16 @@ union Parameter { */ @VintfStability union Specific { + @VintfStability + union Id { + /** + * Equalizer.Tag to identify the parameters in Equalizer. + */ + Equalizer.Tag equalizerTag = Equalizer.Tag.vendor; + } + Id id; + Equalizer equalizer; - // TODO: add other effect definitions here } Specific specific; } From a4ab38caf082c122e7f3f710c46365eb10e51905 Mon Sep 17 00:00:00 2001 From: Shunkai Yao Date: Fri, 14 Oct 2022 01:07:47 +0000 Subject: [PATCH 2/2] AIDL effect: Add Equalizer parameters implementation and vts Bug: 238913361 Test: atest VtsHalAudioEffectTargetTest atest VtsHalAudioEffectFactoryTargetTest atest VtsHalEqualizerTargetTest Change-Id: I94b2283ca2aa0e45715e1c9ac3ea6ad809ec2a2c --- audio/aidl/default/Android.bp | 2 +- audio/aidl/default/EffectThread.cpp | 25 +- audio/aidl/default/equalizer/Equalizer.cpp | 170 ++++--- .../include/effect-impl/EffectContext.h | 65 +++ .../include/effect-impl/EffectThread.h | 15 +- .../default/include/effect-impl/EffectTypes.h | 51 ++ .../default/include/effect-impl/EffectUUID.h | 11 + .../include/effect-impl/EffectWorker.h | 74 +++ .../include/equalizer-impl/EqualizerSw.h | 63 ++- audio/aidl/vts/Android.bp | 39 ++ audio/aidl/vts/EffectFactoryHelper.h | 30 +- audio/aidl/vts/EffectHelper.h | 310 +++++++++++++ .../VtsHalAudioEffectFactoryTargetTest.cpp | 23 +- .../aidl/vts/VtsHalAudioEffectTargetTest.cpp | 434 +++++++----------- audio/aidl/vts/VtsHalEqualizerTargetTest.cpp | 233 ++++++++++ 15 files changed, 1152 insertions(+), 393 deletions(-) create mode 100644 audio/aidl/default/include/effect-impl/EffectContext.h create mode 100644 audio/aidl/default/include/effect-impl/EffectTypes.h create mode 100644 audio/aidl/default/include/effect-impl/EffectWorker.h create mode 100644 audio/aidl/vts/EffectHelper.h create mode 100644 audio/aidl/vts/VtsHalEqualizerTargetTest.cpp diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp index 2baaad9a84..e64b90cf1f 100644 --- a/audio/aidl/default/Android.bp +++ b/audio/aidl/default/Android.bp @@ -116,6 +116,6 @@ cc_binary { cc_library_headers { name: "libaudioaidl_headers", export_include_dirs: ["include"], - vendor: true, + vendor_available: true, host_supported: true, } diff --git a/audio/aidl/default/EffectThread.cpp b/audio/aidl/default/EffectThread.cpp index 0ad9a146c6..80f120b0f0 100644 --- a/audio/aidl/default/EffectThread.cpp +++ b/audio/aidl/default/EffectThread.cpp @@ -28,11 +28,11 @@ EffectThread::EffectThread() { } EffectThread::~EffectThread() { - destroy(); + destroyThread(); LOG(DEBUG) << __func__ << " done"; }; -RetCode EffectThread::create(const std::string& name, const int priority) { +RetCode EffectThread::createThread(const std::string& name, const int priority) { if (mThread.joinable()) { LOG(WARNING) << __func__ << " thread already created, no-op"; return RetCode::SUCCESS; @@ -44,7 +44,7 @@ RetCode EffectThread::create(const std::string& name, const int priority) { return RetCode::SUCCESS; } -RetCode EffectThread::destroy() { +RetCode EffectThread::destroyThread() { { std::lock_guard lg(mMutex); mStop = mExit = true; @@ -58,10 +58,10 @@ RetCode EffectThread::destroy() { return RetCode::SUCCESS; } -RetCode EffectThread::start() { +RetCode EffectThread::startThread() { if (!mThread.joinable()) { LOG(ERROR) << __func__ << " thread already destroyed"; - return RetCode::ERROR; + return RetCode::ERROR_THREAD; } { @@ -78,10 +78,10 @@ RetCode EffectThread::start() { return RetCode::SUCCESS; } -RetCode EffectThread::stop() { +RetCode EffectThread::stopThread() { if (!mThread.joinable()) { LOG(ERROR) << __func__ << " thread already destroyed"; - return RetCode::ERROR; + return RetCode::ERROR_THREAD; } { @@ -117,15 +117,4 @@ void EffectThread::threadLoop() { } } -std::string toString(RetCode& code) { - switch (code) { - case RetCode::SUCCESS: - return "SUCCESS"; - case RetCode::ERROR: - return "ERROR"; - default: - return "EnumError"; - } -} - } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/equalizer/Equalizer.cpp b/audio/aidl/default/equalizer/Equalizer.cpp index 2e4e538b63..43fa206151 100644 --- a/audio/aidl/default/equalizer/Equalizer.cpp +++ b/audio/aidl/default/equalizer/Equalizer.cpp @@ -16,10 +16,12 @@ #define LOG_TAG "AHAL_EqualizerSw" #include -#include +#include #include -#include "effect-impl/EffectUUID.h" +#include +#include + #include "equalizer-impl/EqualizerSw.h" using android::hardware::audio::common::getFrameSizeInBytes; @@ -68,20 +70,26 @@ ndk::ScopedAStatus EqualizerSw::open(const Parameter::Common& common, auto& output = common.output; size_t inputFrameSize = getFrameSizeInBytes(input.base.format, input.base.channelMask); size_t outputFrameSize = getFrameSizeInBytes(output.base.format, output.base.channelMask); - if (!createFmq(1, input.frameCount * inputFrameSize, output.frameCount * outputFrameSize, - _aidl_return)) { + mContext = std::make_shared(1, input.frameCount * inputFrameSize, + output.frameCount * outputFrameSize); + if (!mContext) { + LOG(ERROR) << __func__ << " created EqualizerSwContext failed"; return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_UNSUPPORTED_OPERATION, "FailedToCreateFmq"); } + setContext(mContext); // create the worker thread - if (RetCode::SUCCESS != mWorker->create(LOG_TAG)) { + if (RetCode::SUCCESS != createThread(LOG_TAG)) { LOG(ERROR) << __func__ << " created worker thread failed"; - destroyFmq(); + mContext.reset(); return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_UNSUPPORTED_OPERATION, - "FailedToCreateFmq"); + "FailedToCreateWorker"); } + _aidl_return->statusMQ = mContext->getStatusFmq()->dupeDesc(); + _aidl_return->inputDataMQ = mContext->getInputDataFmq()->dupeDesc(); + _aidl_return->outputDataMQ = mContext->getOutputDataFmq()->dupeDesc(); mState = State::IDLE; return ndk::ScopedAStatus::ok(); } @@ -98,8 +106,9 @@ ndk::ScopedAStatus EqualizerSw::close() { // stop the worker thread mState = State::INIT; - mWorker->destroy(); - destroyFmq(); + destroyThread(); + mContext.reset(); + LOG(DEBUG) << __func__; return ndk::ScopedAStatus::ok(); } @@ -121,19 +130,19 @@ ndk::ScopedAStatus EqualizerSw::command(CommandId in_commandId) { case CommandId::START: // start processing. mState = State::PROCESSING; - mWorker->start(); + startThread(); LOG(DEBUG) << __func__ << " state: " << toString(mState); return ndk::ScopedAStatus::ok(); case CommandId::STOP: // stop processing. mState = State::IDLE; - mWorker->stop(); + stopThread(); LOG(DEBUG) << __func__ << " state: " << toString(mState); return ndk::ScopedAStatus::ok(); case CommandId::RESET: // TODO: reset buffer status. mState = State::IDLE; - mWorker->stop(); + stopThread(); LOG(DEBUG) << __func__ << " state: " << toString(mState); return ndk::ScopedAStatus::ok(); default: @@ -173,23 +182,27 @@ ndk::ScopedAStatus EqualizerSw::getParameter(const Parameter::Id& in_paramId, LOG(DEBUG) << __func__ << " get: " << _aidl_return->toString(); return ndk::ScopedAStatus::ok(); } - case Parameter::Id::specificTag: { - auto& id = in_paramId.get(); - if (id != Parameter::Specific::equalizer) { - LOG(ERROR) << " unsupported parameter Id: " << in_paramId.toString(); - return ndk::ScopedAStatus::fromExceptionCodeWithMessage( - EX_ILLEGAL_ARGUMENT, "Parameter::IdNotSupported"); - } + case Parameter::Id::specificId: { + auto& id = in_paramId.get(); Parameter::Specific specific; - specific.set(mEqualizerParam); + ndk::ScopedAStatus status = getSpecificParameter(id, &specific); + if (!status.isOk()) { + LOG(ERROR) << __func__ + << " getSpecificParameter error: " << status.getDescription(); + return status; + } _aidl_return->set(specific); LOG(DEBUG) << __func__ << _aidl_return->toString(); return ndk::ScopedAStatus::ok(); } - default: - return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, - "Parameter::IdNotSupported"); + case Parameter::Id::vendorTag: { + LOG(DEBUG) << __func__ << " noop for vendor tag now"; + return ndk::ScopedAStatus::ok(); + } } + LOG(ERROR) << " unsupported tag: " << toString(tag); + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Parameter:IdNotSupported"); } ndk::ScopedAStatus EqualizerSw::getState(State* _aidl_return) { @@ -198,35 +211,12 @@ ndk::ScopedAStatus EqualizerSw::getState(State* _aidl_return) { } /// Private methods. -bool EqualizerSw::createFmq(int statusDepth, int inBufferSize, int outBufferSize, - OpenEffectReturn* ret) { - mStatusMQ = std::make_unique(statusDepth, true /*configureEventFlagWord*/); - mInputMQ = std::make_unique(inBufferSize); - mOutputMQ = std::make_unique(outBufferSize); - - if (!mStatusMQ->isValid() || !mInputMQ->isValid() || !mOutputMQ->isValid()) { - LOG(ERROR) << __func__ << " created invalid FMQ"; - return false; - } - ret->statusMQ = mStatusMQ->dupeDesc(); - ret->inputDataMQ = mInputMQ->dupeDesc(); - ret->outputDataMQ = mOutputMQ->dupeDesc(); - return true; -} - -void EqualizerSw::destroyFmq() { - mStatusMQ.reset(nullptr); - mInputMQ.reset(nullptr); - mOutputMQ.reset(nullptr); -} - ndk::ScopedAStatus EqualizerSw::setCommonParameter(const Parameter::Common& common) { mCommonParam = common; LOG(DEBUG) << __func__ << " set: " << mCommonParam.toString(); return ndk::ScopedAStatus::ok(); } -// TODO: implementation need change to save all parameters. ndk::ScopedAStatus EqualizerSw::setSpecificParameter(const Parameter::Specific& specific) { if (Parameter::Specific::equalizer != specific.getTag()) { LOG(ERROR) << " unsupported effect: " << specific.toString(); @@ -234,9 +224,76 @@ ndk::ScopedAStatus EqualizerSw::setSpecificParameter(const Parameter::Specific& "EffectNotSupported"); } - mEqualizerParam = specific.get(); - LOG(DEBUG) << __func__ << mEqualizerParam.toString(); - return ndk::ScopedAStatus::ok(); + auto& eqParam = specific.get(); + auto tag = eqParam.getTag(); + switch (tag) { + case Equalizer::bandLevels: { + auto& bandLevels = eqParam.get(); + const auto& [minItem, maxItem] = std::minmax_element( + bandLevels.begin(), bandLevels.end(), + [](const auto& a, const auto& b) { return a.index < b.index; }); + if (bandLevels.size() >= NUM_OF_BANDS || minItem->index < 0 || + maxItem->index >= NUM_OF_BANDS) { + LOG(ERROR) << " bandLevels " << bandLevels.size() << "minIndex " << minItem->index + << "maxIndex " << maxItem->index << " illegal "; + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "ExceedMaxBandNum"); + } + mBandLevels = bandLevels; + return ndk::ScopedAStatus::ok(); + } + case Equalizer::preset: { + int preset = eqParam.get(); + if (preset < 0 || preset >= NUM_OF_PRESETS) { + LOG(ERROR) << " preset: " << preset << " invalid"; + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "ExceedMaxBandNum"); + } + mPreset = preset; + LOG(DEBUG) << __func__ << " preset set to " << mPreset; + return ndk::ScopedAStatus::ok(); + } + case Equalizer::vendor: { + LOG(DEBUG) << __func__ << " noop for vendor tag now"; + return ndk::ScopedAStatus::ok(); + } + } + + LOG(ERROR) << __func__ << " unsupported eq param tag: " << toString(tag); + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "ParamNotSupported"); +} + +ndk::ScopedAStatus EqualizerSw::getSpecificParameter(Parameter::Specific::Id id, + Parameter::Specific* specific) { + Equalizer eqParam; + auto tag = id.getTag(); + if (tag != Parameter::Specific::Id::equalizerTag) { + LOG(ERROR) << " invalid tag: " << toString(tag); + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "UnsupportedTag"); + } + auto eqTag = id.get(); + switch (eqTag) { + case Equalizer::bandLevels: { + eqParam.set(mBandLevels); + specific->set(eqParam); + return ndk::ScopedAStatus::ok(); + } + case Equalizer::preset: { + eqParam.set(mPreset); + LOG(DEBUG) << __func__ << " preset " << mPreset; + specific->set(eqParam); + return ndk::ScopedAStatus::ok(); + } + case Equalizer::vendor: { + LOG(DEBUG) << __func__ << " noop for vendor tag now"; + return ndk::ScopedAStatus::ok(); + } + } + LOG(ERROR) << __func__ << " unsupported eq param: " << toString(eqTag); + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "ParamNotSupported"); } void EqualizerSw::cleanUp() { @@ -248,9 +305,18 @@ void EqualizerSw::cleanUp() { } } -// Processing method running in worker thread. -void EqualizerSwWorker::process() { - // TODO: add EQ processing with FMQ, should wait until data available before data processing. +IEffect::Status EqualizerSw::status(binder_status_t status, size_t consumed, size_t produced) { + IEffect::Status ret; + ret.status = status; + ret.fmqByteConsumed = consumed; + ret.fmqByteProduced = produced; + return ret; +} + +// Processing method running in EffectWorker thread. +IEffect::Status EqualizerSw::effectProcessImpl() { + // TODO: get data buffer and process. + return status(STATUS_OK, mContext->availableToRead(), mContext->availableToWrite()); } } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/include/effect-impl/EffectContext.h b/audio/aidl/default/include/effect-impl/EffectContext.h new file mode 100644 index 0000000000..36492ec05f --- /dev/null +++ b/audio/aidl/default/include/effect-impl/EffectContext.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2022 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. + */ + +#pragma once +#include +#include +#include +#include + +#include +#include + +namespace aidl::android::hardware::audio::effect { + +class EffectContext { + public: + typedef ::android::AidlMessageQueue< + IEffect::Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite> + StatusMQ; + typedef ::android::AidlMessageQueue< + int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite> + DataMQ; + + EffectContext(size_t statusDepth, size_t inBufferSize, size_t outBufferSize) { + mStatusMQ = std::make_shared(statusDepth, true /*configureEventFlagWord*/); + mInputMQ = std::make_shared(inBufferSize); + mOutputMQ = std::make_shared(outBufferSize); + + if (!mStatusMQ->isValid() || !mInputMQ->isValid() || !mOutputMQ->isValid()) { + LOG(ERROR) << __func__ << " created invalid FMQ"; + } + mWorkBuffer.reserve(std::max(inBufferSize, outBufferSize)); + }; + + std::shared_ptr getStatusFmq() { return mStatusMQ; }; + std::shared_ptr getInputDataFmq() { return mInputMQ; }; + std::shared_ptr getOutputDataFmq() { return mOutputMQ; }; + + int8_t* getWorkBuffer() { return static_cast(mWorkBuffer.data()); }; + // TODO: update with actual available size + size_t availableToRead() { return mWorkBuffer.capacity(); }; + size_t availableToWrite() { return mWorkBuffer.capacity(); }; + + private: + std::shared_ptr mStatusMQ; + std::shared_ptr mInputMQ; + std::shared_ptr mOutputMQ; + // TODO handle effect process input and output + // work buffer set by effect instances, the access and update are in same thread + std::vector mWorkBuffer; +}; +} // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/include/effect-impl/EffectThread.h b/audio/aidl/default/include/effect-impl/EffectThread.h index e831cea4b3..09a0000001 100644 --- a/audio/aidl/default/include/effect-impl/EffectThread.h +++ b/audio/aidl/default/include/effect-impl/EffectThread.h @@ -22,12 +22,10 @@ #include #include +#include "effect-impl/EffectTypes.h" + namespace aidl::android::hardware::audio::effect { -enum class RetCode { SUCCESS, ERROR }; - -std::string toString(RetCode& code); - class EffectThread { public: // default priority is same as HIDL: ANDROID_PRIORITY_URGENT_AUDIO @@ -35,10 +33,11 @@ class EffectThread { virtual ~EffectThread(); // called by effect implementation. - RetCode create(const std::string& name, const int priority = ANDROID_PRIORITY_URGENT_AUDIO); - RetCode destroy(); - RetCode start(); - RetCode stop(); + RetCode createThread(const std::string& name, + const int priority = ANDROID_PRIORITY_URGENT_AUDIO); + RetCode destroyThread(); + RetCode startThread(); + RetCode stopThread(); // Will call process() in a loop if the thread is running. void threadLoop(); diff --git a/audio/aidl/default/include/effect-impl/EffectTypes.h b/audio/aidl/default/include/effect-impl/EffectTypes.h new file mode 100644 index 0000000000..46cfc0cb25 --- /dev/null +++ b/audio/aidl/default/include/effect-impl/EffectTypes.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 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. + */ + +#pragma once +#include +#include + +namespace aidl::android::hardware::audio::effect { + +enum class RetCode { + SUCCESS, + ERROR_ILLEGAL_PARAMETER, /* Illegal parameter */ + ERROR_THREAD, /* Effect thread error */ + ERROR_NULL_POINTER, /* NULL pointer */ + ERROR_ALIGNMENT_ERROR, /* Memory alignment error */ + ERROR_BLOCK_SIZE_EXCEED /* Maximum block size exceeded */ +}; + +inline std::ostream& operator<<(std::ostream& out, const RetCode& code) { + switch (code) { + case RetCode::SUCCESS: + return out << "SUCCESS"; + case RetCode::ERROR_ILLEGAL_PARAMETER: + return out << "ERROR_ILLEGAL_PARAMETER"; + case RetCode::ERROR_THREAD: + return out << "ERROR_THREAD"; + case RetCode::ERROR_NULL_POINTER: + return out << "ERROR_NULL_POINTER"; + case RetCode::ERROR_ALIGNMENT_ERROR: + return out << "ERROR_ALIGNMENT_ERROR"; + case RetCode::ERROR_BLOCK_SIZE_EXCEED: + return out << "ERROR_BLOCK_SIZE_EXCEED"; + } + + return out << "EnumError: " << code; +} + +} // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/include/effect-impl/EffectUUID.h b/audio/aidl/default/include/effect-impl/EffectUUID.h index 99f6c24d83..48b7137981 100644 --- a/audio/aidl/default/include/effect-impl/EffectUUID.h +++ b/audio/aidl/default/include/effect-impl/EffectUUID.h @@ -21,6 +21,17 @@ namespace aidl::android::hardware::audio::effect { using ::aidl::android::media::audio::common::AudioUuid; +// Null UUID +static const AudioUuid EffectNullUuid = {static_cast(0xec7178ec), + 0xe5e1, + 0x4432, + 0xa3f4, + {0x46, 0x57, 0xe6, 0x79, 0x52, 0x10}}; + +// Zero UUID +static const AudioUuid EffectZeroUuid = { + static_cast(0x0), 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; + // Equalizer type UUID. static const AudioUuid EqualizerTypeUUID = {static_cast(0x0bed4300), 0xddd6, diff --git a/audio/aidl/default/include/effect-impl/EffectWorker.h b/audio/aidl/default/include/effect-impl/EffectWorker.h new file mode 100644 index 0000000000..0fe69ff907 --- /dev/null +++ b/audio/aidl/default/include/effect-impl/EffectWorker.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2022 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. + */ + +#pragma once +#include +#include +#include +#include + +#include "EffectContext.h" +#include "EffectThread.h" + +namespace aidl::android::hardware::audio::effect { + +std::string toString(RetCode& code); + +class EffectWorker : public EffectThread { + public: + // set effect context for worker, suppose to only happen once here + void setContext(std::shared_ptr context) { + std::call_once(mOnceFlag, [&]() { mContext = context; }); + }; + + // handle FMQ and call effect implemented virtual function + void process() override { + if (!mContext) { + LOG(ERROR) << __func__ << " invalid context!"; + return; + } + std::shared_ptr statusMQ = mContext->getStatusFmq(); + std::shared_ptr inputMQ = mContext->getInputDataFmq(); + std::shared_ptr outputMQ = mContext->getOutputDataFmq(); + + // Only this worker will read from input data MQ and write to output data MQ. + auto readSize = inputMQ->availableToRead(), writeSize = outputMQ->availableToWrite(); + if (readSize && writeSize) { + LOG(DEBUG) << __func__ << " available to read " << readSize << " available to write " + << writeSize; + auto buffer = mContext->getWorkBuffer(); + inputMQ->read(buffer, readSize); + IEffect::Status status = effectProcessImpl(); + writeSize = std::min((int32_t)writeSize, status.fmqByteProduced); + outputMQ->write(buffer, writeSize); + statusMQ->writeBlocking(&status, 1); + LOG(DEBUG) << __func__ << " done processing, effect consumed " << status.fmqByteConsumed + << " produced " << status.fmqByteProduced; + } else { + // TODO: maybe add some sleep here to avoid busy waiting + } + } + + // must implement by each effect implementation + virtual IEffect::Status effectProcessImpl() = 0; + + private: + // make sure the context only set once. + std::once_flag mOnceFlag; + std::shared_ptr mContext; +}; + +} // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/include/equalizer-impl/EqualizerSw.h b/audio/aidl/default/include/equalizer-impl/EqualizerSw.h index 58ad1dee1d..aa3a727040 100644 --- a/audio/aidl/default/include/equalizer-impl/EqualizerSw.h +++ b/audio/aidl/default/include/equalizer-impl/EqualizerSw.h @@ -21,20 +21,29 @@ #include #include -#include "effect-impl/EffectThread.h" +#include "effect-impl/EffectContext.h" +#include "effect-impl/EffectTypes.h" +#include "effect-impl/EffectUUID.h" +#include "effect-impl/EffectWorker.h" namespace aidl::android::hardware::audio::effect { -class EqualizerSwWorker : public EffectThread { - // EqualizerSwWorker(const std::string name){EffectThread(name)}; - void process() override; +class EqualizerSwContext : public EffectContext { + public: + EqualizerSwContext(int statusDepth, int inBufferSize, int outBufferSize) + : EffectContext(statusDepth, inBufferSize, outBufferSize) { + LOG(DEBUG) << __func__; + } + + private: + // Add equalizer specific context for processing here }; -class EqualizerSw : public BnEffect { +class EqualizerSw : public BnEffect, EffectWorker { public: EqualizerSw() { - // create the worker - mWorker = std::make_unique(); + Equalizer::Capability eqCap = {.bandFrequencies = mBandFrequency, .presets = mPresets}; + mDesc.capability.set(eqCap); LOG(DEBUG) << __func__; }; ~EqualizerSw() { @@ -52,12 +61,11 @@ class EqualizerSw : public BnEffect { ndk::ScopedAStatus getParameter(const Parameter::Id& in_paramId, Parameter* _aidl_return) override; + IEffect::Status effectProcessImpl() override; + private: - // effect processing thread. - std::unique_ptr mWorker; // Effect descriptor. - const Descriptor mDesc = { - .common = {.id = {.type = EqualizerTypeUUID, .uuid = EqualizerSwImplUUID}}}; + Descriptor mDesc = {.common = {.id = {.type = EqualizerTypeUUID, .uuid = EqualizerSwImplUUID}}}; // Parameters. Parameter::Common mCommonParam; @@ -66,21 +74,32 @@ class EqualizerSw : public BnEffect { // Instance state INIT by default. State mState = State::INIT; - typedef ::android::AidlMessageQueue< - Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite> - StatusMQ; - typedef ::android::AidlMessageQueue< - int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite> - DataMQ; + int mPreset = PRESET_CUSTOM; // the current preset + const std::vector mBandFrequency = {{0, 30000, 120000}, + {1, 120001, 460000}, + {2, 460001, 1800000}, + {3, 1800001, 7000000}, + {4, 7000001, 20000000}}; + // preset band level + std::vector mBandLevels = {{0, 3}, {1, 0}, {2, 0}, {3, 0}, {4, 3}}; + // presets supported by the device + const std::vector mPresets = { + {0, "Normal"}, {1, "Classical"}, {2, "Dance"}, {3, "Flat"}, {4, "Folk"}, + {5, "Heavy Metal"}, {6, "Hip Hop"}, {7, "Jazz"}, {8, "Pop"}, {9, "Rock"}}; + static const int NUM_OF_BANDS = 5; + static const int NUM_OF_PRESETS = 10; + static const int PRESET_CUSTOM = -1; - std::unique_ptr mStatusMQ; - std::unique_ptr mInputMQ; - std::unique_ptr mOutputMQ; + // Equalizer worker context + std::shared_ptr mContext; ndk::ScopedAStatus setCommonParameter(const Parameter::Common& common_param); ndk::ScopedAStatus setSpecificParameter(const Parameter::Specific& specific); - bool createFmq(int statusDepth, int inBufferSize, int outBufferSize, OpenEffectReturn* ret); - void destroyFmq(); + ndk::ScopedAStatus getSpecificParameter(Parameter::Specific::Id id, + Parameter::Specific* specific); + void cleanUp(); + + IEffect::Status status(binder_status_t status, size_t consumed, size_t produced); }; } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp index 63fc4158b5..8de1d79947 100644 --- a/audio/aidl/vts/Android.bp +++ b/audio/aidl/vts/Android.bp @@ -60,6 +60,7 @@ cc_test { "android.hardware.common-V2-ndk", "android.hardware.common.fmq-V1-ndk", ], + header_libs: ["libaudioaidl_headers"], cflags: [ "-Wall", "-Wextra", @@ -75,6 +76,7 @@ cc_test { cc_test { name: "VtsHalAudioEffectTargetTest", defaults: [ + "latest_android_hardware_audio_common_ndk_static", "latest_android_media_audio_common_types_ndk_static", "VtsHalTargetTestDefaults", "use_libaidlvintf_gtest_helper_static", @@ -84,12 +86,49 @@ cc_test { ], shared_libs: [ "libbinder_ndk", + "libfmq", ], static_libs: [ "android.hardware.audio.effect-V1-ndk", "android.hardware.common-V2-ndk", "android.hardware.common.fmq-V1-ndk", + "libaudioaidlcommon", ], + header_libs: ["libaudioaidl_headers"], + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-Wthread-safety", + ], + test_suites: [ + "general-tests", + "vts", + ], +} + +cc_test { + name: "VtsHalEqualizerTargetTest", + defaults: [ + "latest_android_hardware_audio_common_ndk_static", + "latest_android_media_audio_common_types_ndk_static", + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + srcs: [ + "VtsHalEqualizerTargetTest.cpp", + ], + shared_libs: [ + "libbinder_ndk", + "libfmq", + ], + static_libs: [ + "android.hardware.audio.effect-V1-ndk", + "android.hardware.common-V2-ndk", + "android.hardware.common.fmq-V1-ndk", + "libaudioaidlcommon", + ], + header_libs: ["libaudioaidl_headers"], cflags: [ "-Wall", "-Wextra", diff --git a/audio/aidl/vts/EffectFactoryHelper.h b/audio/aidl/vts/EffectFactoryHelper.h index cf94e580b4..63efae0646 100644 --- a/audio/aidl/vts/EffectFactoryHelper.h +++ b/audio/aidl/vts/EffectFactoryHelper.h @@ -24,13 +24,14 @@ #include #include "TestUtils.h" +#include "effect-impl/EffectUUID.h" using namespace android; using aidl::android::hardware::audio::effect::Descriptor; +using aidl::android::hardware::audio::effect::EffectNullUuid; using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; -using aidl::android::hardware::audio::effect::Parameter; using aidl::android::hardware::audio::effect::Processing; using aidl::android::media::audio::common::AudioUuid; @@ -69,7 +70,6 @@ class EffectFactoryHelper { } void CreateEffects() { - ASSERT_NE(mEffectFactory, nullptr); for (const auto& id : mIds) { std::shared_ptr effect; EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect)); @@ -80,6 +80,26 @@ class EffectFactoryHelper { } } + void QueryAndCreateEffects(const AudioUuid& type = EffectNullUuid) { + std::vector ids; + ASSERT_NE(mEffectFactory, nullptr); + + if (type == EffectNullUuid) { + EXPECT_IS_OK(mEffectFactory->queryEffects(std::nullopt, std::nullopt, &ids)); + } else { + EXPECT_IS_OK(mEffectFactory->queryEffects(type, std::nullopt, &ids)); + } + for (const auto& id : ids) { + ASSERT_EQ(id.type, type); + std::shared_ptr effect; + EXPECT_IS_OK(mEffectFactory->createEffect(id.uuid, &effect)); + EXPECT_NE(effect, nullptr) << id.toString(); + if (effect) { + mEffectIdMap[effect] = id; + } + } + } + void CreateEffectsAndExpect( const std::vector>& uuid_status) { ASSERT_NE(mEffectFactory, nullptr); @@ -126,8 +146,10 @@ class EffectFactoryHelper { std::shared_ptr GetFactory() { return mEffectFactory; } const std::vector& GetEffectIds() { return mIds; } - const std::vector& GetCompleteEffectIdList() { return mCompleteIds; } - const std::map, Descriptor::Identity>& GetEffectMap() { + const std::vector& GetCompleteEffectIdList() const { + return mCompleteIds; + } + const std::map, Descriptor::Identity>& GetEffectMap() const { return mEffectIdMap; } void ClearEffectMap() { mEffectIdMap.clear(); } diff --git a/audio/aidl/vts/EffectHelper.h b/audio/aidl/vts/EffectHelper.h new file mode 100644 index 0000000000..c58ed13bfb --- /dev/null +++ b/audio/aidl/vts/EffectHelper.h @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2022 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. + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "AudioHalBinderServiceUtil.h" +#include "EffectFactoryHelper.h" +#include "TestUtils.h" + +using namespace android; +using aidl::android::hardware::audio::effect::CommandId; +using aidl::android::hardware::audio::effect::Descriptor; +using aidl::android::hardware::audio::effect::EffectNullUuid; +using aidl::android::hardware::audio::effect::EffectZeroUuid; +using aidl::android::hardware::audio::effect::IEffect; +using aidl::android::hardware::audio::effect::Parameter; +using aidl::android::hardware::audio::effect::State; +using aidl::android::hardware::common::fmq::SynchronizedReadWrite; +using aidl::android::media::audio::common::AudioChannelLayout; +using aidl::android::media::audio::common::AudioDeviceType; +using aidl::android::media::audio::common::AudioFormatDescription; +using aidl::android::media::audio::common::AudioFormatType; +using aidl::android::media::audio::common::AudioUuid; +using aidl::android::media::audio::common::PcmType; + +const AudioFormatDescription DefaultFormat = { + .type = AudioFormatType::PCM, .pcm = PcmType::INT_16_BIT, .encoding = ""}; + +class EffectHelper { + public: + explicit EffectHelper(const std::string& name) : mFactoryHelper(EffectFactoryHelper(name)) { + mFactoryHelper.ConnectToFactoryService(); + } + + void OpenEffects(const AudioUuid& type = EffectNullUuid) { + auto open = [&](const std::shared_ptr& effect) { + IEffect::OpenEffectReturn ret; + EXPECT_IS_OK(effect->open(mCommon, mSpecific, &ret)); + EffectParam params; + params.statusMQ = std::make_unique(ret.statusMQ); + params.inputMQ = std::make_unique(ret.inputDataMQ); + params.outputMQ = std::make_unique(ret.outputDataMQ); + mEffectParams.push_back(std::move(params)); + }; + EXPECT_NO_FATAL_FAILURE(ForEachEffect(open, type)); + } + + void CloseEffects(const binder_status_t status = EX_NONE) { + auto close = [&](const std::shared_ptr& effect) { + EXPECT_STATUS(status, effect->close()); + }; + + EXPECT_NO_FATAL_FAILURE(ForEachEffect(close)); + } + + void CreateEffects(const int n = 1) { + for (int i = 0; i < n; i++) { + ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateAllEffects()); + } + } + + void CreateEffectsWithUUID(const AudioUuid& type = EffectNullUuid) { + ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateEffects(type)); + } + + void QueryEffects() { ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateAllEffects()); } + + void DestroyEffects(const binder_status_t status = EX_NONE, const int remaining = 0) { + ASSERT_NO_FATAL_FAILURE(mFactoryHelper.DestroyEffects(status, remaining)); + mEffectDescriptors.clear(); + } + + void GetEffectDescriptors() { + auto get = [&](const std::shared_ptr& effect) { + Descriptor desc; + EXPECT_IS_OK(effect->getDescriptor(&desc)); + mEffectDescriptors.push_back(std::move(desc)); + }; + EXPECT_NO_FATAL_FAILURE(ForEachEffect(get)); + } + + void CommandEffects(CommandId command) { + auto close = [&](const std::shared_ptr& effect) { + EXPECT_IS_OK(effect->command(command)); + }; + EXPECT_NO_FATAL_FAILURE(ForEachEffect(close)); + } + + void CommandEffectsExpectStatus(CommandId command, const binder_status_t status) { + auto func = [&](const std::shared_ptr& effect) { + EXPECT_STATUS(status, effect->command(command)); + }; + EXPECT_NO_FATAL_FAILURE(ForEachEffect(func)); + } + + void ExpectState(State expected) { + auto get = [&](const std::shared_ptr& effect) { + State state = State::INIT; + EXPECT_IS_OK(effect->getState(&state)); + EXPECT_EQ(expected, state); + }; + EXPECT_NO_FATAL_FAILURE(ForEachEffect(get)); + } + + void SetParameter() { + auto func = [&](const std::shared_ptr& effect) { + Parameter param; + param.set(mCommon); + EXPECT_IS_OK(effect->setParameter(param)); + }; + EXPECT_NO_FATAL_FAILURE(ForEachEffect(func)); + } + + void VerifyParameters() { + auto func = [&](const std::shared_ptr& effect) { + Parameter paramCommonGet = Parameter(), paramCommonExpect = Parameter(); + Parameter::Id id; + id.set(0); + paramCommonExpect.set(mCommon); + EXPECT_IS_OK(effect->getParameter(id, ¶mCommonGet)); + EXPECT_EQ(paramCommonExpect, paramCommonGet) + << paramCommonExpect.toString() << " vs " << paramCommonGet.toString(); + }; + EXPECT_NO_FATAL_FAILURE(ForEachEffect(func)); + } + + void QueryEffects(const std::optional& in_type, + const std::optional& in_instance, + std::vector* _aidl_return) { + mFactoryHelper.QueryEffects(in_type, in_instance, _aidl_return); + } + + template + void ForEachEffect(Functor functor, const std::optional& type = EffectNullUuid) { + auto effectMap = mFactoryHelper.GetEffectMap(); + for (const auto& it : effectMap) { + SCOPED_TRACE(it.second.toString()); + if (type != EffectNullUuid && it.second.type != type) continue; + functor(it.first); + } + } + + template + void ForEachDescriptor(Functor functor) { + for (size_t i = 0; i < mEffectDescriptors.size(); i++) { + SCOPED_TRACE(mEffectDescriptors[i].toString()); + functor(i, mEffectDescriptors[i]); + } + } + + static const size_t mWriteMQSize = 0x400; + + enum class IO : char { INPUT = 0, OUTPUT = 1, INOUT = 2 }; + + void initParamCommonFormat(IO io = IO::INOUT, + const AudioFormatDescription& format = DefaultFormat) { + if (io == IO::INPUT || io == IO::INOUT) { + mCommon.input.base.format = format; + } + if (io == IO::OUTPUT || io == IO::INOUT) { + mCommon.output.base.format = format; + } + } + + void initParamCommonSampleRate(IO io = IO::INOUT, const int& sampleRate = 48000) { + if (io == IO::INPUT || io == IO::INOUT) { + mCommon.input.base.sampleRate = sampleRate; + } + if (io == IO::OUTPUT || io == IO::INOUT) { + mCommon.output.base.sampleRate = sampleRate; + } + } + + void initParamCommonFrameCount(IO io = IO::INOUT, const long& frameCount = 48000) { + if (io == IO::INPUT || io == IO::INOUT) { + mCommon.input.frameCount = frameCount; + } + if (io == IO::OUTPUT || io == IO::INOUT) { + mCommon.output.frameCount = frameCount; + } + } + void initParamCommon(int session = -1, int ioHandle = -1, int iSampleRate = 48000, + int oSampleRate = 48000, long iFrameCount = 0x100, + long oFrameCount = 0x100) { + mCommon.session = session; + mCommon.ioHandle = ioHandle; + + auto& input = mCommon.input; + auto& output = mCommon.output; + input.base.sampleRate = iSampleRate; + input.base.channelMask = mInputChannelLayout; + input.frameCount = iFrameCount; + output.base.sampleRate = oSampleRate; + output.base.channelMask = mOutputChannelLayout; + output.frameCount = oFrameCount; + inputFrameSize = android::hardware::audio::common::getFrameSizeInBytes( + input.base.format, input.base.channelMask); + outputFrameSize = android::hardware::audio::common::getFrameSizeInBytes( + output.base.format, output.base.channelMask); + } + + void setSpecific(Parameter::Specific& specific) { mSpecific = specific; } + + // usually this function only call once. + void PrepareInputData(size_t s = mWriteMQSize) { + size_t maxInputSize = s; + for (auto& it : mEffectParams) { + auto& mq = it.inputMQ; + EXPECT_NE(nullptr, mq); + EXPECT_TRUE(mq->isValid()); + const size_t bytesToWrite = mq->availableToWrite(); + EXPECT_EQ(inputFrameSize * mCommon.input.frameCount, bytesToWrite); + EXPECT_NE(0UL, bytesToWrite); + EXPECT_TRUE(s <= bytesToWrite); + maxInputSize = std::max(maxInputSize, bytesToWrite); + } + mInputBuffer.resize(maxInputSize); + std::fill(mInputBuffer.begin(), mInputBuffer.end(), 0x5a); + } + + void writeToFmq(size_t s = mWriteMQSize) { + for (auto& it : mEffectParams) { + auto& mq = it.inputMQ; + EXPECT_NE(nullptr, mq); + const size_t bytesToWrite = mq->availableToWrite(); + EXPECT_NE(0Ul, bytesToWrite); + EXPECT_TRUE(s <= bytesToWrite); + EXPECT_TRUE(mq->write(mInputBuffer.data(), s)); + } + } + + void readFromFmq(size_t expectSize = mWriteMQSize) { + for (auto& it : mEffectParams) { + IEffect::Status status{}; + auto& statusMq = it.statusMQ; + EXPECT_NE(nullptr, statusMq); + EXPECT_TRUE(statusMq->readBlocking(&status, 1)); + EXPECT_EQ(STATUS_OK, status.status); + EXPECT_EQ(expectSize, (unsigned)status.fmqByteProduced); + + auto& outputMq = it.outputMQ; + EXPECT_NE(nullptr, outputMq); + EXPECT_EQ(expectSize, outputMq->availableToRead()); + } + } + + void setInputChannelLayout(AudioChannelLayout input) { mInputChannelLayout = input; } + void setOutputChannelLayout(AudioChannelLayout output) { mOutputChannelLayout = output; } + const std::vector& GetCompleteEffectIdList() const { + return mFactoryHelper.GetCompleteEffectIdList(); + } + const std::vector& getDescriptorVec() const { return mEffectDescriptors; } + + private: + EffectFactoryHelper mFactoryHelper; + + AudioChannelLayout mInputChannelLayout = + AudioChannelLayout::make( + AudioChannelLayout::LAYOUT_STEREO); + AudioChannelLayout mOutputChannelLayout = + AudioChannelLayout::make( + AudioChannelLayout::LAYOUT_STEREO); + + Parameter::Common mCommon; + Parameter::Specific mSpecific; + + size_t inputFrameSize, outputFrameSize; + std::vector mInputBuffer; // reuse same buffer for all effects testing + + typedef ::android::AidlMessageQueue< + IEffect::Status, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite> + StatusMQ; + typedef ::android::AidlMessageQueue< + int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite> + DataMQ; + + class EffectParam { + public: + std::unique_ptr statusMQ; + std::unique_ptr inputMQ; + std::unique_ptr outputMQ; + }; + std::vector mEffectParams; + std::vector mEffectDescriptors; +}; diff --git a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp index 454ce29e6c..da8ca37e0c 100644 --- a/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp +++ b/audio/aidl/vts/VtsHalAudioEffectFactoryTargetTest.cpp @@ -37,6 +37,8 @@ using namespace android; using aidl::android::hardware::audio::effect::Descriptor; +using aidl::android::hardware::audio::effect::EffectNullUuid; +using aidl::android::hardware::audio::effect::EffectZeroUuid; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Processing; using aidl::android::media::audio::common::AudioUuid; @@ -50,17 +52,8 @@ class EffectFactoryTest : public testing::TestWithParam { EffectFactoryHelper mFactory = EffectFactoryHelper(GetParam()); - // TODO: these UUID can get from config file - // ec7178ec-e5e1-4432-a3f4-4657e6795210 - const AudioUuid nullUuid = {static_cast(0xec7178ec), - 0xe5e1, - 0x4432, - 0xa3f4, - {0x46, 0x57, 0xe6, 0x79, 0x52, 0x10}}; - const AudioUuid zeroUuid = { - static_cast(0x0), 0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; - const Descriptor::Identity nullDesc = {.uuid = nullUuid}; - const Descriptor::Identity zeroDesc = {.uuid = zeroUuid}; + const Descriptor::Identity nullDesc = {.uuid = EffectNullUuid}; + const Descriptor::Identity zeroDesc = {.uuid = EffectZeroUuid}; }; TEST_P(EffectFactoryTest, SetupAndTearDown) { @@ -82,20 +75,20 @@ TEST_P(EffectFactoryTest, DescriptorUUIDNotNull) { mFactory.QueryEffects(std::nullopt, std::nullopt, &descriptors); // TODO: Factory eventually need to return the full list of MUST supported AOSP effects. for (auto& desc : descriptors) { - EXPECT_NE(desc.type, zeroUuid); - EXPECT_NE(desc.uuid, zeroUuid); + EXPECT_NE(desc.type, EffectNullUuid); + EXPECT_NE(desc.uuid, EffectNullUuid); } } TEST_P(EffectFactoryTest, QueriedDescriptorNotExistType) { std::vector descriptors; - mFactory.QueryEffects(nullUuid, std::nullopt, &descriptors); + mFactory.QueryEffects(EffectNullUuid, std::nullopt, &descriptors); EXPECT_EQ(descriptors.size(), 0UL); } TEST_P(EffectFactoryTest, QueriedDescriptorNotExistInstance) { std::vector descriptors; - mFactory.QueryEffects(std::nullopt, nullUuid, &descriptors); + mFactory.QueryEffects(std::nullopt, EffectNullUuid, &descriptors); EXPECT_EQ(descriptors.size(), 0UL); } diff --git a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp index 23b20bd1fb..7ed1f01dfb 100644 --- a/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp +++ b/audio/aidl/vts/VtsHalAudioEffectTargetTest.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ +#define LOG_TAG "VtsHalAudioEffectTargetTest" + #include #include #include #include #include -#define LOG_TAG "VtsHalAudioEffect" - #include #include #include @@ -30,13 +30,13 @@ #include #include +#include #include #include -#include #include #include "AudioHalBinderServiceUtil.h" -#include "EffectFactoryHelper.h" +#include "EffectHelper.h" #include "TestUtils.h" using namespace android; @@ -49,201 +49,72 @@ using aidl::android::hardware::audio::effect::IEffect; using aidl::android::hardware::audio::effect::IFactory; using aidl::android::hardware::audio::effect::Parameter; using aidl::android::hardware::audio::effect::State; -using aidl::android::media::audio::common::AudioChannelLayout; using aidl::android::media::audio::common::AudioDeviceType; -class AudioEffect : public testing::TestWithParam { +class AudioEffectTest : public testing::TestWithParam, public EffectHelper { public: + AudioEffectTest() : EffectHelper(GetParam()) {} + void SetUp() override { - ASSERT_NO_FATAL_FAILURE(mFactoryHelper.ConnectToFactoryService()); CreateEffects(); + initParamCommonFormat(); initParamCommon(); - initParamSpecific(); + // initParamSpecific(); } void TearDown() override { CloseEffects(); DestroyEffects(); } - - void OpenEffects() { - auto open = [&](const std::shared_ptr& effect) { - IEffect::OpenEffectReturn ret; - EXPECT_IS_OK(effect->open(mCommon, mSpecific, &ret)); - }; - EXPECT_NO_FATAL_FAILURE(ForEachEffect(open)); - } - - void CloseEffects(const binder_status_t status = EX_NONE) { - auto close = [&](const std::shared_ptr& effect) { - EXPECT_STATUS(status, effect->close()); - }; - - EXPECT_NO_FATAL_FAILURE(ForEachEffect(close)); - } - - void CreateEffects(const int n = 1) { - for (int i = 0; i < n; i++) { - ASSERT_NO_FATAL_FAILURE(mFactoryHelper.QueryAndCreateAllEffects()); - } - } - - void DestroyEffects(const binder_status_t status = EX_NONE, const int remaining = 0) { - ASSERT_NO_FATAL_FAILURE(mFactoryHelper.DestroyEffects(status, remaining)); - } - - void GetEffectDescriptors() { - auto get = [](const std::shared_ptr& effect) { - Descriptor desc; - EXPECT_IS_OK(effect->getDescriptor(&desc)); - }; - EXPECT_NO_FATAL_FAILURE(ForEachEffect(get)); - } - - void CommandEffects(CommandId command) { - auto close = [&](const std::shared_ptr& effect) { - EXPECT_IS_OK(effect->command(command)); - }; - EXPECT_NO_FATAL_FAILURE(ForEachEffect(close)); - } - - void CommandEffectsExpectStatus(CommandId command, const binder_status_t status) { - auto func = [&](const std::shared_ptr& effect) { - EXPECT_STATUS(status, effect->command(command)); - }; - EXPECT_NO_FATAL_FAILURE(ForEachEffect(func)); - } - - void ExpectState(State expected) { - auto get = [&](const std::shared_ptr& effect) { - State state = State::INIT; - EXPECT_IS_OK(effect->getState(&state)); - EXPECT_EQ(expected, state); - }; - EXPECT_NO_FATAL_FAILURE(ForEachEffect(get)); - } - - void SetParameter() { - auto func = [&](const std::shared_ptr& effect) { - Parameter param; - param.set(mCommon); - EXPECT_IS_OK(effect->setParameter(param)); - }; - EXPECT_NO_FATAL_FAILURE(ForEachEffect(func)); - } - - void VerifyParameters() { - auto func = [&](const std::shared_ptr& effect) { - Parameter paramCommonGet = Parameter(), paramCommonExpect = Parameter(); - Parameter::Id id; - id.set(0); - paramCommonExpect.set(mCommon); - EXPECT_IS_OK(effect->getParameter(id, ¶mCommonGet)); - EXPECT_EQ(paramCommonExpect, paramCommonGet) - << paramCommonExpect.toString() << " vs " << paramCommonGet.toString(); - }; - EXPECT_NO_FATAL_FAILURE(ForEachEffect(func)); - } - - template - void ForEachEffect(Functor functor) { - auto effectMap = mFactoryHelper.GetEffectMap(); - for (const auto& it : effectMap) { - SCOPED_TRACE(it.second.toString()); - functor(it.first); - } - } - - void initParamCommon(int session = -1, int ioHandle = -1, - AudioDeviceType deviceType = AudioDeviceType::NONE, - int iSampleRate = 48000, int oSampleRate = 48000, long iFrameCount = 0x100, - long oFrameCount = 0x100) { - mCommon.session = session; - mCommon.ioHandle = ioHandle; - mCommon.device.type = deviceType; - mCommon.input.base.sampleRate = iSampleRate; - mCommon.input.base.channelMask = mInputChannelLayout; - mCommon.input.frameCount = iFrameCount; - mCommon.output.base.sampleRate = oSampleRate; - mCommon.output.base.channelMask = mOutputChannelLayout; - mCommon.output.frameCount = oFrameCount; - } - - void initParamSpecific(Parameter::Specific::Tag tag = Parameter::Specific::equalizer) { - switch (tag) { - case Parameter::Specific::equalizer: - mSpecific.set(); - break; - default: - return; - } - } - - void setInputChannelLayout(AudioChannelLayout input) { mInputChannelLayout = input; } - void setOutputChannelLayout(AudioChannelLayout output) { mOutputChannelLayout = output; } - - EffectFactoryHelper mFactoryHelper = EffectFactoryHelper(GetParam()); - - private: - AudioChannelLayout mInputChannelLayout = - AudioChannelLayout::make( - AudioChannelLayout::LAYOUT_STEREO); - AudioChannelLayout mOutputChannelLayout = - AudioChannelLayout::make( - AudioChannelLayout::LAYOUT_STEREO); - - Parameter::Common mCommon; - Parameter::Specific mSpecific; - static IEffect::OpenEffectReturn mOpenReturn; }; -TEST_P(AudioEffect, OpenEffectTest) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); +TEST_P(AudioEffectTest, OpenEffectTest) { + OpenEffects(); } -TEST_P(AudioEffect, OpenAndCloseEffect) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); +TEST_P(AudioEffectTest, OpenAndCloseEffect) { + OpenEffects(); + CloseEffects(); } -TEST_P(AudioEffect, CloseUnopenedEffectTest) { - EXPECT_NO_FATAL_FAILURE(CloseEffects()); +TEST_P(AudioEffectTest, CloseUnopenedEffectTest) { + CloseEffects(); } -TEST_P(AudioEffect, DoubleOpenCloseEffects) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); +TEST_P(AudioEffectTest, DoubleOpenCloseEffects) { + OpenEffects(); + CloseEffects(); + OpenEffects(); + CloseEffects(); - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + OpenEffects(); + OpenEffects(); + CloseEffects(); - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + OpenEffects(); + CloseEffects(); + CloseEffects(); } -TEST_P(AudioEffect, GetDescriptors) { - EXPECT_NO_FATAL_FAILURE(GetEffectDescriptors()); +TEST_P(AudioEffectTest, GetDescriptors) { + GetEffectDescriptors(); } -TEST_P(AudioEffect, DescriptorIdExistAndUnique) { +TEST_P(AudioEffectTest, DescriptorIdExistAndUnique) { auto checker = [&](const std::shared_ptr& effect) { Descriptor desc; std::vector idList; EXPECT_IS_OK(effect->getDescriptor(&desc)); - mFactoryHelper.QueryEffects(desc.common.id.type, desc.common.id.uuid, &idList); + QueryEffects(desc.common.id.type, desc.common.id.uuid, &idList); EXPECT_EQ(idList.size(), 1UL); }; - EXPECT_NO_FATAL_FAILURE(ForEachEffect(checker)); + ForEachEffect(checker); // Check unique with a set auto stringHash = [](const Descriptor::Identity& id) { return std::hash()(id.toString()); }; - auto vec = mFactoryHelper.GetCompleteEffectIdList(); + auto vec = GetCompleteEffectIdList(); std::unordered_set idSet(0, stringHash); for (auto it : vec) { EXPECT_EQ(idSet.count(it), 0UL); @@ -253,218 +124,235 @@ TEST_P(AudioEffect, DescriptorIdExistAndUnique) { /// State testing. // An effect instance is in INIT state by default after it was created. -TEST_P(AudioEffect, InitStateAfterCreation) { +TEST_P(AudioEffectTest, InitStateAfterCreation) { ExpectState(State::INIT); } // An effect instance transfer to INIT state after it was open successfully with IEffect.open(). -TEST_P(AudioEffect, IdleStateAfterOpen) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); +TEST_P(AudioEffectTest, IdleStateAfterOpen) { + OpenEffects(); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + CloseEffects(); } // An effect instance is in PROCESSING state after it receive an START command. -TEST_P(AudioEffect, ProcessingStateAfterStart) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START)); +TEST_P(AudioEffectTest, ProcessingStateAfterStart) { + OpenEffects(); + CommandEffects(CommandId::START); ExpectState(State::PROCESSING); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP)); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + CommandEffects(CommandId::STOP); + CloseEffects(); } // An effect instance transfer to IDLE state after Command.Id.STOP in PROCESSING state. -TEST_P(AudioEffect, IdleStateAfterStop) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START)); +TEST_P(AudioEffectTest, IdleStateAfterStop) { + OpenEffects(); + CommandEffects(CommandId::START); ExpectState(State::PROCESSING); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP)); + CommandEffects(CommandId::STOP); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + CloseEffects(); } // An effect instance transfer to IDLE state after Command.Id.RESET in PROCESSING state. -TEST_P(AudioEffect, IdleStateAfterReset) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START)); +TEST_P(AudioEffectTest, IdleStateAfterReset) { + OpenEffects(); + CommandEffects(CommandId::START); ExpectState(State::PROCESSING); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET)); + CommandEffects(CommandId::RESET); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + CloseEffects(); } // An effect instance transfer to INIT if instance receive a close() call. -TEST_P(AudioEffect, InitStateAfterClose) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START)); +TEST_P(AudioEffectTest, InitStateAfterClose) { + OpenEffects(); + CommandEffects(CommandId::START); ExpectState(State::PROCESSING); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP)); + CommandEffects(CommandId::STOP); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + CloseEffects(); ExpectState(State::INIT); } // An effect instance shouldn't accept any command before open. -TEST_P(AudioEffect, NoCommandAcceptedBeforeOpen) { +TEST_P(AudioEffectTest, NoCommandAcceptedBeforeOpen) { ExpectState(State::INIT); - EXPECT_NO_FATAL_FAILURE(CommandEffectsExpectStatus(CommandId::START, EX_ILLEGAL_STATE)); - EXPECT_NO_FATAL_FAILURE(CommandEffectsExpectStatus(CommandId::STOP, EX_ILLEGAL_STATE)); - EXPECT_NO_FATAL_FAILURE(CommandEffectsExpectStatus(CommandId::RESET, EX_ILLEGAL_STATE)); + CommandEffectsExpectStatus(CommandId::START, EX_ILLEGAL_STATE); + CommandEffectsExpectStatus(CommandId::STOP, EX_ILLEGAL_STATE); + CommandEffectsExpectStatus(CommandId::RESET, EX_ILLEGAL_STATE); ExpectState(State::INIT); } // No-op when receive STOP command in IDLE state. -TEST_P(AudioEffect, StopCommandInIdleStateNoOp) { +TEST_P(AudioEffectTest, StopCommandInIdleStateNoOp) { ExpectState(State::INIT); - EXPECT_NO_FATAL_FAILURE(OpenEffects()); + OpenEffects(); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP)); + CommandEffects(CommandId::STOP); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + CloseEffects(); } // No-op when receive STOP command in IDLE state. -TEST_P(AudioEffect, ResetCommandInIdleStateNoOp) { +TEST_P(AudioEffectTest, ResetCommandInIdleStateNoOp) { ExpectState(State::INIT); - EXPECT_NO_FATAL_FAILURE(OpenEffects()); + OpenEffects(); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET)); + CommandEffects(CommandId::RESET); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + CloseEffects(); } // Repeat START and STOP command. -TEST_P(AudioEffect, RepeatStartAndStop) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START)); +TEST_P(AudioEffectTest, RepeatStartAndStop) { + OpenEffects(); + CommandEffects(CommandId::START); ExpectState(State::PROCESSING); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP)); + CommandEffects(CommandId::STOP); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START)); + CommandEffects(CommandId::START); ExpectState(State::PROCESSING); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP)); + CommandEffects(CommandId::STOP); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + CloseEffects(); } // Repeat START and RESET command. -TEST_P(AudioEffect, RepeatStartAndReset) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START)); +TEST_P(AudioEffectTest, RepeatStartAndReset) { + OpenEffects(); + CommandEffects(CommandId::START); ExpectState(State::PROCESSING); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET)); + CommandEffects(CommandId::RESET); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START)); + CommandEffects(CommandId::START); ExpectState(State::PROCESSING); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET)); + CommandEffects(CommandId::RESET); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + CloseEffects(); } // Repeat START and STOP command, try to close at PROCESSING state. -TEST_P(AudioEffect, CloseProcessingStateEffects) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START)); +TEST_P(AudioEffectTest, CloseProcessingStateEffects) { + OpenEffects(); + CommandEffects(CommandId::START); ExpectState(State::PROCESSING); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP)); + CommandEffects(CommandId::STOP); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START)); + CommandEffects(CommandId::START); ExpectState(State::PROCESSING); - EXPECT_NO_FATAL_FAILURE(CloseEffects(EX_ILLEGAL_STATE)); + CloseEffects(EX_ILLEGAL_STATE); // cleanup - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP)); + CommandEffects(CommandId::STOP); ExpectState(State::IDLE); } // Expect EX_ILLEGAL_STATE if the effect instance is not in a proper state to be destroyed. -TEST_P(AudioEffect, DestroyOpenEffects) { +TEST_P(AudioEffectTest, DestroyOpenEffects) { // cleanup all effects. - EXPECT_NO_FATAL_FAILURE(CloseEffects()); - ASSERT_NO_FATAL_FAILURE(DestroyEffects()); + CloseEffects(); + DestroyEffects(); // open effects, destroy without close, expect to get EX_ILLEGAL_STATE status. - EXPECT_NO_FATAL_FAILURE(CreateEffects()); - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(DestroyEffects(EX_ILLEGAL_STATE, 1)); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + CreateEffects(); + OpenEffects(); + DestroyEffects(EX_ILLEGAL_STATE, 1); + CloseEffects(); } /// Parameter testing. // Verify parameters pass in open can be successfully get. -TEST_P(AudioEffect, VerifyParametersAfterOpen) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(VerifyParameters()); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); +TEST_P(AudioEffectTest, VerifyParametersAfterOpen) { + OpenEffects(); + VerifyParameters(); + CloseEffects(); } // Verify parameters pass in set can be successfully get. -TEST_P(AudioEffect, SetAndGetParameter) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(VerifyParameters()); - initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */, - 44100 /* iSampleRate */, 44100 /* oSampleRate */); - EXPECT_NO_FATAL_FAILURE(SetParameter()); - EXPECT_NO_FATAL_FAILURE(VerifyParameters()); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); +TEST_P(AudioEffectTest, SetAndGetParameter) { + OpenEffects(); + VerifyParameters(); + initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, + 44100 /* oSampleRate */); + SetParameter(); + VerifyParameters(); + CloseEffects(); } // Verify parameters pass in set can be successfully get. -TEST_P(AudioEffect, SetAndGetParameterInProcessing) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(VerifyParameters()); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START)); +TEST_P(AudioEffectTest, SetAndGetParameterInProcessing) { + OpenEffects(); + VerifyParameters(); + CommandEffects(CommandId::START); ExpectState(State::PROCESSING); - initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */, - 44100 /* iSampleRate */, 44100 /* oSampleRate */); - EXPECT_NO_FATAL_FAILURE(SetParameter()); - EXPECT_NO_FATAL_FAILURE(VerifyParameters()); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP)); + initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, + 44100 /* oSampleRate */); + SetParameter(); + VerifyParameters(); + CommandEffects(CommandId::STOP); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + CloseEffects(); } // Parameters kept after reset. -TEST_P(AudioEffect, ResetAndVerifyParameter) { - EXPECT_NO_FATAL_FAILURE(OpenEffects()); - EXPECT_NO_FATAL_FAILURE(VerifyParameters()); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START)); +TEST_P(AudioEffectTest, ResetAndVerifyParameter) { + OpenEffects(); + VerifyParameters(); + CommandEffects(CommandId::START); ExpectState(State::PROCESSING); - initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */, - 44100 /* iSampleRate */, 44100 /* oSampleRate */); - EXPECT_NO_FATAL_FAILURE(SetParameter()); - EXPECT_NO_FATAL_FAILURE(VerifyParameters()); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::RESET)); + initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, + 44100 /* oSampleRate */); + SetParameter(); + VerifyParameters(); + CommandEffects(CommandId::RESET); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(VerifyParameters()); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + VerifyParameters(); + CloseEffects(); } // Multiple instances of same implementation running. -TEST_P(AudioEffect, MultipleInstancesRunning) { - EXPECT_NO_FATAL_FAILURE(CreateEffects(3)); +TEST_P(AudioEffectTest, MultipleInstancesRunning) { + CreateEffects(3); ExpectState(State::INIT); - EXPECT_NO_FATAL_FAILURE(OpenEffects()); + OpenEffects(); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::START)); + CommandEffects(CommandId::START); ExpectState(State::PROCESSING); - initParamCommon(1 /* session */, 1 /* ioHandle */, AudioDeviceType::IN_DEFAULT /* deviceType */, - 44100 /* iSampleRate */, 44100 /* oSampleRate */); - EXPECT_NO_FATAL_FAILURE(SetParameter()); - EXPECT_NO_FATAL_FAILURE(VerifyParameters()); - EXPECT_NO_FATAL_FAILURE(CommandEffects(CommandId::STOP)); + initParamCommon(1 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, + 44100 /* oSampleRate */); + SetParameter(); + VerifyParameters(); + CommandEffects(CommandId::STOP); ExpectState(State::IDLE); - EXPECT_NO_FATAL_FAILURE(VerifyParameters()); - EXPECT_NO_FATAL_FAILURE(CloseEffects()); + VerifyParameters(); + CloseEffects(); } -INSTANTIATE_TEST_SUITE_P(AudioEffectTest, AudioEffect, +// Send data to effects and expect it to consume by check statusMQ. +TEST_P(AudioEffectTest, ExpectEffectsToConsumeDataInMQ) { + OpenEffects(); + PrepareInputData(mWriteMQSize); + + CommandEffects(CommandId::START); + writeToFmq(mWriteMQSize); + readFromFmq(mWriteMQSize); + + ExpectState(State::PROCESSING); + CommandEffects(CommandId::STOP); + // cleanup + CommandEffects(CommandId::STOP); + ExpectState(State::IDLE); + CloseEffects(); +} + +INSTANTIATE_TEST_SUITE_P(AudioEffectTestTest, AudioEffectTest, testing::ValuesIn(android::getAidlHalInstanceNames(IFactory::descriptor)), android::PrintInstanceNameToString); -GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffect); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffectTest); int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ABinderProcess_setThreadPoolMaxThreadCount(1); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); -} \ No newline at end of file +} diff --git a/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp new file mode 100644 index 0000000000..3b9699b5aa --- /dev/null +++ b/audio/aidl/vts/VtsHalEqualizerTargetTest.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2022 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. + */ + +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "VtsHalEqualizerTest" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "AudioHalBinderServiceUtil.h" +#include "EffectHelper.h" +#include "TestUtils.h" +#include "effect-impl/EffectUUID.h" + +using namespace android; + +using aidl::android::hardware::audio::effect::Capability; +using aidl::android::hardware::audio::effect::Descriptor; +using aidl::android::hardware::audio::effect::EffectNullUuid; +using aidl::android::hardware::audio::effect::Equalizer; +using aidl::android::hardware::audio::effect::EqualizerTypeUUID; +using aidl::android::hardware::audio::effect::IEffect; +using aidl::android::hardware::audio::effect::IFactory; +using aidl::android::hardware::audio::effect::Parameter; + +/** + * Here we focus on specific parameter checking, general IEffect interfaces testing performed in + * VtsAudioEfectTargetTest. + */ +using EqualizerParamTestParam = std::tuple; + +class EqualizerParamTest : public ::testing::TestWithParam, + public EffectHelper { + public: + EqualizerParamTest() + : EffectHelper(android::getAidlHalInstanceNames(IFactory::descriptor)[0]), + mParamPresetIndex(std::get<0 /* kPresetIndexRange */>(GetParam())), + mParamBandIndex(std::get<1 /* kBandIndexRange */>(GetParam())), + mParamBandLevel(std::get<2 /* kBandLevelRange */>(GetParam())) {} + + void SetUp() override { + CreateEffectsWithUUID(EqualizerTypeUUID); + initParamCommonFormat(); + initParamCommon(); + initParamSpecific(); + OpenEffects(EqualizerTypeUUID); + SCOPED_TRACE(testing::Message() << "preset: " << mParamPresetIndex << " bandIdx " + << mParamBandIndex << " level " << mParamBandLevel); + } + + void TearDown() override { + CloseEffects(); + DestroyEffects(); + CleanUp(); + } + + const int mParamPresetIndex; + const int mParamBandIndex; + const int mParamBandLevel; + + void SetAndGetEqualizerParameters() { + auto functor = [&](const std::shared_ptr& effect) { + for (auto& it : mTags) { + auto& tag = it.first; + auto& eq = it.second; + + // validate parameter + Descriptor desc; + ASSERT_STATUS(EX_NONE, effect->getDescriptor(&desc)); + const bool valid = isTagInRange(it.first, it.second, desc); + const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT; + + // set + Parameter expectParam; + Parameter::Specific specific; + specific.set(*eq.get()); + expectParam.set(specific); + EXPECT_STATUS(expected, effect->setParameter(expectParam)) + << expectParam.toString(); + + // get + if (expected == EX_NONE) { + Parameter getParam; + Parameter::Specific::Id id; + id.set(tag); + // if set success, then get should match + EXPECT_STATUS(expected, effect->getParameter(id, &getParam)); + EXPECT_EQ(expectParam, getParam) << "\n" + << expectParam.toString() << "\n" + << getParam.toString(); + } + } + }; + EXPECT_NO_FATAL_FAILURE(ForEachEffect(functor)); + } + + void addPresetParam(int preset) { + Equalizer eq; + eq.set(preset); + mTags.push_back({Equalizer::preset, std::make_unique(std::move(eq))}); + } + + void addBandLevelsParam(std::vector& bandLevels) { + Equalizer eq; + eq.set(bandLevels); + mTags.push_back({Equalizer::bandLevels, std::make_unique(std::move(eq))}); + } + + bool isTagInRange(const Equalizer::Tag& tag, const std::unique_ptr& eq, + const Descriptor& desc) const { + std::cout << "xxx" << toString(tag) << " " << desc.toString(); + const Equalizer::Capability& eqCap = desc.capability.get(); + switch (tag) { + case Equalizer::preset: { + int index = eq->get(); + return isPresetIndexInRange(eqCap, index); + } + case Equalizer::bandLevels: { + auto& bandLevel = eq->get(); + return isBandIndexInRange(eqCap, bandLevel); + } + default: + return false; + } + return false; + } + + bool isPresetIndexInRange(const Equalizer::Capability& cap, int idx) const { + const auto [min, max] = + std::minmax_element(cap.presets.begin(), cap.presets.end(), + [](const auto& a, const auto& b) { return a.index < b.index; }); + return idx >= min->index && idx <= max->index; + } + + bool isBandIndexInRange(const Equalizer::Capability& cap, + const std::vector& bandLevel) const { + for (auto& it : bandLevel) { + if (!isBandIndexInRange(cap, it.index)) return false; + } + return true; + } + + bool isBandIndexInRange(const Equalizer::Capability& cap, int idx) const { + const auto [min, max] = + std::minmax_element(cap.bandFrequencies.begin(), cap.bandFrequencies.end(), + [](const auto& a, const auto& b) { return a.index < b.index; }); + return idx >= min->index && idx <= max->index; + } + + private: + Equalizer::VendorExtension mVendorExtension; + std::vector>> mTags; + + bool validCapabilityTag(Capability& cap) { return cap.getTag() == Capability::equalizer; } + + void initParamSpecific() { + Equalizer eq; + eq.set(0); + Parameter::Specific specific; + specific.set(eq); + setSpecific(specific); + } + + void CleanUp() { mTags.clear(); } +}; + +TEST_P(EqualizerParamTest, SetAndGetPreset) { + EXPECT_NO_FATAL_FAILURE(addPresetParam(mParamPresetIndex)); + SetAndGetEqualizerParameters(); +} + +TEST_P(EqualizerParamTest, SetAndGetSingleBand) { + Equalizer::BandLevel bandLevel = {mParamBandIndex, mParamBandLevel}; + std::vector bandLevels; + bandLevels.push_back(bandLevel); + EXPECT_NO_FATAL_FAILURE(addBandLevelsParam(bandLevels)); + SetAndGetEqualizerParameters(); +} + +/** + Testing preset index range with [-10, 10], assuming the min/max preset index supported by +effect is in this range. + This range is verified with IEffect.getDescriptor(): for any index supported vts expect EX_NONE +from IEffect.setParameter(), otherwise expect EX_ILLEGAL_ARGUMENT. + */ +constexpr std::pair kPresetIndexRange = {-1, 10}; // valid range [0, 9] +constexpr std::pair kBandIndexRange = {-1, 5}; // valid range [0, 4] +constexpr std::pair kBandLevelRange = {-5, 5}; // needs update with implementation + +INSTANTIATE_TEST_SUITE_P( + EqualizerTest, EqualizerParamTest, + ::testing::Combine(testing::Range(kPresetIndexRange.first, kPresetIndexRange.second), + testing::Range(kBandIndexRange.first, kBandIndexRange.second), + testing::Range(kBandLevelRange.first, kBandLevelRange.second))); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EqualizerTest); + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + return RUN_ALL_TESTS(); +}