From 5bfe81fb7975f557a1db8313d1ad1f427b14d1c4 Mon Sep 17 00:00:00 2001 From: Shunkai Yao Date: Thu, 5 Jan 2023 23:38:39 +0000 Subject: [PATCH 1/2] DynamicsProcessing: update AIDL interface definition Enum change aidl_api was automatically generated. Bug: 258124419 Test: atest VtsHalDynamicsProcessingTargetTest Change-Id: I942c20124190f91efdebbf2fd8d66692699a65cd --- .../hardware/audio/effect/CommandId.aidl | 20 +-- .../hardware/audio/effect/Downmix.aidl | 4 +- .../audio/effect/DynamicsProcessing.aidl | 47 +++--- .../audio/effect/EnvironmentalReverb.aidl | 6 +- .../audio/effect/HapticGenerator.aidl | 6 +- .../audio/effect/NoiseSuppression.aidl | 6 +- .../hardware/audio/effect/PresetReverb.aidl | 14 +- .../android/hardware/audio/effect/State.aidl | 6 +- .../hardware/audio/effect/Visualizer.aidl | 4 +- .../audio/effect/DynamicsProcessing.aidl | 151 +++++++++++------- 10 files changed, 150 insertions(+), 114 deletions(-) diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl index 79299eeef5..86b69fa2f2 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/CommandId.aidl @@ -37,14 +37,14 @@ enum CommandId { START = 0, STOP = 1, RESET = 2, - VENDOR_COMMAND_0 = 256, - VENDOR_COMMAND_1 = 257, - VENDOR_COMMAND_2 = 258, - VENDOR_COMMAND_3 = 259, - VENDOR_COMMAND_4 = 260, - VENDOR_COMMAND_5 = 261, - VENDOR_COMMAND_6 = 262, - VENDOR_COMMAND_7 = 263, - VENDOR_COMMAND_8 = 264, - VENDOR_COMMAND_9 = 265, + VENDOR_COMMAND_0 = 0x100, + VENDOR_COMMAND_1, + VENDOR_COMMAND_2, + VENDOR_COMMAND_3, + VENDOR_COMMAND_4, + VENDOR_COMMAND_5, + VENDOR_COMMAND_6, + VENDOR_COMMAND_7, + VENDOR_COMMAND_8, + VENDOR_COMMAND_9, } diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Downmix.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Downmix.aidl index 76f8ce50fe..402441dbab 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Downmix.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Downmix.aidl @@ -47,7 +47,7 @@ union Downmix { } @VintfStability enum Type { - STRIP = 0, - FOLD = 1, + STRIP, + FOLD, } } diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/DynamicsProcessing.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/DynamicsProcessing.aidl index ed4dc80dc7..8e5b719f0b 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/DynamicsProcessing.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/DynamicsProcessing.aidl @@ -36,14 +36,14 @@ package android.hardware.audio.effect; union DynamicsProcessing { android.hardware.audio.effect.VendorExtension vendorExtension; android.hardware.audio.effect.DynamicsProcessing.EngineArchitecture engineArchitecture; - android.hardware.audio.effect.DynamicsProcessing.BandChannelConfig preEq; - android.hardware.audio.effect.DynamicsProcessing.BandChannelConfig postEq; - android.hardware.audio.effect.DynamicsProcessing.EqBandConfig preEqBand; - android.hardware.audio.effect.DynamicsProcessing.EqBandConfig postEqBand; - android.hardware.audio.effect.DynamicsProcessing.BandChannelConfig mbc; - android.hardware.audio.effect.DynamicsProcessing.MbcBandConfig mbcBand; - android.hardware.audio.effect.DynamicsProcessing.LimiterConfig limiter; - float inputGainDb; + android.hardware.audio.effect.DynamicsProcessing.ChannelConfig[] preEq; + android.hardware.audio.effect.DynamicsProcessing.ChannelConfig[] postEq; + android.hardware.audio.effect.DynamicsProcessing.EqBandConfig[] preEqBand; + android.hardware.audio.effect.DynamicsProcessing.EqBandConfig[] postEqBand; + android.hardware.audio.effect.DynamicsProcessing.ChannelConfig[] mbc; + android.hardware.audio.effect.DynamicsProcessing.MbcBandConfig[] mbcBand; + android.hardware.audio.effect.DynamicsProcessing.LimiterConfig[] limiter; + android.hardware.audio.effect.DynamicsProcessing.InputGain[] inputGain; @VintfStability union Id { int vendorExtensionTag; @@ -52,37 +52,39 @@ union DynamicsProcessing { @VintfStability parcelable Capability { ParcelableHolder extension; + float minCutOffFreq; + float maxCutOffFreq; } enum ResolutionPreference { - FAVOR_FREQUENCY_RESOLUTION = 0, - FAVOR_TIME_RESOLUTION = 1, + FAVOR_FREQUENCY_RESOLUTION, + FAVOR_TIME_RESOLUTION, } @VintfStability - parcelable BandEnablement { + parcelable StageEnablement { boolean inUse; int bandCount; } @VintfStability parcelable EngineArchitecture { android.hardware.audio.effect.DynamicsProcessing.ResolutionPreference resolutionPreference = android.hardware.audio.effect.DynamicsProcessing.ResolutionPreference.FAVOR_FREQUENCY_RESOLUTION; - float preferredFrameDurationMs; - android.hardware.audio.effect.DynamicsProcessing.BandEnablement preEqBand; - android.hardware.audio.effect.DynamicsProcessing.BandEnablement postEqBand; - android.hardware.audio.effect.DynamicsProcessing.BandEnablement mbcBand; + float preferredProcessingDurationMs; + android.hardware.audio.effect.DynamicsProcessing.StageEnablement preEqStage; + android.hardware.audio.effect.DynamicsProcessing.StageEnablement postEqStage; + android.hardware.audio.effect.DynamicsProcessing.StageEnablement mbcStage; boolean limiterInUse; } @VintfStability - parcelable BandChannelConfig { + parcelable ChannelConfig { int channel; - android.hardware.audio.effect.DynamicsProcessing.BandEnablement enablement; + boolean enable; } @VintfStability parcelable EqBandConfig { int channel; int band; boolean enable; - float cutoffFrequency; - float gain; + float cutoffFrequencyHz; + float gainDb; } @VintfStability parcelable MbcBandConfig { @@ -90,7 +92,6 @@ union DynamicsProcessing { int band; boolean enable; float cutoffFrequencyHz; - float gainDb; float attackTimeMs; float releaseTimeMs; float ratio; @@ -105,7 +106,6 @@ union DynamicsProcessing { parcelable LimiterConfig { int channel; boolean enable; - boolean inUse; int linkGroup; float attackTimeMs; float releaseTimeMs; @@ -113,4 +113,9 @@ union DynamicsProcessing { float thresholdDb; float postGainDb; } + @VintfStability + parcelable InputGain { + int channel; + float gainDb; + } } diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/EnvironmentalReverb.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/EnvironmentalReverb.aidl index 0e61932c96..7f34f334dd 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/EnvironmentalReverb.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/EnvironmentalReverb.aidl @@ -44,15 +44,15 @@ union EnvironmentalReverb { int diffusionPm; int densityPm; boolean bypass; - const int MIN_ROOM_LEVEL_MB = -6000; + const int MIN_ROOM_LEVEL_MB = (-6000); const int MAX_ROOM_LEVEL_MB = 0; - const int MIN_ROOM_HF_LEVEL_MB = -4000; + const int MIN_ROOM_HF_LEVEL_MB = (-4000); const int MAX_ROOM_HF_LEVEL_MB = 0; const int MIN_DECAY_TIME_MS = 100; const int MAX_DECAY_TIME_MS = 20000; const int MIN_DECAY_HF_RATIO_PM = 100; const int MAX_DECAY_HF_RATIO_PM = 1000; - const int MIN_LEVEL_MB = -6000; + const int MIN_LEVEL_MB = (-6000); const int MAX_LEVEL_MB = 0; const int MIN_DELAY_MS = 0; const int MAX_DELAY_MS = 65; diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl index 959594b988..20f7e024f3 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/HapticGenerator.aidl @@ -48,9 +48,9 @@ union HapticGenerator { } @Backing(type="int") @VintfStability enum VibratorScale { - MUTE = -100, - VERY_LOW = -2, - LOW = -1, + MUTE = (-100), + VERY_LOW = (-2), + LOW = (-1), NONE = 0, HIGH = 1, VERY_HIGH = 2, diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/NoiseSuppression.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/NoiseSuppression.aidl index 223d95a7b5..397f89744d 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/NoiseSuppression.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/NoiseSuppression.aidl @@ -47,8 +47,8 @@ union NoiseSuppression { } @Backing(type="int") @VintfStability enum Level { - LOW = 0, - MEDIUM = 1, - HIGH = 2, + LOW, + MEDIUM, + HIGH, } } diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/PresetReverb.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/PresetReverb.aidl index 24a9ce1ef2..4651742fac 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/PresetReverb.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/PresetReverb.aidl @@ -38,13 +38,13 @@ union PresetReverb { android.hardware.audio.effect.PresetReverb.Presets preset; @Backing(type="int") @VintfStability enum Presets { - NONE = 0, - SMALLROOM = 1, - MEDIUMROOM = 2, - LARGEROOM = 3, - MEDIUMHALL = 4, - LARGEHALL = 5, - PLATE = 6, + NONE, + SMALLROOM, + MEDIUMROOM, + LARGEROOM, + MEDIUMHALL, + LARGEHALL, + PLATE, } @VintfStability union Id { diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl index 3176b0103a..17f98142f8 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/State.aidl @@ -34,7 +34,7 @@ package android.hardware.audio.effect; @Backing(type="byte") @VintfStability enum State { - INIT = 0, - IDLE = 1, - PROCESSING = 2, + INIT, + IDLE, + PROCESSING, } diff --git a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Visualizer.aidl b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Visualizer.aidl index 25f0b736d4..c8cb551b99 100644 --- a/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Visualizer.aidl +++ b/audio/aidl/aidl_api/android.hardware.audio.effect/current/android/hardware/audio/effect/Visualizer.aidl @@ -62,12 +62,12 @@ union Visualizer { @VintfStability enum ScalingMode { NORMALIZED = 0, - AS_PLAYED = 1, + AS_PLAYED, } @VintfStability enum MeasurementMode { NONE = 0, - PEAK_RMS = 1, + PEAK_RMS, } @VintfStability union GetOnlyParameters { diff --git a/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl b/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl index ee5dcad9e0..6db3338d73 100644 --- a/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl +++ b/audio/aidl/android/hardware/audio/effect/DynamicsProcessing.aidl @@ -51,6 +51,14 @@ union DynamicsProcessing { * capability definition not enough. */ ParcelableHolder extension; + /** + * Min Cut off frequency (in Hz) for all Bands. + */ + float minCutOffFreq; + /** + * Max Cut off frequency (in Hz) for all Bands. + */ + float maxCutOffFreq; } /** @@ -68,16 +76,16 @@ union DynamicsProcessing { } /** - * Band enablement configuration. + * Stage enablement configuration. */ @VintfStability - parcelable BandEnablement { + parcelable StageEnablement { /** - * True if multi-band stage is in use. + * True if stage is in use. */ boolean inUse; /** - * Number of bands configured for this stage. + * Number of bands configured for this stage. Must be positive when inUse is true. */ int bandCount; } @@ -92,21 +100,22 @@ union DynamicsProcessing { */ ResolutionPreference resolutionPreference = ResolutionPreference.FAVOR_FREQUENCY_RESOLUTION; /** - * Preferred frame duration in milliseconds (ms). + * Preferred processing duration in milliseconds (ms). Must not be negative, 0 means no + * preference. */ - float preferredFrameDurationMs; + float preferredProcessingDurationMs; /** * PreEq stage (Multi-band Equalizer) configuration. */ - BandEnablement preEqBand; + StageEnablement preEqStage; /** * PostEq stage (Multi-band Equalizer) configuration. */ - BandEnablement postEqBand; + StageEnablement postEqStage; /** * MBC stage (Multi-band Compressor) configuration. */ - BandEnablement mbcBand; + StageEnablement mbcStage; /** * True if Limiter stage is in use. */ @@ -114,18 +123,19 @@ union DynamicsProcessing { } /** - * Band enablement configuration for a specific channel. + * Enablement configuration for a specific channel. */ @VintfStability - parcelable BandChannelConfig { + parcelable ChannelConfig { /** - * Channel index. + * Channel index. Must not be negative, and not exceed the channel count calculated from + * Parameter.common.input.base.channelMask. */ int channel; /** - * Channel index. + * Channel enablement configuration. Can not be true if corresponding stage is not in use. */ - BandEnablement enablement; + boolean enable; } /** @@ -134,7 +144,8 @@ union DynamicsProcessing { @VintfStability parcelable EqBandConfig { /** - * Channel index. + * Channel index. Must not be negative, and not exceed the channel count calculated from + * Parameter.common.input.base.channelMask. */ int channel; /** @@ -142,17 +153,20 @@ union DynamicsProcessing { */ int band; /** - * True if EQ stage is enabled. + * True if EQ band is enabled. + * If EngineArchitecture EQ stage inUse was set to false, then enable can not be set to + * true. */ boolean enable; /** - * Topmost frequency number (in Hz) this band will process. + * Topmost frequency number (in Hz) this band will process. Must be in the range of + * [minCutOffFreq, maxCutOffFreq] defined in Capability. */ - float cutoffFrequency; + float cutoffFrequencyHz; /** * Gain factor in decibels (dB). */ - float gain; + float gainDb; } /** @@ -161,51 +175,53 @@ union DynamicsProcessing { @VintfStability parcelable MbcBandConfig { /** - * Channel index. + * Channel index. Must not be negative, and not exceed the channel count calculated from + * Parameter.common.input.base.channelMask. */ int channel; /** - * Band index, must in the range of [0, bandCount-1]. + * Band index. Must be in the range of [0, bandCount-1]. */ int band; /** - * True if MBC stage is enabled. + * True if MBC band is enabled. + * If EngineArchitecture MBC inUse was set to false, then enable here can not be set to + * true. */ boolean enable; /** - * Topmost frequency number (in Hz) this band will process. + * Topmost frequency number (in Hz) this band will process. Must be in the range of + * [minCutOffFreq, maxCutOffFreq] defined in Capability. */ float cutoffFrequencyHz; /** - * Gain factor in decibels (dB). - */ - float gainDb; - /** - * Attack Time for compressor in milliseconds (ms). + * Attack Time for compressor in milliseconds (ms). Must not be negative. */ float attackTimeMs; /** - * Release Time for compressor in milliseconds (ms). + * Release Time for compressor in milliseconds (ms). Must not be negative. */ float releaseTimeMs; /** - * Compressor ratio (N:1) (input:output). + * Compressor ratio (N:1) (input:output). Must not be negative. */ float ratio; /** - * Compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS). + * Compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS). Must not be + * positive. */ float thresholdDb; /** - * Width in decibels (dB) around compressor threshold point. + * Width in decibels (dB) around compressor threshold point. Must not be negative. */ float kneeWidthDb; /** - * Noise gate threshold in decibels (dB) from 0 dB Full Scale (dBFS). + * Noise gate threshold in decibels (dB) from 0 dB Full Scale (dBFS). Must not be positive. */ float noiseGateThresholdDb; /** - * Expander ratio (1:N) (input:output) for signals below the Noise Gate Threshold. + * Expander ratio (1:N) (input:output) for signals below the Noise Gate Threshold. Must not + * be negative. */ float expanderRatio; /** @@ -224,36 +240,35 @@ union DynamicsProcessing { @VintfStability parcelable LimiterConfig { /** - * Channel index. + * Channel index. Must not be negative, and not exceed the channel count calculated from + * Parameter.common.input.base.channelMask. */ int channel; /** - * True if Limiter stage is enabled. + * True if Limiter band is enabled. + * If EngineArchitecture limiterInUse was set to false, then enable can not be set to true. */ boolean enable; - /** - * True if Limiter stage is in use. - */ - boolean inUse; /** * Index of group assigned to this Limiter. Only limiters that share the same linkGroup * index will react together. */ int linkGroup; /** - * Attack Time for compressor in milliseconds (ms). + * Attack Time for compressor in milliseconds (ms). Must not be negative. */ float attackTimeMs; /** - * Release Time for compressor in milliseconds (ms). + * Release Time for compressor in milliseconds (ms). Must not be negative. */ float releaseTimeMs; /** - * Compressor ratio (N:1) (input:output). + * Compressor ratio (N:1) (input:output). Must not be negative. */ float ratio; /** - * Compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS). + * Compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS). Must not be + * positive. */ float thresholdDb; /** @@ -262,40 +277,56 @@ union DynamicsProcessing { float postGainDb; } + /** + * Input gain for a channel (specified by the channel index). + */ + @VintfStability + parcelable InputGain { + /** + * Channel index. Must not be negative, and not exceed the channel count calculated from + * Parameter.common.input.base.channelMask. + */ + int channel; + /** + * Gain applied to the input signal in decibels (dB). 0 dB means no change in level. + */ + float gainDb; + } + /** * Effect engine architecture. */ EngineArchitecture engineArchitecture; /** - * PreEq stage per channel configuration. + * PreEq stage per channel configuration. Only valid when pre EQ stage inUse is true. */ - BandChannelConfig preEq; + ChannelConfig[] preEq; /** - * PostEq stage per channel configuration. + * PostEq stage per channel configuration. Only valid when post EQ stage inUse is true. */ - BandChannelConfig postEq; + ChannelConfig[] postEq; /** - * PreEq stage per band configuration. + * PreEq stage per band configuration. Only valid when pre EQ stage inUse is true. */ - EqBandConfig preEqBand; + EqBandConfig[] preEqBand; /** - * PostEq stage per band configuration. + * PostEq stage per band configuration. Only valid when post EQ stage inUse is true. */ - EqBandConfig postEqBand; + EqBandConfig[] postEqBand; /** - * MBC stage per channel configuration. + * MBC stage per channel configuration. Only valid when MBC stage inUse is true. */ - BandChannelConfig mbc; + ChannelConfig[] mbc; /** - * PostEq stage per band configuration. + * PostEq stage per band configuration. Only valid when MBC stage inUse is true. */ - MbcBandConfig mbcBand; + MbcBandConfig[] mbcBand; /** - * Limiter stage configuration. + * Limiter stage configuration. Only valid when limiter stage inUse is true. */ - LimiterConfig limiter; + LimiterConfig[] limiter; /** - * Input gain factor in decibels (dB). 0 dB means no change in level. + * Input gain factor. */ - float inputGainDb; + InputGain[] inputGain; } From e4064cebba408f64c972a64fcce35e0a5f801282 Mon Sep 17 00:00:00 2001 From: Ram Mohan Date: Tue, 20 Dec 2022 18:05:14 +0530 Subject: [PATCH 2/2] DynamicsProcessing: Add AIDL placeholder implementation and its unit test Update audio_effects_config.xml to use libeffect dynamicsProcessing implementation. Bug: 258124419 Test: atest VtsHalDynamicsProcessingTargetTest Test: atest VtsHalAudioEffectTargetTest Change-Id: If93d084be383b716ff950faf1c3e23d6c1edaa66 --- audio/aidl/TEST_MAPPING | 3 + audio/aidl/default/Android.bp | 2 +- .../AcousticEchoCancelerSw.cpp | 1 - audio/aidl/default/audio_effects_config.xml | 4 +- .../DynamicsProcessingSw.cpp | 387 +++++- .../dynamicProcessing/DynamicsProcessingSw.h | 80 +- .../include/effect-impl/EffectContext.h | 4 +- .../default/include/effect-impl/EffectTypes.h | 13 +- .../default/include/effect-impl/EffectUUID.h | 6 + audio/aidl/vts/Android.bp | 6 + .../aidl/vts/VtsHalDynamicsProcessingTest.cpp | 1115 +++++++++++++++++ 11 files changed, 1598 insertions(+), 23 deletions(-) create mode 100644 audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp diff --git a/audio/aidl/TEST_MAPPING b/audio/aidl/TEST_MAPPING index a166e61872..486c13d799 100644 --- a/audio/aidl/TEST_MAPPING +++ b/audio/aidl/TEST_MAPPING @@ -12,6 +12,9 @@ { "name": "VtsHalDownmixTargetTest" }, + { + "name": "VtsHalDynamicProcessingTargetTest" + }, { "name": "VtsHalEnvironmentalReverbTargetTest" }, diff --git a/audio/aidl/default/Android.bp b/audio/aidl/default/Android.bp index 6f23636744..1e6785f940 100644 --- a/audio/aidl/default/Android.bp +++ b/audio/aidl/default/Android.bp @@ -156,7 +156,7 @@ cc_binary { "libbassboostsw", "libbundleaidl", "libdownmixaidl", - "libdynamicsprocessingsw", + "libdynamicsprocessingaidl", "libenvreverbsw", "libequalizersw", "libhapticgeneratoraidl", diff --git a/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp b/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp index 7f06013ee6..40b46e0642 100644 --- a/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp +++ b/audio/aidl/default/acousticEchoCanceler/AcousticEchoCancelerSw.cpp @@ -54,7 +54,6 @@ extern "C" binder_exception_t queryEffect(const AudioUuid* in_impl_uuid, Descrip return EX_ILLEGAL_ARGUMENT; } *_aidl_return = AcousticEchoCancelerSw::kDescriptor; - LOG(ERROR) << __func__ << "xxx " << _aidl_return->toString(); return EX_NONE; } diff --git a/audio/aidl/default/audio_effects_config.xml b/audio/aidl/default/audio_effects_config.xml index 9670e9c08b..e460f89056 100644 --- a/audio/aidl/default/audio_effects_config.xml +++ b/audio/aidl/default/audio_effects_config.xml @@ -33,7 +33,7 @@ - + @@ -72,7 +72,7 @@ - + diff --git a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp index 39345a9683..0ffbaa11cc 100644 --- a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp +++ b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "AHAL_DynamicsProcessingSw" #include #include +#include #include #include @@ -60,7 +61,8 @@ extern "C" binder_exception_t queryEffect(const AudioUuid* in_impl_uuid, Descrip namespace aidl::android::hardware::audio::effect { const std::string DynamicsProcessingSw::kEffectName = "DynamicsProcessingSw"; -const DynamicsProcessing::Capability DynamicsProcessingSw::kCapability; +const DynamicsProcessing::Capability DynamicsProcessingSw::kCapability = {.minCutOffFreq = 220, + .maxCutOffFreq = 20000}; const Descriptor DynamicsProcessingSw::kDescriptor = { .common = {.id = {.type = kDynamicsProcessingTypeUUID, .uuid = kDynamicsProcessingSwImplUUID, @@ -83,16 +85,143 @@ ndk::ScopedAStatus DynamicsProcessingSw::setParameterSpecific(const Parameter::S RETURN_IF(Parameter::Specific::dynamicsProcessing != specific.getTag(), EX_ILLEGAL_ARGUMENT, "EffectNotSupported"); - mSpecificParam = specific.get(); - LOG(DEBUG) << __func__ << " success with: " << specific.toString(); - return ndk::ScopedAStatus::ok(); + RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); + + LOG(INFO) << __func__ << specific.toString(); + auto& dpParam = specific.get(); + auto tag = dpParam.getTag(); + switch (tag) { + case DynamicsProcessing::engineArchitecture: { + RETURN_IF(mContext->setEngineArchitecture( + dpParam.get()) != + RetCode::SUCCESS, + EX_ILLEGAL_ARGUMENT, "setEngineArchitectureFailed"); + return ndk::ScopedAStatus::ok(); + } + case DynamicsProcessing::preEq: { + RETURN_IF(mContext->setPreEqChannelCfgs(dpParam.get()) != + RetCode::SUCCESS, + EX_ILLEGAL_ARGUMENT, "setPreEqChannelCfgsFailed"); + return ndk::ScopedAStatus::ok(); + } + case DynamicsProcessing::postEq: { + RETURN_IF(mContext->setPostEqChannelCfgs(dpParam.get()) != + RetCode::SUCCESS, + EX_ILLEGAL_ARGUMENT, "setPostEqChannelCfgsFailed"); + return ndk::ScopedAStatus::ok(); + } + case DynamicsProcessing::mbc: { + RETURN_IF(mContext->setMbcChannelCfgs(dpParam.get()) != + RetCode::SUCCESS, + EX_ILLEGAL_ARGUMENT, "setMbcChannelCfgsFailed"); + return ndk::ScopedAStatus::ok(); + } + case DynamicsProcessing::preEqBand: { + RETURN_IF(mContext->setPreEqBandCfgs(dpParam.get()) != + RetCode::SUCCESS, + EX_ILLEGAL_ARGUMENT, "setPreEqBandCfgsFailed"); + return ndk::ScopedAStatus::ok(); + } + case DynamicsProcessing::postEqBand: { + RETURN_IF(mContext->setPostEqBandCfgs(dpParam.get()) != + RetCode::SUCCESS, + EX_ILLEGAL_ARGUMENT, "setPostEqBandCfgsFailed"); + return ndk::ScopedAStatus::ok(); + } + case DynamicsProcessing::mbcBand: { + RETURN_IF(mContext->setMbcBandCfgs(dpParam.get()) != + RetCode::SUCCESS, + EX_ILLEGAL_ARGUMENT, "setMbcBandCfgsFailed"); + return ndk::ScopedAStatus::ok(); + } + case DynamicsProcessing::limiter: { + RETURN_IF(mContext->setLimiterCfgs(dpParam.get()) != + RetCode::SUCCESS, + EX_ILLEGAL_ARGUMENT, "limiterCfgsFailed"); + return ndk::ScopedAStatus::ok(); + } + case DynamicsProcessing::inputGain: { + RETURN_IF(mContext->setInputGainCfgs(dpParam.get()) != + RetCode::SUCCESS, + EX_ILLEGAL_ARGUMENT, "inputGainCfgFailed"); + return ndk::ScopedAStatus::ok(); + } + case DynamicsProcessing::vendorExtension: { + LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag); + return ndk::ScopedAStatus::fromExceptionCodeWithMessage( + EX_ILLEGAL_ARGUMENT, "DynamicsProcessingTagNotSupported"); + } + } } ndk::ScopedAStatus DynamicsProcessingSw::getParameterSpecific(const Parameter::Id& id, Parameter::Specific* specific) { auto tag = id.getTag(); RETURN_IF(Parameter::Id::dynamicsProcessingTag != tag, EX_ILLEGAL_ARGUMENT, "wrongIdTag"); - specific->set(mSpecificParam); + auto dpId = id.get(); + auto dpIdTag = dpId.getTag(); + switch (dpIdTag) { + case DynamicsProcessing::Id::commonTag: + return getParameterDynamicsProcessing(dpId.get(), + specific); + case DynamicsProcessing::Id::vendorExtensionTag: + LOG(ERROR) << __func__ << " unsupported tag: " << toString(dpIdTag); + return ndk::ScopedAStatus::fromExceptionCodeWithMessage( + EX_ILLEGAL_ARGUMENT, "DynamicsProcessingTagNotSupported"); + } +} + +ndk::ScopedAStatus DynamicsProcessingSw::getParameterDynamicsProcessing( + const DynamicsProcessing::Tag& tag, Parameter::Specific* specific) { + RETURN_IF(!mContext, EX_NULL_POINTER, "nullContext"); + + DynamicsProcessing dpParam; + switch (tag) { + case DynamicsProcessing::Tag::engineArchitecture: { + dpParam.set(mContext->getEngineArchitecture()); + break; + } + case DynamicsProcessing::Tag::preEq: { + dpParam.set(mContext->getPreEqChannelCfgs()); + break; + } + case DynamicsProcessing::Tag::postEq: { + dpParam.set(mContext->getPostEqChannelCfgs()); + break; + } + case DynamicsProcessing::Tag::mbc: { + dpParam.set(mContext->getMbcChannelCfgs()); + break; + } + case DynamicsProcessing::Tag::preEqBand: { + dpParam.set(mContext->getPreEqBandCfgs()); + break; + } + case DynamicsProcessing::Tag::postEqBand: { + dpParam.set(mContext->getPostEqBandCfgs()); + break; + } + case DynamicsProcessing::Tag::mbcBand: { + dpParam.set(mContext->getMbcBandCfgs()); + break; + } + case DynamicsProcessing::Tag::limiter: { + dpParam.set(mContext->getLimiterCfgs()); + break; + } + case DynamicsProcessing::Tag::inputGain: { + dpParam.set(mContext->getInputGainCfgs()); + break; + } + case DynamicsProcessing::vendorExtension: { + LOG(ERROR) << __func__ << " unsupported tag: " << toString(tag); + return ndk::ScopedAStatus::fromExceptionCodeWithMessage( + EX_ILLEGAL_ARGUMENT, "DynamicsProcessingTagNotSupported"); + } + } + + specific->set(dpParam); + LOG(INFO) << __func__ << specific->toString(); return ndk::ScopedAStatus::ok(); } @@ -127,4 +256,252 @@ IEffect::Status DynamicsProcessingSw::effectProcessImpl(float* in, float* out, i return {STATUS_OK, samples, samples}; } +RetCode DynamicsProcessingSwContext::setCommon(const Parameter::Common& common) { + mCommon = common; + mChannelCount = + ::android::hardware::audio::common::getChannelCount(common.input.base.channelMask); + resizeChannels(); + resizeBands(); + LOG(INFO) << __func__ << mCommon.toString(); + return RetCode::SUCCESS; +} + +RetCode DynamicsProcessingSwContext::setEngineArchitecture( + const DynamicsProcessing::EngineArchitecture& cfg) { + RETURN_VALUE_IF(!validateEngineConfig(cfg), RetCode::ERROR_ILLEGAL_PARAMETER, + "illegalEngineConfig"); + + if (mEngineSettings == cfg) { + LOG(INFO) << __func__ << " not change in engine, do nothing"; + return RetCode::SUCCESS; + } + mEngineSettings = cfg; + resizeBands(); + return RetCode::SUCCESS; +} + +RetCode DynamicsProcessingSwContext::setChannelCfgs( + const std::vector& cfgs, + std::vector& targetCfgs, + const DynamicsProcessing::StageEnablement& stage) { + RETURN_VALUE_IF(!stage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER, "stageNotInUse"); + + RetCode ret = RetCode::SUCCESS; + std::unordered_set channelSet; + for (auto& cfg : cfgs) { + if (cfg.channel < 0 || (size_t)cfg.channel >= mChannelCount) { + LOG(ERROR) << __func__ << " skip illegal channel config " << cfg.toString(); + ret = RetCode::ERROR_ILLEGAL_PARAMETER; + continue; + } + if (0 != channelSet.count(cfg.channel)) { + LOG(WARNING) << __func__ << " duplicated channel " << cfg.channel; + ret = RetCode::ERROR_ILLEGAL_PARAMETER; + } else { + channelSet.insert(cfg.channel); + } + targetCfgs[cfg.channel] = cfg; + } + return ret; +} + +RetCode DynamicsProcessingSwContext::setPreEqChannelCfgs( + const std::vector& cfgs) { + return setChannelCfgs(cfgs, mPreEqChCfgs, mEngineSettings.preEqStage); +} + +RetCode DynamicsProcessingSwContext::setPostEqChannelCfgs( + const std::vector& cfgs) { + return setChannelCfgs(cfgs, mPostEqChCfgs, mEngineSettings.postEqStage); +} + +RetCode DynamicsProcessingSwContext::setMbcChannelCfgs( + const std::vector& cfgs) { + return setChannelCfgs(cfgs, mMbcChCfgs, mEngineSettings.mbcStage); +} + +RetCode DynamicsProcessingSwContext::setEqBandCfgs( + const std::vector& cfgs, + std::vector& targetCfgs, + const DynamicsProcessing::StageEnablement& stage, + const std::vector& channelConfig) { + RETURN_VALUE_IF(!stage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER, "eqStageNotInUse"); + + RetCode ret = RetCode::SUCCESS; + std::set> bandSet; + + for (auto& cfg : cfgs) { + if (0 != bandSet.count({cfg.channel, cfg.band})) { + LOG(WARNING) << __func__ << " duplicated band " << cfg.toString(); + ret = RetCode::ERROR_ILLEGAL_PARAMETER; + } else { + bandSet.insert({cfg.channel, cfg.band}); + } + if (!validateEqBandConfig(cfg, mChannelCount, stage.bandCount, channelConfig)) { + LOG(WARNING) << __func__ << " skip invalid band " << cfg.toString(); + ret = RetCode::ERROR_ILLEGAL_PARAMETER; + continue; + ; + } + targetCfgs[cfg.channel * stage.bandCount + cfg.band] = cfg; + } + return ret; +} + +RetCode DynamicsProcessingSwContext::setPreEqBandCfgs( + const std::vector& cfgs) { + return setEqBandCfgs(cfgs, mPreEqChBands, mEngineSettings.preEqStage, mPreEqChCfgs); +} + +RetCode DynamicsProcessingSwContext::setPostEqBandCfgs( + const std::vector& cfgs) { + return setEqBandCfgs(cfgs, mPostEqChBands, mEngineSettings.postEqStage, mPostEqChCfgs); +} + +RetCode DynamicsProcessingSwContext::setMbcBandCfgs( + const std::vector& cfgs) { + RETURN_VALUE_IF(!mEngineSettings.mbcStage.inUse, RetCode::ERROR_ILLEGAL_PARAMETER, + "mbcNotInUse"); + + RetCode ret = RetCode::SUCCESS; + std::set> bandSet; + + int bandCount = mEngineSettings.mbcStage.bandCount; + std::vector filled(mChannelCount * bandCount, false); + for (auto& it : cfgs) { + if (0 != bandSet.count({it.channel, it.band})) { + LOG(WARNING) << __func__ << " duplicated band " << it.toString(); + ret = RetCode::ERROR_ILLEGAL_PARAMETER; + } else { + bandSet.insert({it.channel, it.band}); + } + if (!validateMbcBandConfig(it, mChannelCount, mEngineSettings.mbcStage.bandCount, + mMbcChCfgs)) { + LOG(WARNING) << __func__ << " skip invalid band " << it.toString(); + ret = RetCode::ERROR_ILLEGAL_PARAMETER; + continue; + ; + } + mMbcChBands[it.channel * bandCount + it.band] = it; + } + return ret; +} + +RetCode DynamicsProcessingSwContext::setLimiterCfgs( + const std::vector& cfgs) { + RETURN_VALUE_IF(!mEngineSettings.limiterInUse, RetCode::ERROR_ILLEGAL_PARAMETER, + "limiterNotInUse"); + + RetCode ret = RetCode::SUCCESS; + std::unordered_set channelSet; + + for (auto& it : cfgs) { + if (0 != channelSet.count(it.channel)) { + LOG(WARNING) << __func__ << " duplicated channel " << it.channel; + ret = RetCode::ERROR_ILLEGAL_PARAMETER; + } else { + channelSet.insert(it.channel); + } + if (!validateLimiterConfig(it, mChannelCount)) { + LOG(WARNING) << __func__ << " skip invalid limiter " << it.toString(); + ret = RetCode::ERROR_ILLEGAL_PARAMETER; + continue; + } + mLimiterCfgs[it.channel] = it; + } + return ret; +} + +void DynamicsProcessingSwContext::resizeChannels() { + if (mPreEqChCfgs.size() != mChannelCount) { + mPreEqChCfgs.resize(mChannelCount, {.channel = kInvalidChannelId}); + } + if (mPostEqChCfgs.size() != mChannelCount) { + mPostEqChCfgs.resize(mChannelCount, {.channel = kInvalidChannelId}); + } + if (mMbcChCfgs.size() != mChannelCount) { + mMbcChCfgs.resize(mChannelCount, {.channel = kInvalidChannelId}); + } + if (mLimiterCfgs.size() != mChannelCount) { + mLimiterCfgs.resize(mChannelCount, {.channel = kInvalidChannelId}); + } + if (mInputGainCfgs.size() != mChannelCount) { + mInputGainCfgs.resize(mChannelCount, {.channel = kInvalidChannelId}); + } +} + +void DynamicsProcessingSwContext::resizeBands() { + if (mPreEqChBands.size() != (size_t)(mChannelCount * mEngineSettings.preEqStage.bandCount)) { + mPreEqChBands.resize(mChannelCount * mEngineSettings.preEqStage.bandCount, + {.channel = kInvalidChannelId}); + } + if (mPostEqChBands.size() != (size_t)(mChannelCount * mEngineSettings.postEqStage.bandCount)) { + mPostEqChBands.resize(mChannelCount * mEngineSettings.postEqStage.bandCount, + {.channel = kInvalidChannelId}); + } + if (mMbcChBands.size() != (size_t)(mChannelCount * mEngineSettings.mbcStage.bandCount)) { + mMbcChBands.resize(mChannelCount * mEngineSettings.mbcStage.bandCount, + {.channel = kInvalidChannelId}); + } +} + +RetCode DynamicsProcessingSwContext::setInputGainCfgs( + const std::vector& cfgs) { + for (const auto& cfg : cfgs) { + RETURN_VALUE_IF(cfg.channel < 0 || (size_t)cfg.channel >= mChannelCount, + RetCode::ERROR_ILLEGAL_PARAMETER, "invalidChannel"); + mInputGainCfgs[cfg.channel] = cfg; + } + return RetCode::SUCCESS; +} + +std::vector DynamicsProcessingSwContext::getInputGainCfgs() { + std::vector ret; + std::copy_if(mInputGainCfgs.begin(), mInputGainCfgs.end(), std::back_inserter(ret), + [&](const auto& gain) { return gain.channel != kInvalidChannelId; }); + return ret; +} + +bool DynamicsProcessingSwContext::validateCutoffFrequency(float freq) { + return freq >= DynamicsProcessingSw::kCapability.minCutOffFreq && + freq <= DynamicsProcessingSw::kCapability.maxCutOffFreq; +} + +bool DynamicsProcessingSwContext::validateStageEnablement( + const DynamicsProcessing::StageEnablement& enablement) { + return !enablement.inUse || (enablement.inUse && enablement.bandCount > 0); +} + +bool DynamicsProcessingSwContext::validateEngineConfig( + const DynamicsProcessing::EngineArchitecture& engine) { + return engine.preferredProcessingDurationMs >= 0 && + validateStageEnablement(engine.preEqStage) && + validateStageEnablement(engine.postEqStage) && validateStageEnablement(engine.mbcStage); +} + +bool DynamicsProcessingSwContext::validateEqBandConfig( + const DynamicsProcessing::EqBandConfig& band, int maxChannel, int maxBand, + const std::vector& channelConfig) { + return band.channel >= 0 && band.channel < maxChannel && + (size_t)band.channel < channelConfig.size() && channelConfig[band.channel].enable && + band.band >= 0 && band.band < maxBand && validateCutoffFrequency(band.cutoffFrequencyHz); +} + +bool DynamicsProcessingSwContext::validateMbcBandConfig( + const DynamicsProcessing::MbcBandConfig& band, int maxChannel, int maxBand, + const std::vector& channelConfig) { + return band.channel >= 0 && band.channel < maxChannel && + (size_t)band.channel < channelConfig.size() && channelConfig[band.channel].enable && + band.band >= 0 && band.band < maxBand && + validateCutoffFrequency(band.cutoffFrequencyHz) && band.attackTimeMs >= 0 && + band.releaseTimeMs >= 0 && band.ratio >= 0 && band.thresholdDb <= 0 && + band.kneeWidthDb <= 0 && band.noiseGateThresholdDb <= 0 && band.expanderRatio >= 0; +} + +bool DynamicsProcessingSwContext::validateLimiterConfig( + const DynamicsProcessing::LimiterConfig& limiter, int maxChannel) { + return limiter.channel >= 0 && limiter.channel < maxChannel && limiter.attackTimeMs >= 0 && + limiter.releaseTimeMs >= 0 && limiter.ratio >= 0 && limiter.thresholdDb <= 0; +} + } // namespace aidl::android::hardware::audio::effect diff --git a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h index 2ae46c5ac5..e336df7b8e 100644 --- a/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h +++ b/audio/aidl/default/dynamicProcessing/DynamicsProcessingSw.h @@ -29,11 +29,77 @@ namespace aidl::android::hardware::audio::effect { class DynamicsProcessingSwContext final : public EffectContext { public: DynamicsProcessingSwContext(int statusDepth, const Parameter::Common& common) - : EffectContext(statusDepth, common) { + : EffectContext(statusDepth, common), + mChannelCount(::android::hardware::audio::common::getChannelCount( + common.input.base.channelMask)), + mPreEqChCfgs(mChannelCount, {.channel = kInvalidChannelId}), + mPostEqChCfgs(mChannelCount, {.channel = kInvalidChannelId}), + mMbcChCfgs(mChannelCount, {.channel = kInvalidChannelId}), + mLimiterCfgs(mChannelCount, {.channel = kInvalidChannelId}) { LOG(DEBUG) << __func__; } - // TODO: add specific context here -}; + + // utils + RetCode setChannelCfgs(const std::vector& cfgs, + std::vector& targetCfgs, + const DynamicsProcessing::StageEnablement& engineSetting); + + RetCode setEqBandCfgs(const std::vector& cfgs, + std::vector& targetCfgs, + const DynamicsProcessing::StageEnablement& stage, + const std::vector& channelConfig); + + // set params + RetCode setCommon(const Parameter::Common& common) override; + RetCode setEngineArchitecture(const DynamicsProcessing::EngineArchitecture& cfg); + RetCode setPreEqChannelCfgs(const std::vector& cfgs); + RetCode setPostEqChannelCfgs(const std::vector& cfgs); + RetCode setMbcChannelCfgs(const std::vector& cfgs); + RetCode setPreEqBandCfgs(const std::vector& cfgs); + RetCode setPostEqBandCfgs(const std::vector& cfgs); + RetCode setMbcBandCfgs(const std::vector& cfgs); + RetCode setLimiterCfgs(const std::vector& cfgs); + RetCode setInputGainCfgs(const std::vector& cfgs); + + // get params + DynamicsProcessing::EngineArchitecture getEngineArchitecture() { return mEngineSettings; } + std::vector getPreEqChannelCfgs() { return mPreEqChCfgs; } + std::vector getPostEqChannelCfgs() { return mPostEqChCfgs; } + std::vector getMbcChannelCfgs() { return mMbcChCfgs; } + std::vector getPreEqBandCfgs() { return mPreEqChBands; } + std::vector getPostEqBandCfgs() { return mPostEqChBands; } + std::vector getMbcBandCfgs() { return mMbcChBands; } + std::vector getLimiterCfgs() { return mLimiterCfgs; } + std::vector getInputGainCfgs(); + + private: + static constexpr int32_t kInvalidChannelId = -1; + size_t mChannelCount = 0; + DynamicsProcessing::EngineArchitecture mEngineSettings; + // Channel config vector with size of mChannelCount + std::vector mPreEqChCfgs; + std::vector mPostEqChCfgs; + std::vector mMbcChCfgs; + std::vector mLimiterCfgs; + std::vector mInputGainCfgs; + // Band config vector with size of mChannelCount * bandCount + std::vector mPreEqChBands; + std::vector mPostEqChBands; + std::vector mMbcChBands; + + bool validateCutoffFrequency(float freq); + bool validateStageEnablement(const DynamicsProcessing::StageEnablement& enablement); + bool validateEngineConfig(const DynamicsProcessing::EngineArchitecture& engine); + bool validateEqBandConfig(const DynamicsProcessing::EqBandConfig& band, int maxChannel, + int maxBand, + const std::vector& channelConfig); + bool validateMbcBandConfig(const DynamicsProcessing::MbcBandConfig& band, int maxChannel, + int maxBand, + const std::vector& channelConfig); + bool validateLimiterConfig(const DynamicsProcessing::LimiterConfig& limiter, int maxChannel); + void resizeChannels(); + void resizeBands(); +}; // DynamicsProcessingSwContext class DynamicsProcessingSw final : public EffectImpl { public: @@ -60,7 +126,9 @@ class DynamicsProcessingSw final : public EffectImpl { private: std::shared_ptr mContext; - /* parameters */ - DynamicsProcessing mSpecificParam; -}; + ndk::ScopedAStatus getParameterDynamicsProcessing(const DynamicsProcessing::Tag& tag, + Parameter::Specific* specific); + +}; // DynamicsProcessingSw + } // 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 index 1f39db01e7..a3e7ff2066 100644 --- a/audio/aidl/default/include/effect-impl/EffectContext.h +++ b/audio/aidl/default/include/effect-impl/EffectContext.h @@ -119,11 +119,11 @@ class EffectContext { virtual RetCode setCommon(const Parameter::Common& common) { mCommon = common; - LOG(ERROR) << __func__ << mCommon.toString(); + LOG(INFO) << __func__ << mCommon.toString(); return RetCode::SUCCESS; } virtual Parameter::Common getCommon() { - LOG(ERROR) << __func__ << mCommon.toString(); + LOG(INFO) << __func__ << mCommon.toString(); return mCommon; } diff --git a/audio/aidl/default/include/effect-impl/EffectTypes.h b/audio/aidl/default/include/effect-impl/EffectTypes.h index 58c8672145..b100a2ebf3 100644 --- a/audio/aidl/default/include/effect-impl/EffectTypes.h +++ b/audio/aidl/default/include/effect-impl/EffectTypes.h @@ -98,12 +98,13 @@ inline std::ostream& operator<<(std::ostream& out, const RetCode& code) { } \ } while (0) -#define RETURN_VALUE_IF(expr, ret, log) \ - do { \ - if (expr) { \ - LOG(ERROR) << __func__ << ":" << __LINE__ << " return with expr " << #expr << (log); \ - return ret; \ - } \ +#define RETURN_VALUE_IF(expr, ret, log) \ + do { \ + if (expr) { \ + LOG(ERROR) << __func__ << ":" << __LINE__ << " return with expr \"" << #expr \ + << "\":" << (log); \ + return ret; \ + } \ } while (0) #define RETURN_IF_BINDER_EXCEPTION(functor) \ diff --git a/audio/aidl/default/include/effect-impl/EffectUUID.h b/audio/aidl/default/include/effect-impl/EffectUUID.h index 6eec29e036..1a60829ce8 100644 --- a/audio/aidl/default/include/effect-impl/EffectUUID.h +++ b/audio/aidl/default/include/effect-impl/EffectUUID.h @@ -135,6 +135,12 @@ static const AudioUuid kDynamicsProcessingSwImplUUID = {static_cast(0xf 0x11ed, 0x9b6a, {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}}; +// e0e6539b-1781-7261-676f-6d7573696340 +static const AudioUuid kDynamicsProcessingImplUUID = {static_cast(0xe0e6539b), + 0x1781, + 0x7261, + 0x676f, + {0x6d, 0x75, 0x73, 0x69, 0x63, 0x40}}; // 1411e6d6-aecd-4021-a1cf-a6aceb0d71e5 static const AudioUuid kHapticGeneratorTypeUUID = {static_cast(0x1411e6d6), 0xaecd, diff --git a/audio/aidl/vts/Android.bp b/audio/aidl/vts/Android.bp index becdf1bd9f..f9d12ddea0 100644 --- a/audio/aidl/vts/Android.bp +++ b/audio/aidl/vts/Android.bp @@ -80,6 +80,12 @@ cc_test { srcs: ["VtsHalDownmixTargetTest.cpp"], } +cc_test { + name: "VtsHalDynamicsProcessingTargetTest", + defaults: ["VtsHalAudioTargetTestDefaults"], + srcs: ["VtsHalDynamicsProcessingTest.cpp"], +} + cc_test { name: "VtsHalEnvironmentalReverbTargetTest", defaults: ["VtsHalAudioTargetTestDefaults"], diff --git a/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp new file mode 100644 index 0000000000..9feff911ed --- /dev/null +++ b/audio/aidl/vts/VtsHalDynamicsProcessingTest.cpp @@ -0,0 +1,1115 @@ +/* + * Copyright (C) 2023 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 + +#define LOG_TAG "VtsHalDynamicsProcessingTest" + +#include +#include +#include +#include + +#include +#include "EffectHelper.h" + +using namespace android; + +using aidl::android::hardware::audio::effect::Capability; +using aidl::android::hardware::audio::effect::Descriptor; +using aidl::android::hardware::audio::effect::DynamicsProcessing; +using aidl::android::hardware::audio::effect::IEffect; +using aidl::android::hardware::audio::effect::IFactory; +using aidl::android::hardware::audio::effect::kDynamicsProcessingTypeUUID; +using aidl::android::hardware::audio::effect::Parameter; + +/** + * Here we focus on specific parameter checking, general IEffect interfaces testing performed in + * VtsAudioEffectTargetTest. + */ +class DynamicsProcessingTestHelper : public EffectHelper { + public: + DynamicsProcessingTestHelper(std::pair, Descriptor> pair, + int32_t channelLayOut = AudioChannelLayout::LAYOUT_STEREO) { + std::tie(mFactory, mDescriptor) = pair; + mChannelLayout = channelLayOut; + mChannelCount = ::android::hardware::audio::common::getChannelCount( + AudioChannelLayout::make(mChannelLayout)); + } + + // setup + void SetUpDynamicsProcessingEffect() { + ASSERT_NE(nullptr, mFactory); + ASSERT_NO_FATAL_FAILURE(create(mFactory, mEffect, mDescriptor)); + + Parameter::Specific specific = getDefaultParamSpecific(); + Parameter::Common common = EffectHelper::createParamCommon( + 0 /* session */, 1 /* ioHandle */, 44100 /* iSampleRate */, 44100 /* oSampleRate */, + 0x100 /* iFrameCount */, 0x100 /* oFrameCount */, + AudioChannelLayout::make(mChannelLayout), + AudioChannelLayout::make(mChannelLayout)); + IEffect::OpenEffectReturn ret; + ASSERT_NO_FATAL_FAILURE(open(mEffect, common, specific, &ret, EX_NONE)); + ASSERT_NE(nullptr, mEffect); + mEngineConfigApplied = mEngineConfigPreset; + } + + Parameter::Specific getDefaultParamSpecific() { + DynamicsProcessing dp = DynamicsProcessing::make( + mEngineConfigPreset); + Parameter::Specific specific = + Parameter::Specific::make(dp); + return specific; + } + + // teardown + void TearDownDynamicsProcessingEffect() { + ASSERT_NO_FATAL_FAILURE(close(mEffect)); + ASSERT_NO_FATAL_FAILURE(destroy(mFactory, mEffect)); + } + + // utils functions for parameter checking + bool isParamValid(const DynamicsProcessing::Tag& tag, const DynamicsProcessing& dp, + const Descriptor& desc); + bool isParamEqual(const DynamicsProcessing::Tag& tag, const DynamicsProcessing& dpRef, + const DynamicsProcessing& dpTest); + + bool isEnablementValid(const DynamicsProcessing::StageEnablement& enablement); + bool isEngineConfigValid(const DynamicsProcessing::EngineArchitecture& cfg); + + bool isCutoffFrequencyValid(float freq, const DynamicsProcessing::Capability& cap); + bool isChannelConfigValid(const std::vector& cfgs, + bool stageInUse); + + bool isPreEqBandConfigValid(const DynamicsProcessing::Capability& cap, + const std::vector& cfgs, + bool stageInUse, int bandCount); + bool isPostEqBandConfigValid(const DynamicsProcessing::Capability& cap, + const std::vector& cfgs, + bool stageInUse, int bandCount); + bool isMbcBandConfigValid(const DynamicsProcessing::Capability& cap, + const std::vector& cfgs, + bool stageInUse, int bandCount); + bool isLimiterConfigValid(const std::vector& cfgs, + bool stageInUse); + bool isInputGainValid(const std::vector& cfgs); + + bool isEngineConfigEqual(const DynamicsProcessing::EngineArchitecture& refCfg, + const DynamicsProcessing::EngineArchitecture& testCfg); + + template + std::vector filterEnabledVector(const std::vector& vec); + + template + bool isAidlVectorEqualAfterFilter(const std::vector& source, const std::vector& target); + + template + bool isAidlVectorEqual(const std::vector& source, const std::vector& target); + + // get set params and validate + void SetAndGetDynamicsProcessingParameters(); + + // enqueue test parameters + void addEngineConfig(const DynamicsProcessing::EngineArchitecture& cfg); + void addPreEqChannelConfig(const std::vector& cfg); + void addPostEqChannelConfig(const std::vector& cfg); + void addMbcChannelConfig(const std::vector& cfg); + void addPreEqBandConfigs(const std::vector& cfgs); + void addPostEqBandConfigs(const std::vector& cfgs); + void addMbcBandConfigs(const std::vector& cfgs); + void addLimiterConfig(const std::vector& cfg); + void addInputGain(const std::vector& inputGain); + + static constexpr float kPreferredProcessingDurationMs = 10.0f; + static constexpr int kBandCount = 5; + std::shared_ptr mFactory; + std::shared_ptr mEffect; + Descriptor mDescriptor; + DynamicsProcessing::EngineArchitecture mEngineConfigApplied; + DynamicsProcessing::EngineArchitecture mEngineConfigPreset{ + .resolutionPreference = + DynamicsProcessing::ResolutionPreference::FAVOR_FREQUENCY_RESOLUTION, + .preferredProcessingDurationMs = kPreferredProcessingDurationMs, + .preEqStage = {.inUse = true, .bandCount = kBandCount}, + .postEqStage = {.inUse = true, .bandCount = kBandCount}, + .mbcStage = {.inUse = true, .bandCount = kBandCount}, + .limiterInUse = true, + }; + + std::unordered_set mPreEqChannelEnable; + std::unordered_set mPostEqChannelEnable; + std::unordered_set mMbcChannelEnable; + std::unordered_set mLimiterChannelEnable; + static const std::set> kChannelConfigTestSet; + static const std::set kStageEnablementTestSet; + static const std::set> kInputGainTestSet; + + private: + int32_t mChannelLayout; + int mChannelCount; + std::vector> mTags; + void CleanUp() { + mTags.clear(); + mPreEqChannelEnable.clear(); + mPostEqChannelEnable.clear(); + mMbcChannelEnable.clear(); + mLimiterChannelEnable.clear(); + } +}; + +// test value set for DynamicsProcessing::StageEnablement +const std::set + DynamicsProcessingTestHelper::kStageEnablementTestSet = { + {.inUse = true, .bandCount = DynamicsProcessingTestHelper::kBandCount}, + {.inUse = true, .bandCount = 0}, + {.inUse = true, .bandCount = -1}, + {.inUse = false, .bandCount = DynamicsProcessingTestHelper::kBandCount}}; + +// test value set for DynamicsProcessing::ChannelConfig +const std::set> + DynamicsProcessingTestHelper::kChannelConfigTestSet = { + {{.channel = -1, .enable = false}, + {.channel = 0, .enable = true}, + {.channel = 1, .enable = false}, + {.channel = 2, .enable = true}}, + + {{.channel = -1, .enable = false}, {.channel = 2, .enable = true}}, + + {{.channel = 0, .enable = true}, {.channel = 1, .enable = true}}}; + +// test value set for DynamicsProcessing::InputGain +const std::set> + DynamicsProcessingTestHelper::kInputGainTestSet = { + {{.channel = 0, .gainDb = 10.f}, + {.channel = 1, .gainDb = 0.f}, + {.channel = 2, .gainDb = -10.f}}, + + {{.channel = -1, .gainDb = -10.f}, {.channel = -2, .gainDb = 10.f}}, + + {{.channel = -1, .gainDb = 10.f}, {.channel = 0, .gainDb = -10.f}}}; + +bool DynamicsProcessingTestHelper::isParamValid(const DynamicsProcessing::Tag& tag, + const DynamicsProcessing& dp, + const Descriptor& desc) { + const DynamicsProcessing::Capability& dpCap = + desc.capability.get(); + switch (tag) { + case DynamicsProcessing::engineArchitecture: { + return isEngineConfigValid(dp.get()); + } + case DynamicsProcessing::preEq: { + return isChannelConfigValid(dp.get(), + mEngineConfigApplied.preEqStage.inUse); + } + case DynamicsProcessing::postEq: { + return isChannelConfigValid(dp.get(), + mEngineConfigApplied.postEqStage.inUse); + } + case DynamicsProcessing::mbc: { + return isChannelConfigValid(dp.get(), + mEngineConfigApplied.mbcStage.inUse); + } + case DynamicsProcessing::preEqBand: { + return isPreEqBandConfigValid(dpCap, dp.get(), + mEngineConfigApplied.preEqStage.inUse, + mEngineConfigApplied.preEqStage.bandCount); + } + case DynamicsProcessing::postEqBand: { + return isPostEqBandConfigValid(dpCap, dp.get(), + mEngineConfigApplied.postEqStage.inUse, + mEngineConfigApplied.postEqStage.bandCount); + } + case DynamicsProcessing::mbcBand: { + return isMbcBandConfigValid(dpCap, dp.get(), + mEngineConfigApplied.mbcStage.inUse, + mEngineConfigApplied.mbcStage.bandCount); + } + case DynamicsProcessing::limiter: { + return isLimiterConfigValid(dp.get(), + mEngineConfigApplied.limiterInUse); + } + case DynamicsProcessing::inputGain: { + return isInputGainValid(dp.get()); + } + case DynamicsProcessing::vendorExtension: { + return true; + } + } + return true; +} + +bool DynamicsProcessingTestHelper::isParamEqual(const DynamicsProcessing::Tag& tag, + const DynamicsProcessing& dpRef, + const DynamicsProcessing& dpTest) { + switch (tag) { + case DynamicsProcessing::engineArchitecture: { + return isEngineConfigEqual(dpRef.get(), + dpTest.get()); + } + case DynamicsProcessing::preEq: { + const auto& source = dpRef.get(); + const auto& target = dpTest.get(); + return isAidlVectorEqualAfterFilter(source, target); + } + case DynamicsProcessing::postEq: { + return isAidlVectorEqualAfterFilter( + dpRef.get(), + dpTest.get()); + } + case DynamicsProcessing::mbc: { + return isAidlVectorEqualAfterFilter( + dpRef.get(), dpTest.get()); + } + case DynamicsProcessing::preEqBand: { + return isAidlVectorEqualAfterFilter( + dpRef.get(), + dpTest.get()); + } + case DynamicsProcessing::postEqBand: { + return isAidlVectorEqualAfterFilter( + dpRef.get(), + dpTest.get()); + } + case DynamicsProcessing::mbcBand: { + return isAidlVectorEqualAfterFilter( + dpRef.get(), + dpTest.get()); + } + case DynamicsProcessing::limiter: { + return isAidlVectorEqualAfterFilter( + dpRef.get(), + dpTest.get()); + } + case DynamicsProcessing::inputGain: { + return isAidlVectorEqual( + dpRef.get(), + dpTest.get()); + } + case DynamicsProcessing::vendorExtension: { + return false; + } + } +} + +bool DynamicsProcessingTestHelper::isEnablementValid( + const DynamicsProcessing::StageEnablement& enablement) { + return !enablement.inUse || (enablement.inUse && enablement.bandCount > 0); +} + +bool DynamicsProcessingTestHelper::isEngineConfigValid( + const DynamicsProcessing::EngineArchitecture& cfg) { + return cfg.preferredProcessingDurationMs >= 0 && isEnablementValid(cfg.preEqStage) && + isEnablementValid(cfg.postEqStage) && isEnablementValid(cfg.mbcStage); +} + +bool DynamicsProcessingTestHelper::isChannelConfigValid( + const std::vector& cfgs, bool stageInUse) { + std::unordered_set channelSet; + if (!stageInUse) return false; + for (auto cfg : cfgs) { + if (cfg.channel < 0 || cfg.channel >= mChannelCount || 0 != channelSet.count(cfg.channel)) { + return false; + } + channelSet.insert(cfg.channel); + } + return true; +} + +bool DynamicsProcessingTestHelper::isCutoffFrequencyValid( + float freq, const DynamicsProcessing::Capability& cap) { + return freq >= cap.minCutOffFreq && freq <= cap.maxCutOffFreq; +} + +bool DynamicsProcessingTestHelper::isPreEqBandConfigValid( + const DynamicsProcessing::Capability& cap, + const std::vector& cfgs, bool stageInUse, int bandCount) { + std::set> bandSet; + if (!stageInUse) return false; + for (auto cfg : cfgs) { + if (0 == mPreEqChannelEnable.count(cfg.channel) || cfg.channel < 0 || + cfg.channel >= mChannelCount || cfg.band < 0 || cfg.band >= bandCount || + !isCutoffFrequencyValid(cfg.cutoffFrequencyHz, cap) || + 0 != bandSet.count({cfg.channel, cfg.band})) { + return false; + } + bandSet.insert({cfg.channel, cfg.band}); + } + return true; +} + +bool DynamicsProcessingTestHelper::isPostEqBandConfigValid( + const DynamicsProcessing::Capability& cap, + const std::vector& cfgs, bool stageInUse, int bandCount) { + std::set> bandSet; + // not able to set/get parameter when stage not in use. + if (!stageInUse) return false; + for (auto cfg : cfgs) { + if (0 == mPostEqChannelEnable.count(cfg.channel) || cfg.channel < 0 || + cfg.channel >= mChannelCount || cfg.band < 0 || cfg.band >= bandCount || + !isCutoffFrequencyValid(cfg.cutoffFrequencyHz, cap) || + 0 != bandSet.count({cfg.channel, cfg.band})) { + return false; + } + bandSet.insert({cfg.channel, cfg.band}); + } + return true; +} + +bool DynamicsProcessingTestHelper::isMbcBandConfigValid( + const DynamicsProcessing::Capability& cap, + const std::vector& cfgs, bool stageInUse, + int bandCount) { + std::set> bandSet; + if (!stageInUse) return false; + for (auto cfg : cfgs) { + if (0 == mMbcChannelEnable.count(cfg.channel) || cfg.channel < 0 || + cfg.channel >= mChannelCount || cfg.band < 0 || cfg.band >= bandCount || + (cfg.attackTimeMs < 0) || cfg.releaseTimeMs < 0 || cfg.ratio < 0 || + cfg.thresholdDb > 0 || cfg.kneeWidthDb < 0 || cfg.noiseGateThresholdDb > 0 || + cfg.expanderRatio < 0 || !isCutoffFrequencyValid(cfg.cutoffFrequencyHz, cap) || + 0 != bandSet.count({cfg.channel, cfg.band})) { + return false; + } + bandSet.insert({cfg.channel, cfg.band}); + } + return true; +} + +bool DynamicsProcessingTestHelper::isLimiterConfigValid( + const std::vector& cfgs, bool stageInUse) { + std::set channelSet; + if (!stageInUse) return false; + for (auto cfg : cfgs) { + if (0 == mLimiterChannelEnable.count(cfg.channel) || cfg.channel < 0 || + cfg.channel >= mChannelCount || cfg.attackTimeMs < 0 || cfg.releaseTimeMs < 0 || + cfg.ratio < 0 || cfg.thresholdDb > 0 || 0 != channelSet.count(cfg.channel)) { + return false; + } + channelSet.insert(cfg.channel); + } + return true; +} + +bool DynamicsProcessingTestHelper::isInputGainValid( + const std::vector& cfgs) { + std::set channelSet; + for (auto cfg : cfgs) { + if (cfg.channel < 0 || cfg.channel >= mChannelCount || 0 != channelSet.count(cfg.channel)) { + return false; + } + channelSet.insert(cfg.channel); + } + return true; +} + +bool DynamicsProcessingTestHelper::isEngineConfigEqual( + const DynamicsProcessing::EngineArchitecture& ref, + const DynamicsProcessing::EngineArchitecture& test) { + return ref == test; +} + +template +std::vector DynamicsProcessingTestHelper::filterEnabledVector(const std::vector& vec) { + std::vector ret; + std::copy_if(vec.begin(), vec.end(), std::back_inserter(ret), + [](const auto& v) { return v.enable; }); + return ret; +} + +template +bool DynamicsProcessingTestHelper::isAidlVectorEqual(const std::vector& source, + const std::vector& target) { + if (source.size() != target.size()) return false; + + auto tempS = source; + auto tempT = target; + std::sort(tempS.begin(), tempS.end()); + std::sort(tempT.begin(), tempT.end()); + return tempS == tempT; +} + +template +bool DynamicsProcessingTestHelper::isAidlVectorEqualAfterFilter(const std::vector& source, + const std::vector& target) { + return isAidlVectorEqual(filterEnabledVector(source), filterEnabledVector(target)); +} + +void DynamicsProcessingTestHelper::SetAndGetDynamicsProcessingParameters() { + for (auto& it : mTags) { + auto& tag = it.first; + auto& dp = it.second; + + // validate parameter + Descriptor desc; + ASSERT_STATUS(EX_NONE, mEffect->getDescriptor(&desc)); + const bool valid = isParamValid(tag, dp, desc); + const binder_exception_t expected = valid ? EX_NONE : EX_ILLEGAL_ARGUMENT; + + // set parameter + Parameter expectParam; + Parameter::Specific specific; + specific.set(dp); + expectParam.set(specific); + ASSERT_STATUS(expected, mEffect->setParameter(expectParam)) << expectParam.toString(); + + // only get if parameter in range and set success + if (expected == EX_NONE) { + Parameter getParam; + Parameter::Id id; + DynamicsProcessing::Id dpId; + dpId.set(tag); + id.set(dpId); + // if set success, then get should match + EXPECT_STATUS(expected, mEffect->getParameter(id, &getParam)); + Parameter::Specific specificTest = getParam.get(); + const auto& target = specificTest.get(); + EXPECT_TRUE(isParamEqual(tag, dp, target)) << dp.toString() << "\n" + << target.toString(); + // update mEngineConfigApplied after setting successfully + if (tag == DynamicsProcessing::engineArchitecture) { + mEngineConfigApplied = target.get(); + } + } + } +} + +void DynamicsProcessingTestHelper::addEngineConfig( + const DynamicsProcessing::EngineArchitecture& cfg) { + DynamicsProcessing dp; + dp.set(cfg); + mTags.push_back({DynamicsProcessing::engineArchitecture, dp}); +} + +void DynamicsProcessingTestHelper::addPreEqChannelConfig( + const std::vector& cfgs) { + DynamicsProcessing dp; + dp.set(cfgs); + mTags.push_back({DynamicsProcessing::preEq, dp}); + for (auto& cfg : cfgs) { + if (cfg.enable) mPreEqChannelEnable.insert(cfg.channel); + } +} + +void DynamicsProcessingTestHelper::addPostEqChannelConfig( + const std::vector& cfgs) { + DynamicsProcessing dp; + dp.set(cfgs); + mTags.push_back({DynamicsProcessing::postEq, dp}); + for (auto& cfg : cfgs) { + if (cfg.enable) mPostEqChannelEnable.insert(cfg.channel); + } +} + +void DynamicsProcessingTestHelper::addMbcChannelConfig( + const std::vector& cfgs) { + DynamicsProcessing dp; + dp.set(cfgs); + mTags.push_back({DynamicsProcessing::mbc, dp}); + for (auto& cfg : cfgs) { + if (cfg.enable) mMbcChannelEnable.insert(cfg.channel); + } +} + +void DynamicsProcessingTestHelper::addPreEqBandConfigs( + const std::vector& cfgs) { + DynamicsProcessing dp; + dp.set(cfgs); + mTags.push_back({DynamicsProcessing::preEqBand, dp}); +} + +void DynamicsProcessingTestHelper::addPostEqBandConfigs( + const std::vector& cfgs) { + DynamicsProcessing dp; + dp.set(cfgs); + mTags.push_back({DynamicsProcessing::postEqBand, dp}); +} + +void DynamicsProcessingTestHelper::addMbcBandConfigs( + const std::vector& cfgs) { + DynamicsProcessing dp; + dp.set(cfgs); + mTags.push_back({DynamicsProcessing::mbcBand, dp}); +} + +void DynamicsProcessingTestHelper::addLimiterConfig( + const std::vector& cfgs) { + DynamicsProcessing dp; + dp.set(cfgs); + mTags.push_back({DynamicsProcessing::limiter, dp}); + for (auto& cfg : cfgs) { + if (cfg.enable) mLimiterChannelEnable.insert(cfg.channel); + } +} + +void DynamicsProcessingTestHelper::addInputGain( + const std::vector& inputGains) { + DynamicsProcessing dp; + dp.set(inputGains); + mTags.push_back({DynamicsProcessing::inputGain, dp}); +} + +/** + * Test DynamicsProcessing Engine Configuration + */ +enum EngineArchitectureTestParamName { + ENGINE_TEST_INSTANCE_NAME, + ENGINE_TEST_RESOLUTION_PREFERENCE, + ENGINE_TEST_PREFERRED_DURATION, + ENGINE_TEST_STAGE_ENABLEMENT, + ENGINE_TEST_LIMITER_IN_USE +}; +using EngineArchitectureTestParams = std::tuple, Descriptor>, + DynamicsProcessing::ResolutionPreference, float, + DynamicsProcessing::StageEnablement, bool>; + +void fillEngineArchConfig(DynamicsProcessing::EngineArchitecture& cfg, + const EngineArchitectureTestParams& params) { + cfg.resolutionPreference = std::get(params); + cfg.preferredProcessingDurationMs = std::get(params); + cfg.preEqStage = cfg.postEqStage = cfg.mbcStage = + std::get(params); + cfg.limiterInUse = std::get(params); +} + +class DynamicsProcessingTestEngineArchitecture + : public ::testing::TestWithParam, + public DynamicsProcessingTestHelper { + public: + DynamicsProcessingTestEngineArchitecture() + : DynamicsProcessingTestHelper(std::get(GetParam())) { + fillEngineArchConfig(mCfg, GetParam()); + }; + + void SetUp() override { SetUpDynamicsProcessingEffect(); } + + void TearDown() override { TearDownDynamicsProcessingEffect(); } + + DynamicsProcessing::EngineArchitecture mCfg; +}; + +TEST_P(DynamicsProcessingTestEngineArchitecture, SetAndGetEngineArch) { + EXPECT_NO_FATAL_FAILURE(addEngineConfig(mCfg)); + SetAndGetDynamicsProcessingParameters(); +} + +INSTANTIATE_TEST_SUITE_P( + DynamicsProcessingTest, DynamicsProcessingTestEngineArchitecture, + ::testing::Combine( + testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( + IFactory::descriptor, kDynamicsProcessingTypeUUID)), + testing::Values(DynamicsProcessing::ResolutionPreference::FAVOR_TIME_RESOLUTION, + DynamicsProcessing::ResolutionPreference:: + FAVOR_FREQUENCY_RESOLUTION), // variant + testing::Values(-10.f, 0.f, 10.f), // processing duration + testing::ValuesIn( + DynamicsProcessingTestHelper::kStageEnablementTestSet), // preEQ/postEQ/mbc + testing::Bool()), // limiter enable + [](const auto& info) { + auto descriptor = std::get(info.param).second; + DynamicsProcessing::EngineArchitecture cfg; + fillEngineArchConfig(cfg, info.param); + std::string name = "Implementor_" + descriptor.common.implementor + "_name_" + + descriptor.common.name + "_UUID_" + + descriptor.common.id.uuid.toString() + "_Cfg_" + cfg.toString(); + std::replace_if( + name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); + return name; + }); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestEngineArchitecture); + +/** + * Test DynamicsProcessing Input Gain + */ +enum InputGainTestParamName { + INPUT_GAIN_INSTANCE_NAME, + INPUT_GAIN_PARAM, +}; +class DynamicsProcessingTestInputGain + : public ::testing::TestWithParam, Descriptor>, + std::vector>>, + public DynamicsProcessingTestHelper { + public: + DynamicsProcessingTestInputGain() + : DynamicsProcessingTestHelper(std::get(GetParam())), + mInputGain(std::get(GetParam())){}; + + void SetUp() override { SetUpDynamicsProcessingEffect(); } + + void TearDown() override { TearDownDynamicsProcessingEffect(); } + + const std::vector mInputGain; +}; + +TEST_P(DynamicsProcessingTestInputGain, SetAndGetInputGain) { + EXPECT_NO_FATAL_FAILURE(addInputGain(mInputGain)); + SetAndGetDynamicsProcessingParameters(); +} + +INSTANTIATE_TEST_SUITE_P( + DynamicsProcessingTest, DynamicsProcessingTestInputGain, + ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( + IFactory::descriptor, kDynamicsProcessingTypeUUID)), + testing::ValuesIn(DynamicsProcessingTestInputGain::kInputGainTestSet)), + [](const auto& info) { + auto descriptor = std::get(info.param).second; + std::string gains = + ::android::internal::ToString(std::get(info.param)); + std::string name = "Implementor_" + descriptor.common.implementor + "_name_" + + descriptor.common.name + "_UUID_" + + descriptor.common.id.uuid.toString() + "_inputGains_" + gains; + std::replace_if( + name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); + return name; + }); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestInputGain); + +/** + * Test DynamicsProcessing Limiter Config + */ +enum LimiterConfigTestParamName { + LIMITER_INSTANCE_NAME, + LIMITER_CHANNEL, + LIMITER_ENABLE, + LIMITER_LINK_GROUP, + LIMITER_ENGINE_IN_USE, + LIMITER_ADDITIONAL, +}; +enum LimiterConfigTestAdditionalParam { + LIMITER_ATTACK_TIME, + LIMITER_RELEASE_TIME, + LIMITER_RATIO, + LIMITER_THRESHOLD, + LIMITER_POST_GAIN, + LIMITER_MAX_NUM, +}; +using LimiterConfigTestAdditional = std::array; +// attachTime, releaseTime, ratio, thresh, postGain +static constexpr std::array kLimiterConfigTestAdditionalParam = { + {{-1, -60, -2.5, -2, -3.14}, + {-1, 60, -2.5, 2, -3.14}, + {1, -60, 2.5, -2, 3.14}, + {1, 60, 2.5, 2, 3.14}}}; + +using LimiterConfigTestParams = + std::tuple, Descriptor>, int32_t, bool, int32_t, bool, + LimiterConfigTestAdditional>; + +void fillLimiterConfig(DynamicsProcessing::LimiterConfig& cfg, + const LimiterConfigTestParams& params) { + const std::array additional = std::get(params); + cfg.channel = std::get(params); + cfg.enable = std::get(params); + cfg.linkGroup = std::get(params); + cfg.attackTimeMs = additional[LIMITER_ATTACK_TIME]; + cfg.releaseTimeMs = additional[LIMITER_RELEASE_TIME]; + cfg.ratio = additional[LIMITER_RATIO]; + cfg.thresholdDb = additional[LIMITER_THRESHOLD]; + cfg.postGainDb = additional[LIMITER_POST_GAIN]; +} + +class DynamicsProcessingTestLimiterConfig + : public ::testing::TestWithParam, + public DynamicsProcessingTestHelper { + public: + DynamicsProcessingTestLimiterConfig() + : DynamicsProcessingTestHelper(std::get(GetParam())), + mLimiterInUseEngine(std::get(GetParam())) { + fillLimiterConfig(mCfg, GetParam()); + } + + void SetUp() override { SetUpDynamicsProcessingEffect(); } + + void TearDown() override { TearDownDynamicsProcessingEffect(); } + + DynamicsProcessing::LimiterConfig mCfg; + bool mLimiterInUseEngine; +}; + +TEST_P(DynamicsProcessingTestLimiterConfig, SetAndGetLimiterConfig) { + mEngineConfigPreset.limiterInUse = mLimiterInUseEngine; + EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset)); + EXPECT_NO_FATAL_FAILURE(addLimiterConfig({mCfg})); + SetAndGetDynamicsProcessingParameters(); +} + +INSTANTIATE_TEST_SUITE_P( + DynamicsProcessingTest, DynamicsProcessingTestLimiterConfig, + ::testing::Combine(testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( + IFactory::descriptor, kDynamicsProcessingTypeUUID)), + testing::Values(-1, 0, 1, 2), // channel count + testing::Bool(), // enable + testing::Values(3), // link group + testing::Bool(), // engine limiter enable + testing::ValuesIn(kLimiterConfigTestAdditionalParam)), // Additional + [](const auto& info) { + auto descriptor = std::get(info.param).second; + DynamicsProcessing::LimiterConfig cfg; + fillLimiterConfig(cfg, info.param); + std::string engineLimiterInUse = + std::to_string(std::get(info.param)); + std::string name = "Implementor_" + descriptor.common.implementor + "_name_" + + descriptor.common.name + "_UUID_" + + descriptor.common.id.uuid.toString() + "_limiterConfig_" + + cfg.toString() + "_engineSetting_" + engineLimiterInUse; + std::replace_if( + name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); + return name; + }); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestLimiterConfig); + +/** + * Test DynamicsProcessing ChannelConfig + */ +enum ChannelConfigTestParamName { + BAND_CHANNEL_TEST_INSTANCE_NAME, + BAND_CHANNEL_TEST_CHANNEL_CONFIG, + BAND_CHANNEL_TEST_ENGINE_IN_USE +}; +using ChannelConfigTestParams = std::tuple, Descriptor>, + std::vector, bool>; + +class DynamicsProcessingTestChannelConfig + : public ::testing::TestWithParam, + public DynamicsProcessingTestHelper { + public: + DynamicsProcessingTestChannelConfig() + : DynamicsProcessingTestHelper(std::get(GetParam())), + mCfg(std::get(GetParam())), + mInUseEngine(std::get(GetParam())) {} + + void SetUp() override { SetUpDynamicsProcessingEffect(); } + + void TearDown() override { TearDownDynamicsProcessingEffect(); } + + std::vector mCfg; + const bool mInUseEngine; +}; + +TEST_P(DynamicsProcessingTestChannelConfig, SetAndGetPreEqChannelConfig) { + mEngineConfigPreset.preEqStage.inUse = mInUseEngine; + EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset)); + EXPECT_NO_FATAL_FAILURE(addPreEqChannelConfig(mCfg)); + SetAndGetDynamicsProcessingParameters(); +} + +TEST_P(DynamicsProcessingTestChannelConfig, SetAndGetPostEqChannelConfig) { + mEngineConfigPreset.postEqStage.inUse = mInUseEngine; + EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset)); + EXPECT_NO_FATAL_FAILURE(addPostEqChannelConfig(mCfg)); + SetAndGetDynamicsProcessingParameters(); +} + +TEST_P(DynamicsProcessingTestChannelConfig, SetAndGetMbcChannelConfig) { + mEngineConfigPreset.mbcStage.inUse = mInUseEngine; + EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset)); + EXPECT_NO_FATAL_FAILURE(addMbcChannelConfig(mCfg)); + SetAndGetDynamicsProcessingParameters(); +} + +INSTANTIATE_TEST_SUITE_P( + DynamicsProcessingTest, DynamicsProcessingTestChannelConfig, + ::testing::Combine( + testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( + IFactory::descriptor, kDynamicsProcessingTypeUUID)), + testing::ValuesIn( + DynamicsProcessingTestHelper::kChannelConfigTestSet), // channel config + testing::Bool()), // Engine inUse + [](const auto& info) { + auto descriptor = std::get(info.param).second; + std::string engineInUse = + std::to_string(std::get(info.param)); + std::string channelConfig = ::android::internal::ToString( + std::get(info.param)); + + std::string name = "Implementor_" + descriptor.common.implementor + "_name_" + + descriptor.common.name + "_UUID_" + + descriptor.common.id.uuid.toString() + "_" + channelConfig + + "_engineInUse_" + engineInUse; + std::replace_if( + name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); + return name; + }); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestChannelConfig); + +/** + * Test DynamicsProcessing EqBandConfig + */ +enum EqBandConfigTestParamName { + EQ_BAND_INSTANCE_NAME, + EQ_BAND_CHANNEL, + EQ_BAND_CHANNEL_ENABLE, + EQ_BAND_ENABLE, + EQ_BAND_CUT_OFF_FREQ, + EQ_BAND_GAIN, + EQ_BAND_STAGE_IN_USE +}; +using EqBandConfigTestParams = std::tuple, Descriptor>, int32_t, + std::vector, bool, + std::vector>, float, bool>; + +void fillEqBandConfig(std::vector& cfgs, + const EqBandConfigTestParams& params) { + const std::vector> cutOffFreqs = std::get(params); + int bandCount = cutOffFreqs.size(); + cfgs.resize(bandCount); + for (int i = 0; i < bandCount; i++) { + cfgs[i].channel = std::get(params); + cfgs[i].band = cutOffFreqs[i].first; + cfgs[i].enable = std::get(params); + cfgs[i].cutoffFrequencyHz = cutOffFreqs[i].second; + cfgs[i].gainDb = std::get(params); + } +} + +class DynamicsProcessingTestEqBandConfig : public ::testing::TestWithParam, + public DynamicsProcessingTestHelper { + public: + DynamicsProcessingTestEqBandConfig() + : DynamicsProcessingTestHelper(std::get(GetParam())), + mStageInUse(std::get(GetParam())), + mChannelConfig(std::get(GetParam())) { + fillEqBandConfig(mCfgs, GetParam()); + } + + void SetUp() override { SetUpDynamicsProcessingEffect(); } + + void TearDown() override { TearDownDynamicsProcessingEffect(); } + + std::vector mCfgs; + const bool mStageInUse; + const std::vector mChannelConfig; +}; + +TEST_P(DynamicsProcessingTestEqBandConfig, SetAndGetPreEqBandConfig) { + mEngineConfigPreset.preEqStage.inUse = mStageInUse; + mEngineConfigPreset.preEqStage.bandCount = mCfgs.size(); + EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset)); + EXPECT_NO_FATAL_FAILURE(addPreEqChannelConfig(mChannelConfig)); + EXPECT_NO_FATAL_FAILURE(addPreEqBandConfigs(mCfgs)); + SetAndGetDynamicsProcessingParameters(); +} + +TEST_P(DynamicsProcessingTestEqBandConfig, SetAndGetPostEqBandConfig) { + mEngineConfigPreset.postEqStage.inUse = mStageInUse; + mEngineConfigPreset.postEqStage.bandCount = mCfgs.size(); + EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset)); + EXPECT_NO_FATAL_FAILURE(addPostEqChannelConfig(mChannelConfig)); + EXPECT_NO_FATAL_FAILURE(addPostEqBandConfigs(mCfgs)); + SetAndGetDynamicsProcessingParameters(); +} + +std::vector>> kBands{ + { + {0, 600}, + {1, 2000}, + {2, 6000}, + {3, 10000}, + {4, 16000}, + }, // 5 bands + { + {0, 800}, + {3, 15000}, + {2, 6000}, + {1, 2000}, + }, // 4 bands, unsorted + { + {0, 650}, + {1, 2000}, + {2, 6000}, + {3, 10000}, + {3, 16000}, + }, // 5 bands, missing band + { + {0, 900}, + {1, 8000}, + {2, 4000}, + {3, 12000}, + }, // 4 bands, cutoff freq not increasing + { + {0, 450}, + {1, 2000}, + {7, 6000}, + {3, 10000}, + {4, 16000}, + }, // bad band index + { + {0, 1}, + {1, 8000}, + }, // too low cutoff freq + { + {0, 1200}, + {1, 80000}, + }, // too high cutoff freq +}; + +INSTANTIATE_TEST_SUITE_P( + DynamicsProcessingTest, DynamicsProcessingTestEqBandConfig, + ::testing::Combine( + testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( + IFactory::descriptor, kDynamicsProcessingTypeUUID)), + testing::Values(-1, 0, 10), // channel ID + testing::ValuesIn( + DynamicsProcessingTestHelper::kChannelConfigTestSet), // channel enable + testing::Bool(), // band enable + testing::ValuesIn(kBands), // cut off frequencies + testing::Values(-3.14f, 3.14f), // gain + testing::Bool()), // stage in use + [](const auto& info) { + auto descriptor = std::get(info.param).second; + std::vector cfgs; + fillEqBandConfig(cfgs, info.param); + std::string enable = + ::android::internal::ToString(std::get(info.param)); + std::string bands = ::android::internal::ToString(cfgs); + std::string stageInUse = std::to_string(std::get(info.param)); + std::string name = "Implementor_" + descriptor.common.implementor + "_name_" + + descriptor.common.name + "_UUID_" + + descriptor.common.id.uuid.toString() + "_" + enable + "_bands_" + + bands + "_stageInUse_" + stageInUse; + std::replace_if( + name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); + return name; + }); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestEqBandConfig); + +/** + * Test DynamicsProcessing MbcBandConfig + */ + +enum MbcBandConfigParamName { + MBC_BAND_INSTANCE_NAME, + MBC_BAND_CHANNEL, + MBC_BAND_CHANNEL_CONFIG, + MBC_BAND_ENABLE, + MBC_BAND_CUTOFF_FREQ, + MBC_BAND_STAGE_IN_USE, + MBC_BAND_ADDITIONAL +}; +enum MbcBandConfigAdditional { + MBC_ADD_ATTACK_TIME, + MBC_ADD_RELEASE_TIME, + MBC_ADD_RATIO, + MBC_ADD_THRESHOLD, + MBC_ADD_KNEE_WIDTH, + MBC_ADD_NOISE_GATE_THRESHOLD, + MBC_ADD_EXPENDER_RATIO, + MBC_ADD_PRE_GAIN, + MBC_ADD_POST_GAIN, + MBC_ADD_MAX_NUM +}; +using TestParamsMbcBandConfigAdditional = std::array; + +// attachTime, releaseTime, ratio, thresh, kneeWidth, noise, expander, preGain, postGain +static constexpr std::array kMbcBandConfigAdditionalParam = { + {{-3, -10, -2, -2, -5, -90, -2.5, -2, -2}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {-3, 10, -2, 2, -5, 90, -2.5, 2, -2}, + {3, 10, 2, 2, 5, 90, 2.5, 2, 2}}}; + +using TestParamsMbcBandConfig = + std::tuple, Descriptor>, int32_t, + std::vector, bool, + std::vector>, bool, TestParamsMbcBandConfigAdditional>; + +void fillMbcBandConfig(std::vector& cfgs, + const TestParamsMbcBandConfig& params) { + const std::vector> cutOffFreqs = std::get(params); + const std::array additional = std::get(params); + int bandCount = cutOffFreqs.size(); + cfgs.resize(bandCount); + for (int i = 0; i < bandCount; i++) { + cfgs[i] = DynamicsProcessing::MbcBandConfig{ + .channel = std::get(params), + .band = cutOffFreqs[i].first, + .enable = std::get(params), + .cutoffFrequencyHz = cutOffFreqs[i].second, + .attackTimeMs = additional[MBC_ADD_ATTACK_TIME], + .releaseTimeMs = additional[MBC_ADD_RELEASE_TIME], + .ratio = additional[MBC_ADD_RATIO], + .thresholdDb = additional[MBC_ADD_THRESHOLD], + .kneeWidthDb = additional[MBC_ADD_KNEE_WIDTH], + .noiseGateThresholdDb = additional[MBC_ADD_NOISE_GATE_THRESHOLD], + .expanderRatio = additional[MBC_ADD_EXPENDER_RATIO], + .preGainDb = additional[MBC_ADD_PRE_GAIN], + .postGainDb = additional[MBC_ADD_POST_GAIN]}; + } +} + +class DynamicsProcessingTestMbcBandConfig + : public ::testing::TestWithParam, + public DynamicsProcessingTestHelper { + public: + DynamicsProcessingTestMbcBandConfig() + : DynamicsProcessingTestHelper(std::get(GetParam())), + mStageInUse(std::get(GetParam())), + mChannelConfig(std::get(GetParam())) { + fillMbcBandConfig(mCfgs, GetParam()); + } + + void SetUp() override { SetUpDynamicsProcessingEffect(); } + + void TearDown() override { TearDownDynamicsProcessingEffect(); } + + std::vector mCfgs; + const bool mStageInUse; + const std::vector mChannelConfig; +}; + +TEST_P(DynamicsProcessingTestMbcBandConfig, SetAndGetMbcBandConfig) { + mEngineConfigPreset.mbcStage.inUse = mStageInUse; + mEngineConfigPreset.mbcStage.bandCount = mCfgs.size(); + EXPECT_NO_FATAL_FAILURE(addEngineConfig(mEngineConfigPreset)); + EXPECT_NO_FATAL_FAILURE(addMbcChannelConfig(mChannelConfig)); + EXPECT_NO_FATAL_FAILURE(addMbcBandConfigs(mCfgs)); + SetAndGetDynamicsProcessingParameters(); +} + +INSTANTIATE_TEST_SUITE_P( + DynamicsProcessingTest, DynamicsProcessingTestMbcBandConfig, + ::testing::Combine( + testing::ValuesIn(EffectFactoryHelper::getAllEffectDescriptors( + IFactory::descriptor, kDynamicsProcessingTypeUUID)), + testing::Values(-1, 0, 10), // channel count + testing::ValuesIn( + DynamicsProcessingTestHelper::kChannelConfigTestSet), // channel config + testing::Bool(), // enable + testing::ValuesIn(kBands), // cut off frequencies + testing::Bool(), // stage in use + testing::ValuesIn(kMbcBandConfigAdditionalParam)), // Additional + [](const auto& info) { + auto descriptor = std::get(info.param).second; + std::vector cfgs; + fillMbcBandConfig(cfgs, info.param); + std::string enable = + ::android::internal::ToString(std::get(info.param)); + std::string mbcBands = ::android::internal::ToString(cfgs); + std::string stageInUse = std::to_string(std::get(info.param)); + std::string name = "Implementor_" + descriptor.common.implementor + "_name_" + + descriptor.common.name + "_UUID_" + + descriptor.common.id.uuid.toString() + "_enable_" + enable + + "_bands_" + mbcBands + "_stageInUse_" + stageInUse; + std::replace_if( + name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_'); + return name; + }); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DynamicsProcessingTestMbcBandConfig); + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + return RUN_ALL_TESTS(); +}